【wp】HWS计划2021硬件安全冬令营线上选拔赛

逆向手在夹缝中艰难求生系列。

这篇真的存粹是做题笔记了,对内核驱动啥的不太懂,pwn也不会,能做出来的题都是硬逆出来的(

childre最后死活没整出来,后来看大佬的wp才知道对子进程有修改(。)呜呜呜多进程真的不太会啊。

REVERSE

obfu

一堆密码堆起来的题(密码菜鸡卑微落泪

按逻辑来说拿到serial number就能拿到flag了。

通过findcrypt找到三个加密算法的常量,通过交叉引用确定函数用途。

v10和v8相比之下v8更容易出,v8跟输入没有关系,相当于已知字符串(=md5(“admin”)。

然后就是enctrpy函数。

按照顺序是AES_enc->RC4_dec->循环左移三位复原。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from binascii import *
from Crypto.Cipher import AES,ARC4
from hashlib import sha256,md5

v8=unhexlify(md5("admin").hexdigest())
admin=[ord(x) for x in unhexlify(sha256("admin").hexdigest())]
tmp=""+chr(admin[0])
for i in range(1,32):
tmp+=chr(admin[i]^admin[i-1])
key=tmp[:16]
iv=tmp[16:]
aes=AES.new(key,AES.MODE_CBC,iv)
cipher=aes.encrypt(v8)
rc4=ARC4.new(key)
input_enc=rc4.decrypt(cipher)
tmps=bin(int(hexlify(input_enc),16))[2:].rjust(16*8,'0')
tmps=tmps[3:]+tmps[:3]
serial=""
for i in range(16):
serial+=hex(int(tmps[i*8:i*8+8],2))[2:].rjust(2,'0')
print(serial)

拿到serial:653b987431e5a2fc7c3d748fba008869

最后拿到flag:0725f66471f85ba9d742eb583c75959c

decryption

送!分!题!泪目了呜呜呜呜呜

加密函数在这里:

懒得逆逻辑了,直接copy算法爆破(毕竟是按字节加密hhh。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
arr=[0x12, 0x45, 0x10, 0x47, 0x19, 0x49, 0x49, 0x49, 0x1A, 0x4F, 0x1C, 0x1E, 0x52, 0x66, 0x1D, 0x52, 0x66, 0x67, 0x68, 0x67, 0x65, 0x6F, 0x5F, 0x59, 0x58, 0x5E, 0x6D, 0x70, 0xA1, 0x6E, 0x70, 0xA3]
flag=""
for i in range(32):
for j in range(32,127):
v5=j
v4=i
while True:
v3=2*(v4&v5)
v5^=v4
v4=v3
if v3==0:
break
if v5^0x23==arr[i]:
flag+=chr(j)
break
print(flag)

flag:1e1a6edc1c52e80b539127fccd48f05a

babyre

乍一看主函数逻辑很简单,实际上如果不patch的话是不可能完成的(byte_41A808这里有不可见字符。

看到这篇wp(HWS计划2021硬件安全冬令营线上选拔赛 re wp_20000s的博客-CSDN博客)里说是“内部加载dll,hook了驱动”,好像是这样,不过为啥动态调试的时候没有hook到呢(tcl不懂,选择手动hook

然后注意到了2047这个奇怪的数字,正常来说ZwLoadDriver应该是不会返回这种东西的(吧,所以从左边函数表一个一个翻下来找到了关键函数sub_412A20()

结合前面v9的赋值可以猜测,应该调用这个函数,并且a2是v9,才能对输入的Str调用v4进行加密得到byte_41A808。

所以先直接暴力patch

然后下断点,动态调试,可以看到v4实际上是:

噢,感谢密钥的提示,不用看了hhh,直接sm4解密(从SM4 python_dumpling-cat的博客-CSDN博客抓了脚本改了改)。

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# S盒
SboxTable = \
[
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
]
# 常数FK
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc] ; ENCRYPT = 0 ;DECRYPT = 1
# 固定参数CK
CK = \
[
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
def padding(data): #填充
print ("plaintext:\t", bytes (data))
file_data_list = list(data)
lenth = len (file_data_list)
#print ("data lenth:", lenth)
remainder = lenth % 16
if remainder != 0:
i=16-remainder #i为需要填充的位数
#print ("padding numbers = ", i)
for j in range(i):
file_data_list.append(i) #填充 char 0-(i-1)
if remainder == 0:
for k in range(16):
file_data_list.append (0x08) #刚好的话 填充0x08
print("after PKCS5 padding:",file_data_list)
return file_data_list
def list_4_8_to_int32(key_data): # 列表4个8位,组成32位
return int ((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))
def n32_to_list4_8(n): #把n分别取32位的每8位放入列表
return [int ((n >> 24) & 0xff), int ((n >> 16) & 0xff), int ((n >> 8) & 0xff), int ((n) & 0xff)]
#循环左移
def shift_left_n(x, n):
return int (int (x << n) & 0xffffffff)
def shift_logical_left(x, n):
return shift_left_n (x, n) | int ((x >> (32 - n)) & 0xffffffff) #两步合在一起实现了循环左移n位
def XOR(a, b):
return list (map (lambda x, y: x ^ y, a, b))
#s盒查找
def sbox(idx):
return SboxTable[idx]
def extended_key_LB(ka): #拓展密钥算法LB
a = n32_to_list4_8 (ka) #a是ka的每8位组成的列表
b = [sbox (i) for i in a] #在s盒中每8位查找后,放入列表b,再组合成int bb
bb = list_4_8_to_int32 (b)
rk = bb ^ (shift_logical_left (bb, 13)) ^ (shift_logical_left (bb, 23))
return rk
def linear_transform_L(ka): #线性变换L
a = n32_to_list4_8 (ka)
b = [sbox (i) for i in a]
bb = list_4_8_to_int32 (b) #bb是经过s盒变换的32位数
return bb ^ (shift_logical_left (bb, 2)) ^ (shift_logical_left (bb, 10)) ^ (shift_logical_left (bb, 18)) ^ (shift_logical_left (bb, 24)) #书上公式
def sm4_round_function(x0, x1, x2, x3, rk): #轮函数
return (x0 ^ linear_transform_L (x1 ^ x2 ^ x3 ^ rk))
class Sm4 (object):
def __init__(self):
self.sk = [0] * 32
self.mode = ENCRYPT
def sm4_set_key(self, key_data, mode): #先算出拓展密钥
self.extended_key_last (key_data, mode)
def extended_key_last(self, key, mode): #密钥扩展算法
MK = [0, 0, 0, 0]
k = [0] * 36
MK[0] = list_4_8_to_int32 (key[0:4])
MK[1] = list_4_8_to_int32 (key[4:8])
MK[2] = list_4_8_to_int32 (key[8:12])
MK[3] = list_4_8_to_int32 (key[12:16])
k[0:4] = XOR (MK, FK)
for i in range (32):
k[i + 4] = k[i] ^ (extended_key_LB (k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]))
self.sk = k[4:] #生成的32轮子密钥放到sk中
self.mode = mode
if mode == DECRYPT: #解密时rki逆序
self.sk.reverse ()
def sm4_one_round(self, sk, in_put): #一轮算法 ,4个32位的字=128bit=16个字节(8*16)
item = [list_4_8_to_int32 (in_put[0:4]), list_4_8_to_int32 (in_put[4:8]), list_4_8_to_int32 (in_put[8:12]),
list_4_8_to_int32 (in_put[12:16])] #4字节一个字,把每4个字节变成32位的int
x=item
for i in range (32):
temp=x[3]
x[3] = sm4_round_function (x[0], x[1], x[2], x[3], sk[i]) #x[3]成为x[4]
x[0]=x[1]
x[1]=x[2]
x[2]=temp
print("%dround----->"%(i+1),"key:%-12d\n"%sk[i],"result:",x)
res=x
# res = reduce (lambda x, y: [x[1], x[2], x[3], sm4_round_function (x[0], x[1], x[2], x[3], y)],sk, item) #32轮循环加密
res.reverse ()
rev = map (n32_to_list4_8, res)
out_put = []
[out_put.extend (_) for _ in rev]
return out_put
def encrypt(self, input_data):
# 块加密
output_data = []
tmp = [input_data[i:i + 16] for i in range (0, len (input_data), 16)] #输入数据分块
[output_data.extend (each) for each in map (lambda x: self.sm4_one_round (self.sk, x), tmp)]
return output_data
def encrypt(mode, key, data):
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key, mode)
en_data = sm4_d.encrypt (data)
return en_data
def sm4_crypt_cbc(mode, key, iv, data):
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key, mode)
en_data = sm4_d.sm4_crypt_cbc (iv, data)
return en_data
if __name__ == "__main__":
key_data=[ord(c) for c in "Ez_5M4_C1pH@r!!!"]
en_data=b"\xEA\x63\x58\xB7\x8C\xE2\xA1\xE9\xC5\x29\x8F\x53\xE8\x08\x32\x59\xAF\x1B\x67\xAE\xD9\xDA\xCF\xC4\x72\xFF\xB1\xEC\x76\x73\xF3\x06"
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key_data, DECRYPT)
de_data = sm4_d.encrypt (en_data)
flag=''.join(list(map(chr,de_data)))
print(flag)

flag:42b061b4cb41cfa89ca78047bde1856e

Enigma

逻辑很清晰,直接分析loc_4018F0。

这边有一个SetUnhandledExceptionFilter捕获异常。

一开始以为下面的都是花指令,但每次都是0xC7转不了就很奇怪,异常+0xC7想到了平时用的CC断点,再加上sub_401630看起来像是个虚拟机,感觉0xC7是一个中断号(计组刚学,捕获中断然后用sub_401630处理。

每一个case里sub_4011B0是取值,另一个函数则是处理,v18是指令长度。

通过对ExceptionInfo的查阅发现,v19=*(_DWORD *)(*(_DWORD *)(a1 + 4) + 0xB8)开始是异常发生的地址即0xC7的地址,最后则应该是异常处理完毕后要返回的用户空间代码地址。(这么一推异常应该是C7FF才对

opcode是C7FF的下一个即v19+2,后面的为操作数。

二级函数里面都是对a1[40]~a1[44]进行操作,猜测是寄存器。

于是可以分析出:

1
2
3
4
5
6
7
8
9
10
11
12
// xx为[1,5],rx为对应的a1[40]~a1[44]寄存器,是二级函数中的case
// 1->a1[44],2->a1[41],3->a1[43],4->a1[42],5->a1[40]
// imm为立即数
C7 FF 00 xx imm //add rx,imm
C7 FF 01 xx imm //sub rx,imm
C7 FF 02 xx //inc rx
C7 FF 03 xx //dec rx
C7 FF 04 xx imm //and rx,imm
C7 FF 05 xx imm //or rx,imm
C7 FF 06 xx imm //xor rx,imm
C7 FF 07 xx imm //shl rx,imm
C7 FF 08 xx imm //shr rx,imm

然后把下面的机器码中带有C7 FF系列指令以外的先转换成code。

接下来处理这些异常部分的机器码,比如第一个中断翻译出来是and r1,0,即将r1清空,汇编习惯上更常用xor r1,r1,先用注释标注。

然后根据上下文以及上图可以推断出r1-r5分别对应如下:

1
2
3
4
5
r1 -> eax;
r2 -> ebx;
r3 -> ecx;
r4 -> edx;
r5 -> esi;

patch程序,多余字节直接nop(因为不记得有些机器码了所以直接用keypatch。

然后得到反编译的加密函数。

根据逻辑逆向写出exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from hashlib import md5

dst="938b8f431268f7907a4b6e421301b42120738d68cb19fcf8b26bc4abc89b8d22"
output=[]
for i in range(0,64,2):
output.append(int("0x"+dst[i:i+2],16))
key=[0x42, 0x69, 0x65, 0x72]
tmp2=[output[0]]
for i in range(1,32):
tmp2.append(output[i]^tmp2[i-1]^key[i&3])
tmp1=[]
for i in range(32):
tmp1.append(((tmp2[i]&0xf8)>>3)|((tmp2[(i-1)&0x1F]&0x07)<<5))
v0=0
arr=[]
for _ in range(32):
v0=(v0+17)&0x1F
arr.append(v0)
flag=['-' for _ in range(32)]
for i in range(0,32,2):
flag[arr[i]],flag[arr[i+1]]=chr(tmp1[arr[i+1]]),chr(tmp1[arr[i]])
flag=''.join(flag)
print(md5(flag).hexdigest())

flag:751542a09b8b341dda23ebfc387a5e91

内核安全

easy_kernel

是个ring3调用ring0的题(前半部分。

从ring3看起,v7是flag,走了sub_401005函数。

跳到最后有

int 2E,查了一下是用户模式进入内核模式的中断,于是去看ring0(即DriverXP.sys,调用号是186。

可以看到这里有186,并且参数个数也是6个,猜测sub_401340就是调用的函数。

进一步可以看到,a1==-1,刚好也吻合sub_401005的第一个参数。

findcrypt可以看到DES,并且查交叉引用和反编译代码证实sub_401680就是DES加密过程。

可以看到这边密钥只用了"}aglf_T_ton_5i_sihT_yrroS{galf"的前八字节。

第一层加密确认,从ring3继续往下看。

这个MEMORY[0x5804E8]实在是没看出来是什么,看汇编是call fword ptr,查了一下是个长调用,返回用retf。

gdt表也懒得查,于是在ring3的汇编这里从头开始一个一个往下看retf,准备把碰到的都试一遍。

诶,看到一个,转函数看看。

代到exp里试了一下,发现成了,运气很好hhh,最开始这个就是MEMORY[0x5804E8]。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Cipher import DES
from hashlib import md5

key='}aglf_T_ton_5i_sihT_yrroS{galf'[:8]
c=[0xB2, 0xC4, 0x86, 0xD5, 0x54, 0x6C, 0x38, 0xAD, 0xBD, 0x69, 0xD4, 0xE9, 0x44, 0x47, 0x36, 0x21, 0x99, 0x91, 0xFB, 0x13, 0x70, 0xD8, 0x6B, 0xE4, 0x80, 0x12, 0xE2, 0x43, 0x2A, 0x4B, 0x49, 0x8E]
for i in range(30,-1,-1):
c[i]^=c[i+1]
cipher=''.join(list(map(chr,c)))
des=DES.new(key)
flag=des.decrypt(cipher)
print(flag)
print(md5(flag).hexdigest())

flag:c1878dfb2b0c23c74ec4e6650d8f7004

固件安全

NodeMCU

strings nodemcu.bin > str.out,导出字符串后直接查找flag即可

flag:6808dcf0-526e-11eb-92de-acde48001122

STM

用bin2hex将bin文件转为hex文件,然后用ida加载hex。

查找STM的datasheet(以常见的STM32F103CB为参照)可知flash映射在0x08000000地址上

于是载入ida的时候还要把基址改成0x08000000。

然后查找字符串,直接看Hello World的交叉引用,找到主函数。

根据相应的函数功能猜测并命名,sub_8000314是加密函数。

逻辑很简单,直接写exp:

1
2
3
arr=[0x7D, 0x77, 0x40, 0x7A, 0x66, 0x30, 0x2A, 0x2F, 0x28, 0x40, 0x7E, 0x30, 0x33, 0x34, 0x2C, 0x2E, 0x2B, 0x28, 0x34, 0x30, 0x30, 0x7C, 0x41, 0x34, 0x28, 0x33, 0x7E, 0x30, 0x34, 0x33, 0x33, 0x30, 0x7E, 0x2F, 0x31, 0x2A, 0x41, 0x7F, 0x2F, 0x28, 0x2E, 0x64]
for x in arr:
print(chr((x^0x1E)+3),end='')

flag{1749ac10-5389-11eb-90c1-001c427bd493}

文章作者: c10udlnk
文章链接: https://c10udlnk.top/p/wpFor-2021HWSTrial/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 c10udlnk_Log