-
-
[原创]bytectf mordencpp
-
发表于: 2021-11-8 17:38 6202
-
程序分析
最终判断逻辑非常简单,与v10做比较,相同就提示congrats!(真的只是逐个byte比较),不过v10长得比较奇怪。
胡乱输入数据,到达最后判断函数的时候输入的数据不会改变,显然这个题目不会是让你直接输入v10(因为输入是flag)。
我们再查看keyfunc函数。
在keyfunc中,如果发现输入的字符串长度为41且开头为"bytectf{"且结尾为"}",则进行一个错误(exception)的抛(cxa_throw)。
抛出错误后会跳转到entrance处的catch部分(符号被去掉了)(跳转过程省略)
catch下面的汇编代码在伪代码界面并不会显示
按照提示构造payload后程序会输出"H4ckingT0Th3Gate"。
在keyfunc下面的虚表函数调用处进行一个断点的打,然后用ida进行一个调试的跟,进入函数process1(自己起的名称)(如果没有走catch的路线,则会进入另外一个函数)。
函数的开头部分将输入的字符串逐个转化为01组成的字符串,并将其串在一起。
我们输入的字符串第一字符是'b',生成的字符串存在了v14里
每次循环v14都会将字符串append在v13上
函数的后半部分将生成的01字符串当成二进制数,每8位将其转换为16进制数据,并存放在一起
处理后的数据保存在v13里,由v13传递给v9,再由v9传递给v4,再当作参数传递给某虚表函数调用
跟进虚表函数调用,发现是tea加密函数。
tea加密后的数据保存在v14里,由v14传递到v8,再由v8传递到input_1,与v10进行比较。
如果与v10一致则检测通过。
题解
提取码表
首先要解决生成的01字符串与输入字符的对应问题,经过一番乱找,在keyfunc中的某个函数中发现了一个奇怪的数组的初始化。
此数组在执行本函数的时候会将原本的数据逐个与0xAAu进行异或,得到一个字符串。
长得就很像一个码表,接下来只要找到这些字母与生成的01字符串的对应关系即可。
首先利用这些字符构造payload:"bytectf{adghijklmnopqrsuvwxz0123456789!@}"和"bytectf{adghijklmnopqrsuvw#%^&*()_+-=[];}"这样就能生成所有字符对应的01字符串
在ida里动态调试跟进第一个虚表函数调用,在汇编界面中,在call 生成01字符串
的下一行打下断点,运行到此位置会发现rax里面存着相应的01字符串以及其长度(rax+8)。
打下断点之后用IDApython提取信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from ida_dbg import * input = "bytectf{adghijklmnopqrsuvw#%^&*()_+-=[];}" continue_process() dir = {} for i in range ( 41 ): tmp = "" wait_for_next_event(WFNE_SUSP , - 1 ) rax = get_reg_value( "rax" ) rax_value = read_dbg_qword(rax) len = read_dbg_byte(rax + 0x8 ) for j in range ( len ): tmp + = chr (read_dbg_byte(rax_value + j)) dir [tmp] = input [i] continue_process() print ( dir ) |
把两个payload都跑一遍就能得到所有对应关系了。
1 | dir = { '00001' : 'b' , '10011' : 'y' , '11000' : 't' , '0011010' : 'e' , '01110' : 'c' , '010010' : 'f' , '10001' : '{' , '100101' : 'a' , '11011' : 'd' , '111011' : 'g' , '01000' : 'h' , '10110' : 'i' , '00110111' : 'j' , '1111010' : 'k' , '110010' : 'l' , '00011' : 'm' , '10000' : 'n' , '10100011101' : 'o' , '0110011' : 'p' , '011000' : 'q' , '111110' : 'r' , '01011' : 's' , '11110110' : 'u' , '000001' : 'v' , '111000' : 'w' , '00101' : 'x' , '101000110' : 'z' , '1110010' : '0' , '100100' : '1' , '111111' : '2' , '01101' : '3' , '11010' : '4' , '11110111' : '5' , '001100' : '6' , '111010' : '7' , '00111' : '8' , '10101' : '9' , '00110110' : '!' , '1110011' : '@' , '1010001111' : '}' , '101001' : '#' , '00010' : '%' , '01111' : '^' , '10100011100' : '&' , '0110010' : '*' , '010011' : '(' , '111100' : ')' , '01010' : '_' , '10111' : '+' , '10100010' : '-' , '000000' : '=' , '110011' : '[' , '00100' : ']' , '1010000' : ';' } |
逆向数据
理论上来说只需要对v10进行一个tea的解密,再进行一个码表的查就能得出flag。
tea
只要找到key就行了,v17、v16、v15、v14分别对应key[0]、key[1]、key[2]、key[3]。
v17="etyb",v16="ftc-",v15="clew",v14="~emo"。所以key="tybftc-clew~emo"
从ida里提取出来v10的数据。
1 2 3 4 5 6 7 | unsigned char ida_chars[ 40 ] = { 0x9F , 0x66 , 0xD3 , 0xC5 , 0x1A , 0x17 , 0x17 , 0xB9 , 0x19 , 0x7B , 0xB3 , 0xB4 , 0x5F , 0x0C , 0xE8 , 0x0A , 0x7F , 0x30 , 0x80 , 0x8D , 0x80 , 0x28 , 0x52 , 0x21 , 0x89 , 0x05 , 0xD8 , 0x34 , 0xD1 , 0x83 , 0x6C , 0xDE , 0x18 , 0x36 , 0xB7 , 0x59 , 0x35 , 0x5D , 0xE6 , 0xC6 }; |
一次解密8byte的数据,一共要解密5次。
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 | void decrypt(uint32_t * v, uint32_t * key) { uint32_t l = v[ 0 ], r = v[ 1 ], sum = 0 , delta = 0x9e3779b9 ; sum = delta * 32 ; for (size_t i = 0 ; i < 32 ; i + + ) { r - = ((l << 4 ) + key[ 2 ]) ^ (l + sum ) ^ ((l >> 5 ) + key[ 3 ]); l - = ((r << 4 ) + key[ 0 ]) ^ (r + sum ) ^ ((r >> 5 ) + key[ 1 ]); sum - = delta; } v[ 0 ] = l; v[ 1 ] = r; } unsigned char ida_chars[] = { 0x9F , 0x66 , 0xD3 , 0xC5 , 0x1A , 0x17 , 0x17 , 0xB9 , 0x19 , 0x7B , 0xB3 , 0xB4 , 0x5F , 0x0C , 0xE8 , 0x0A , 0x7F , 0x30 , 0x80 , 0x8D , 0x80 , 0x28 , 0x52 , 0x21 , 0x89 , 0x05 , 0xD8 , 0x34 , 0xD1 , 0x83 , 0x6C , 0xDE , 0x18 , 0x36 , 0xB7 , 0x59 , 0x35 , 0x5D , 0xE6 , 0xC6 }; int main() { / / test unsigned char k[] = "etybftc-clew~emo" ; uint32_t * v = (uint32_t * )ida_chars; uint32_t * key = (uint32_t * )k; for ( int j = 0 ; 8 * j< 0x28 ; + + j) decrypt(v + 2 * j,key); / / for ( int i = 0 ;i< 40 ;i + + ){ / / printf( "0x%x," ,v[i]); / / } unsigned char * res = (unsigned char * )v; for ( int i = 0 ;i< 42 ;i + + ){ printf( "0x%x," ,res[i]); } return 0 ; } |
得出解密后的数据,用python的列表存储,然后将每个byte转化为8位的二进制数据。
1 2 3 | list = [ 0xc , 0xf0 , 0x69 , 0xd8 , 0x4a , 0x32 , 0xfb , 0x62 , 0x8e , 0xa4 , 0xcc , 0xc , 0xc0 , 0x22 , 0x63 , 0xe5 , 0xb6 , 0xfd , 0x7 , 0x5e , 0xe6 , 0xfe , 0xc6 , 0x8d , 0xfd , 0x8d , 0x51 , 0xad , 0xe4 , 0x68 , 0xfa , 0x14 , 0x78 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ] for i in list : print ( bin (i)[ 2 :].rjust( 8 , '0' ),end = "") |
得出二进制字符串。
1 | str = "000011001111000001101001110110000100101000110010111110110110001010001110101001001100110000001100110000000010001001100011111001011011011011111101000001110101111011100110111111101100011010001101111111011000110101010001101011011110010001101000111110100001010001111000000000000000000000000000000000000000000000000000000000000000000000000000" |
暴力匹配码表
将其与前面得到的字典(输入与01字符串的对应关系)进行一个暴力匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | dir = { '00001' : 'b' , '10011' : 'y' , '11000' : 't' , '0011010' : 'e' , '01110' : 'c' , '010010' : 'f' , '10001' : '{' , '100101' : 'a' , '11011' : 'd' , '111011' : 'g' , '01000' : 'h' , '10110' : 'i' , '00110111' : 'j' , '1111010' : 'k' , '110010' : 'l' , '00011' : 'm' , '10000' : 'n' , '10100011101' : 'o' , '0110011' : 'p' , '011000' : 'q' , '111110' : 'r' , '01011' : 's' , '11110110' : 'u' , '000001' : 'v' , '111000' : 'w' , '00101' : 'x' , '101000110' : 'z' , '1110010' : '0' , '100100' : '1' , '111111' : '2' , '01101' : '3' , '11010' : '4' , '11110111' : '5' , '001100' : '6' , '111010' : '7' , '00111' : '8' , '10101' : '9' , '00110110' : '!' , '1110011' : '@' , '1010001111' : '}' , '101001' : '#' , '00010' : '%' , '01111' : '^' , '10100011100' : '&' , '0110010' : '*' , '010011' : '(' , '111100' : ')' , '01010' : '_' , '10111' : '+' , '10100010' : '-' , '000000' : '=' , '110011' : '[' , '00100' : ']' , '1010000' : ';' } str = "000011001111000001101001110110000100101000110010111110110110001010001110101001001100110000001100110000000010001001100011111001011011011011111101000001110101111011100110111111101100011010001101111111011000110101010001101011011110010001101000111110100001010001111000000000000000000000000000000000000000000000000000000000000000000000000000" start = 0 end = 5 while ( 1 ): tmp = str [start:end] # print(tmp) for i in dir .keys(): if (i = = tmp): print ( dir [i],end = '') start = end end = start + 4 break end = end + 1 |
赞赏
- [原创]CVE-2021-4034分析 12369
- [原创]cve-2022-0847复现 11611
- [原创]FlatBuffers小记 10751
- [原创]一次简单的golang栈溢出 13093
- [原创]第五空间 crackme深度分析 12459