WHUCTF2020-wp

武汉大学第二届“珞格杯”校园CTF大赛部分题目Writeup

Web

ezphp

一个代码审计题。

1
2
3
4
5
6
7
//1st
if($_GET['num'] !== '23333' && preg_match('/^23333$/', $_GET['num'])){
echo '1st ok'."<br>";
}
else{
die('会代码审计嘛23333');
}

正则表达式。$ 匹配输入字符串的结尾位置。加一个 %0a 换行符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//2nd
if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);

if($md5_1 != $md5_2){
$a = strtr($md5_1, 'pggnb', '12345');
$b = strtr($md5_2, 'pggnb', '12345');
if($a == $b){
echo '2nd ok'."<br>";
}
else{
die("can u give me the right str???");
}
}
else{
die("no!!!!!!!!");
}
}
else{
die('is str1 numeric??????');
}

构造一下,php 会将 0e\d 开头的纯数字字符串识别为浮点数。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$count = 0;
for ($i = 0; $i <= 30000000; $i++) {
$md5 = strtr(md5($i), 'pggnb', '12345');
if (preg_match('/^0e\d+$/', $md5)) {
echo $i . " " . md5($i) . "<br>";
$count++;
}
if ($count == 2) {
break;
}
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//3nd
function filter($string){
return preg_replace('/x/', 'yy', $string);
}

$username = $_POST['username'];

$password = "aaaaa";
$user = array($username, $password);

$r = filter(serialize($user));
if(unserialize($r)[1] == "123456"){
echo file_get_contents('flag.php');
}

反序列化。将

1
;i:1;s:6:"123456";}

放在 username 的最后,并在前面写与这串字符串一样长的 x,一个 x 变成两个 y 后我们构造的字符串就会被执行。

payload:

flag.png

ezinclude

好像禁止了 get 请求?有一个发送数据后的 thankyou.php,发现有 get 参数,flag 可以在这个页面读出。

ezinclude.png

ezcmd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
if(isset($_GET['ip'])){
$ip = $_GET['ip'];
if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("no space!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("no flag");
} else if(preg_match("/tac|rm|echo|cat|nl|less|more|tail|head/", $ip)){
die("cat't read flag");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "<pre>";
print_r($a);
}
highlight_file(__FILE__);
?>

构造命令行。用变量绕过 flag 的正则匹配,用 \$IFS 做空格,用grep查看文件

QQ截图20200523182559.png

Misc

check-in

签到题。找到 .git 中的 remote GitHub 地址,明文 flag 就在仓库里。

shellOfAwd

在第 0 个TCP流中发现服务器接受 POST 的 ”ANT“ 字段,可以从这里入手,第 3 个流中客户端发送了一段 php 代码:

1.png

由服务器在第 5 个流返回的 AES128 密钥即可解密之后通讯的所有数据,最终在第 7 个流处找到 flag。

QQ截图20200526154754.png

Wechat_game

注意到 txt 文件夹里的文本都是反的,用 grep 扫一遍反的 flag 头得到答案。

image.png

佛系青年

开头是佛曰,在 这里 解密,之后看到题目里有栅栏,再用栅栏密码解密一次就可得到 flag 的 ASCII。

版权保护

观察题目结构,除正常汉字外还有大量 E2808D 和 E2E0EC。提取出 8D 和 8C 变成 0 和 1,再翻译成 ASCII,即可得到 flag。

image.png

Crypto

bivibivi

先枚举方程的解,再用官方接口转换,民间也有很多算法源码。

bvtoav avtobv

RE

RE1

输入 7 个数,要使这 7 个数和已知的 2 个数组成的 3*3 矩阵每行和和每列和等于 15,枚举求出(其实手算也很容易)。

1
2
3
4
5
6
7
8
9
10
for (int a = 0; a <= 9; a++)
for (int b = 0; b <= 9; b++)
for (int c = 0; c <= 9; c++)
for (int d = 0; d <= 9; d++)
for (int f = 0; f <= 9; f++)
{
int e = 5, g = 4, h = 9, i = 2, j = 15;
if (a + b + c != j || d + e + f != j || a + d + g != j || b + e + h != j || c + f + i != j) continue;
printf("%d %d %d %d %d %d\n",a,b,c,d,e,f);
}

RE3

看到 .data 节有一串神秘字符和一串 58 个字符的密码表,直接与 base58 密码表对比翻译密文,再base58解密一次就能得到 flag。

image.png

RE4

三道题,都是 base64 编码的简单程序,分别要解一元、二元、三元方程。我的方式是先确定数在哪里,找到位置后再用sympy解方程。

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
'''
@Date: 2020-05-25 01:08:32
@LastEditors: QiuJhao
@LastEditTime: 2020-05-26 23:56:52
'''

import base64
from pwn import *
import sympy as sp
import ctypes
def deci(code):
content1 = base64.b64decode((code))
with open('tmp.txt', 'wb+') as f_now:
f_now.write(content1)
return content1

def tq(str):
a = str[1880] + str[1881] * 256 + str[1882] * 256 * 256 + str[1883] * 256 * 256 * 256
print (a)
cc = 1
b = 0
for i in range(1886,1894):
b = b + cc * str[i]
cc = cc * 256
print (b)
return repr(b/a)

def todec(str):
l = list(str)
cc = 1
b = 0
flag = 1
if str[-1] >= 128 :
flag = -1
q = 0
for i in l:
l[q] = l[q] ^ 255
q = q + 1
l[0] = l[0] + 1
for i in l:
b = b + cc * i
cc = cc * 256
return b * flag

def hextodouble(s):
cp = ctypes.pointer(ctypes.c_longlong(s))
fp = ctypes.cast(cp, ctypes.POINTER(ctypes.c_double))
return (fp.contents.value)

if __name__ == '__main__':
sh = remote('218.197.154.9', 10055)
for i in range(1,11):
sh.recvuntil('\n\n')
code = sh.recvuntil('\n\n')
print(code[:-2])
if i == 1:
answer = tq(deci(code[:-2]))
sh.sendline(answer)
if i == 2:
str = deci(code[:-2])
x = sp.Symbol('x')
y = sp.Symbol('y')
answer1 = sp.solve([todec(str[0x0778:0x077C]) * x + todec(str[0x0783:0x0787]) * y - todec(str[0x078C:0x0794]), todec(str[0x07A0:0x07A4]) * x + todec(str[0x07AB:0x07AF]) * y - todec(str[0x07B4:0x07BC])], [x, y])[x]
answer2 = sp.solve([todec(str[0x0778:0x077C]) * x + todec(str[0x0783:0x0787]) * y - todec(str[0x078C:0x0794]), todec(str[0x07A0:0x07A4]) * x + todec(str[0x07AB:0x07AF]) * y - todec(str[0x07B4:0x07BC])], [x, y])[y]
sh.send(repr(answer1)+' '+repr(answer2))
if i == 3:
str = deci(code[:-2])
x, y, z = sp.symbols('x y z', positive=True)
a1 = hextodouble(todec(str[0xAB0:0xAB8]))
b1 = hextodouble(todec(str[0xAB8:0xAC0]))
c1 = hextodouble(todec(str[0xAC0:0xAC8]))
e1 = hextodouble(todec(str[0xAC8:0xAD0]))
a2 = hextodouble(todec(str[0xAD0:0xAD8]))
b2 = hextodouble(todec(str[0xAD8:0xAE0]))
c2 = hextodouble(todec(str[0xAE0:0xAE8]))
e2 = hextodouble(todec(str[0xAE8:0xAF0]))
a3 = hextodouble(todec(str[0xAF0:0xAF8]))
b3 = hextodouble(todec(str[0xAF8:0xB00]))
c3 = hextodouble(todec(str[0xB00:0xB08]))
e3 = hextodouble(todec(str[0xB08:0xB10]))
answer1 = (sp.solve([x*a1+y*b1+z*c1-e1, x*a2+y*b2+z*c2-e2, x*a3+y*b3+z*c3-e3], [x, y, z])[x])
answer2 = (sp.solve([x*a1+y*b1+z*c1-e1, x*a2+y*b2+z*c2-e2, x*a3+y*b3+z*c3-e3], [x, y, z])[y])
answer3 = (sp.solve([x*a1+y*b1+z*c1-e1, x*a2+y*b2+z*c2-e2, x*a3+y*b3+z*c3-e3], [x, y, z])[z])
print(repr(answer1), repr(answer2), repr(answer3))
sh.send(repr(round(answer1))+' '+repr(round(answer2))+' '+repr(round(answer3)))
sh.interactive()
print(sh.recvuntil('Right!\n\n'))

PS:IdontknowAngrorUnicorn

Decrypt

固件解密思路是用旧版本未被加密的固件提出解密程序。先下载所有该型号固件,发现有一个标有 Middleware 的版本,经过 binwalk 多次提取后得到文件系统,在 /bin 下可发现二进制文件 imgdecrypt。使用 qemu 运行这个 MIPS 程序解密 bin 即可得到key。

1
sudo chroot . ./qemu-mips-static bin/imgdecrypt DIR878A1_FW1.12B01_Encrypt.bin

区块链

智能合约? 那是啥

在 etherscan 打开题目给的合约地址,查看第一条交易,flag 就在 Input Data 中。

image.png

正解应该用 remix.ethereum.org 把程序编译后再调用开头的合约地址运行。

image.png