D^3CTF Reverse/车联网 Writeup

考完研第一次完整的参加一次CTF,考也没考上,ida咋用也忘干净了,还让队友掉出了前十。这就滚去学习re和iov。

reverse

randomvm

linux elf,一个利用rand函数跑起来的vm。还有用到syscall、ptrace反调试。

程序把真正的程序逻辑分到了一个个函数里,然后通过rand函数的返回值跳转到另一个函数。输入字符是使用syscall 0x00,中间还有syscall 0x65来调用ptrace(返回0xff..ffff),达到反调试。

vm函数运行结束后回到main,将加密后的输入与一段12bytes的数据进行比对。

部分解密:

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
flag = '1234567890ab'
inp = [ord(c) for c in flag]

for v in inp:
print(hex(v), end=', ')

print()

inp[0] = ((((inp[0] << 5) | (inp[0] >> 3)) & 0xff) ^ 0x03) ^ inp[1]
# inp[0] = ((((inp[0] << 7) | (inp[0] >> 1)) & 0xff) ^ 0x01)
# inp[0] = ((((inp[0] << 7) | (inp[0] >> 1)) & 0xff))
# inp[0] = ((((inp[0] << 2) | (inp[0] >> 6)) & 0xff)) ^ inp[1]
# inp[1] ^= 0xff
# inp[1] ^= inp[0]
# inp[1] = ((inp[1] << 9) | (inp[1] >> 0xff)) & 0xff
# inp[1] ^= 0xff

inp[1] = 0xff
tmp1 = inp[3]
inp[3] ^= (((inp[2] << 0x01) | (inp[2] >> 0x07)) & 0xff) ^ 0x07
tmp2 = inp[4]
inp[4] ^= (((tmp1 << 0x04) | (tmp1 >> 0x04)) & 0xff) ^ 0x04
tmp3 = inp[5]
inp[5] ^= (((tmp2 << 0x04) | (tmp2 >> 0x04)) & 0xff)
tmp4 = inp[6]
inp[6] ^= ((((tmp3 << 0x01) | (tmp3 >> 0x07)) & 0xff) ^ 0x07)
tmp5 = inp[7]
inp[7] ^= (((tmp4 << 0x01) | (tmp4 >> 0x07)) & 0xff)
tmp6 = inp[8]
inp[8] ^= (((tmp5 << 0x06) | (tmp5 >> 0x02)) & 0xff)
tmp7 = inp[9]
inp[9] ^= (((tmp6 << 0x04) | (tmp6 >> 0x04)) & 0xff)
tmp8 = inp[10]
inp[10] ^= (((tmp7 << 0x04) | (tmp7 >> 0x04)) & 0xff)
tmp9 = inp[11]
inp[11] = (((tmp8 << 0x01) | (tmp8 >> 0x07)) & 0xff) ^ 0x07

print('before xor--------')
for v in inp:
print(hex(v), end=', ')

print()

for i in range(1, len(inp)):
inp[i] ^= inp[i - 1]

print('enc----')
for v in inp:
print(hex(v), end=', ')

print()


secret = [0x9D, 0x6B, 0xA1, 0x02, 0xD7, 0xED, 0x40, 0xF6, 0x0E, 0xAE, 0x84, 0x19]
# secret = [0x6B, 0x2C, 0xF1, 0x41, 0xC4, 0xDC, 0x77, 0xFF, 0x80, 0xF7, 0xD6, 0xB5]
# secret = [0x17, 0xB5, 0x4D, 0x17, 0x76, 0x22, 0x73, 0x3A, 0x44, 0x26, 0x52, 0x91]
# secret = inp
flag = [0 for i in range(12)]

for i in range(len(secret) - 1, 0, -1):
secret[i] ^= secret[i - 1]

print('----')
for v in secret:
print(hex(v), end=', ')

print()


flag[10] = (((secret[11] ^ 0x07) >> 1) | ((secret[11] ^ 0x07) << 7)) & 0xff
flag[9] = ((((secret[10] ^ (flag[10])) >> 4) | ((secret[10] ^ (flag[10])) << 4)) & 0xff)
flag[8] = ((((secret[9] ^ (flag[9])) >> 4) | ((secret[9] ^ (flag[9])) << 4)) & 0xff)
flag[7] = ((((secret[8] ^ (flag[8])) >> 6) | ((secret[8] ^ (flag[8])) << 2)) & 0xff)
flag[6] = ((((secret[7] ^ (flag[7])) >> 1) | ((secret[7] ^ (flag[7])) << 7)) & 0xff)
flag[5] = ((((secret[6] ^ (flag[6]) ^ 0x07) >> 1) | ((secret[6] ^ (flag[6]) ^ 0x07) << 7)) & 0xff)
flag[4] = ((((secret[5] ^ (flag[5])) >> 4) | ((secret[5] ^ (flag[5])) << 4)) & 0xff)
flag[3] = ((((secret[4] ^ (flag[4]) ^ 0x04) >> 4) | ((secret[4] ^ (flag[4]) ^ 0x04) << 4)) & 0xff)
flag[2] = ((((secret[3] ^ (flag[3]) ^ 0x07) >> 1) | ((secret[3] ^ (flag[3]) ^ 0x07) << 7)) & 0xff)
# flag[1] = ((((secret[2] ^ (flag[2])) >> (6)) | ((secret[2] ^ (flag[2])) << 2)) & 0xff)

print('test1----')
for v in flag:
print(chr(v), end='')

print()

剩下的爆破:

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
from subprocess import Popen, PIPE, STDOUT
import string

flag = 'wJumpVmvM'

def run(s):
p = Popen('./RandomVM', stdout=PIPE, stdin=PIPE, stderr=PIPE, text=False)
stdout_data = p.communicate(input=s.encode('ascii'))[0]
p.kill()
return stdout_data


i = 0

for c1 in string.printable:
for c2 in string.ascii_letters:
for c3 in string.ascii_letters:
s = c1 + c2 + c3 + flag
res = run(s)
if b'Yes' in res:
print(s)
exit()
else:
print(f'no {i}', end=',')
i += 1

ezjunk

windows程序,junk code还算简单,一直f8就能找到真正的逻辑。

程序里用了tls callback,IsDebuggerPresent反调试,会修改TEA的key。另外TEA之后还有一个CRC。

爆破crc:

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
def enc2_inner(x):
if x >> 31 == 1:
x = (x << 1) & 0xffffffff
x ^= 0x84A6972F
else:
x = (x << 1) & 0xffffffff
return x

def enc2(x):
path = [x]
for i in range(32):
if x >> 31 == 1:
x = (x << 1) & 0xffffffff
x ^= 0x84A6972F
else:
x = (x << 1) & 0xffffffff
path += [x]

# for p in path:
# print(hex(p), end=', ')
# print()
return x

print(hex(enc2(0x0F1F145FF)))

def dec2_inner(x):
maybe = [(x ^ 0x84A6972F) >> 1, ((x ^ 0x84A6972F) >> 1) | 0x80000000, ((x ^ 0x84A6972F) >> 1) & 0x7fffffff, (x) >> 1, ((x) >> 1) | 0x80000000, ((x) >> 1) & 0x7fffffff, ]
res = []
for m in maybe:
if enc2_inner(m) == x:
res += [m]
return res[0]

def dec2(x):
for i in range(32):
x = dec2_inner(x)
return x

secret = [0x0B6DDB3A9, 0x36162C23, 0x1889FABF, 0x6CE4E73B, 0x0A5AF8FC, 0x21FF8415, 0x44859557, 0x2DC227B7, 0x3538045A,]
for ss in secret:
print(hex(dec2(ss)), end=', ')

print()

tea解密:

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
import ctypes

keys = [0x5454, 0x4646, 0x4444, 0x5E5E]
keys = [0x5410, 0x4646, 0x4444, 0x5E6d]
keys = [0x5454, 0x4602, 0x4477, 0x5E5E]

def enc(v, k):
v1, v2 = ctypes.c_uint32(v[0]), ctypes.c_uint32(v[1])
sum = ctypes.c_uint32(0x0E8017300)
delta = ctypes.c_uint32(0x0FF58F981)
for i in range(32):
v1.value += (v2.value + ((v2.value << 4) ^ (v2.value >> 5))) ^ (k[sum.value & 3] + sum.value) ^ 0x44
v2.value += (v1.value + ((v1.value << 5) ^ (v1.value >> 6))) ^ (k[(sum.value >> 11) & 3] + sum.value) ^ 0x33
sum.value -= delta.value
return v1.value, v2.value


def dec(v, k):
v1, v2 = ctypes.c_uint32(v[0]), ctypes.c_uint32(v[1])
sum = ctypes.c_uint32(0x0E8017300)
delta = ctypes.c_uint32(0x0FF58F981)
for i in range(32):
sum.value -= delta.value
for i in range(32):
sum.value += delta.value
v2.value -= (v1.value + ((v1.value << 5) ^ (v1.value >> 6))) ^ (k[(sum.value >> 11) & 3] + sum.value) ^ 0x33
v1.value -= (v2.value + ((v2.value << 4) ^ (v2.value >> 5))) ^ (k[sum.value & 3] + sum.value) ^ 0x44
return v1.value, v2.value

def num2str(n):
while n != 0:
tmp = n & 0xff
print(chr(tmp), end='')
n = n >> 8

secret = []
v1, v2 = enc([0x61616161, 0x62626262], keys)
secret += [v1, v2]
print(hex(v1), hex(v2))
v1, v2 = enc([0x63636363, 0x64646464], keys)
secret += [v1, v2]
print(hex(v1), hex(v2))
v1, v2 = enc([0x61616161, 0x62626262], keys)
secret += [v1, v2]
print(hex(v1), hex(v2))
v1, v2 = enc([0x63636363, 0x64646464], keys)
secret += [v1, v2]
print(hex(v1), hex(v2))
# print(secret)

secret = [0x5406CBB1, 0xA4A41EA2, 0x34489AC5, 0x53D68797, 0xB8E0C06F, 0x0259F2DB, 0x52E38D82, 0x595D5E1D,]
secret = [0xB6DDB3A9, 0x36162C23, 0x1889FABF, 0x6CE4E73B, 0x0A5AF8FC, 0x21FF8415, 0x44859557, 0x2DC227B7,]
secret = [0xdd243dab, 0x898587d8, 0x2fdf1d, 0xbc4aca2, 0x4d68a16a, 0x616c5d29, 0x5b911af5, 0x4c7710dc, 0x5901851,]
# for i, v in enumerate(secret):
# secret[i] ^= 0x84A6972F

# secret = [0x0F1F145FF, 0x0CCDD1F47, 0x63F89C2B, 0x31C705B0, 0x0F1F145FF, 0x0CCDD1F47, 0x63F89C2B, 0x31C705B0]
for i in range(0, 8, 2):
v1, v2 = dec([secret[i], secret[i+1]], keys)
print(hex(v1), hex(v2))
num2str(v1)
num2str(v2)
print()

车联网

flag1

启动容器后,使用adb connect ip:port来连接车机,使用 pm 来查找和导出apk,发现有一个D3Factory,直接拿出来用jadx逆向即可看到flag。

flag2 & flag3

当时,拿到 flag1 之后并没有仔细再看apk里面的线索,也没有发现tcpdump的提示,然后就没有思路了,然后用 scrypy 看屏幕,也没通过主屏幕联想到需要搞GPS….不过当时确实有想过分析CAN和自己写apk。还是要多参加比赛(