-
-
[原创][看雪.众安 2021 KCTF 秋季赛]第二题 迷失丛林 WP
-
2021-11-16 23:36 16758
-
[看雪.众安 2021 KCTF 秋季赛]第二题 迷失丛林 WP
题目是一个32位的窗体程序,通过GetDlgItemTextA
函数定位到消息处理函数sub_401270。主要部分伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | GetDlgItemTextA(this[ 1 ], 1000 , String, 40 ); if ( strlen(String) = = 32 && unhex_4014A0(( int )unhex_input_part1_4041F0, ( int )String, 32 ) = = 16 && ( * (_DWORD * )&table_404000[ 4 ] = unhex_input_part1_4041F0[ 1 ], * (_DWORD * )table_404000 = unhex_input_part1_4041F0[ 0 ], check_401580(this, &unhex_input_part2_4041F8) > 0 ) ) { DestroyWindow(this[ 1 ]); } else { MessageBoxA(this[ 1 ], Text, Caption, 0x40u ); DestroyWindow(this[ 1 ]); } PostQuitMessage( 1 ); |
取得输入后,检查输入长度为32,然后进行unhex操作得到16字节的数据,进入验证。
验证阶段,把处理得到的16字节数据分成前后两个8字节数据,进行两步验证,第一步验证验证前8字节数据(过程只涉及前8字节),后面验证后8字节(过程涉及前8字节参与变换的表)。
仔细研究了下,前后两部分均用到的0x404000处的表大小为256,其前8字节由输入数据处理后的前8字节初始化,且第二步的验证中还有表元素的交换操作,再加上其248个原始值都是唯一的,所以比较确定整个表的256个值原本都是唯一的,也就是说输入变换的前8字节必定表中未出现的8个值,只是需要确定顺序。所以第一部分的输入可以用穷举的方式通过第一步的验证条件来确定。
至于第二部分算法,没看懂。但是明显其算法是单字节进行的,也可以通过单字节爆破的方式来得到结果。
两部分的爆破脚本如下(unhex操作中,高低4bit顺序需要注意):
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 | # -*- coding:utf-8 -*- import itertools def test(): table = map ( ord , file ( 'table.bin' , 'rb' ).read()) dst = [ 0 ] * 8 a = 0x21 i = 0 dst[i] = table[i] for j in range ( 8 ): if a & 1 : dst[i] + = 1 else : dst[i] = table[dst[i]] a >> = 1 if i in [ 0 , 7 ]: dst[i] - = 1 print dst[i] def crack_part2(): table = map ( ord , file ( 'table.bin' , 'rb' ).read()) dst = [ 0 ] * 8 flag = 'B4D682C8BF2DE13A' check = [ 0x47 , 0x6F , 0x6F , 0x64 , 0x4A , 0x6F , 0x62 , 0x7E ] for i in range ( 8 ): for k in range ( 256 ): a = k dst[i] = table[i] for j in range ( 8 ): if a & 1 : dst[i] + = 1 else : dst[i] = table[dst[i]] a >> = 1 if i in [ 0 , 7 ]: dst[i] - = 1 if dst[i] = = check[i]: flag + = '{:02X}' . format (k)[:: - 1 ] break print 'flag:' + flag print 'flag:' + flag def crack_table(a1,a2,a3,a4,a5,a6,a7,a8): byte_40400 = table = map ( ord , file ( 'table_ori.bin' , 'rb' ).read()) byte_40400[ 0 ] = a1 byte_40400[ 1 ] = a2 byte_40400[ 2 ] = a3 byte_40400[ 3 ] = a4 byte_40400[ 4 ] = a5 byte_40400[ 5 ] = a6 byte_40400[ 6 ] = a7 byte_40400[ 7 ] = a8 i = 0 dword_404100 = [ 0x00000002 , 0x00000004 , 0x00000008 , 0x00000010 , 0x00000020 , 0x00000040 , 0x00000080 , 0 ] v21 = 0 v22 = 0 v23 = 0 v24 = 0 byte_404220 = [ 0 ] * 512 byte_v25 = [] while i< 256 : byte_v25.append([ 0 ] * 256 ) j = 0 byte_404220[ 0 ] = byte_40400[i] byte_404220[ 1 ] = (i + 1 ) % 256 tt = 2 v3 = 0 while j< 7 : tmp = dword_404100[j] double = 0 while tmp: byte_404220[tt] = byte_40400[byte_404220[v3]] byte_404220[tt + 1 ] = (byte_404220[v3] + 1 ) % 256 tt + = 2 v3 + = 1 tmp - = 1 j + = 1 for k in range ( 256 ): byte_v25[i][byte_404220[v3]] + = 1 v3 + = 1 i + = 1 for i in range ( 256 ): first = 40 if byte_v25[i][first - 40 ]: v21 + = 1 if byte_v25[i][first - 26 ]: v22 + = 1 if byte_v25[i][first]: v23 + = 1 if byte_v25[i][first + 0x27 ]: v24 + = 1 if v21 = = 0xa9 and v22 = = 0xac and v23 = = 0xa7 and v24> 0xc8 : print ( "Got" ) return True return False def crack_part1(): t = [ 0x1e , 0x28 , 0x4b , 0x6d , 0x8c , 0xa3 , 0xd2 , 0xfb ] it = itertools.permutations(t) for i in it: if crack_table( * i): print ' '.join(map(lambda x:' {: 02X }'. format (x)[:: - 1 ],i)) break def main(): # crack_part1() crack_part2() if __name__ = = '__main__' : main() |
最后结果为:B4D682C8BF2DE13AD9B6AEF24A80CB22
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
他的文章
看原图