目录
119_[CFI-CTF 2018]Automated Reversing
用IDA打开后发现main先输入再调用start_quest然后就进行比较
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- ...
- std::operator<<<std::char_traits<char>>(
- &std::cout,
- (unsigned int)"Enter the dragon's secret: ",
- "Enter the dragon's secret: ");
- fgets(s, 257, stdin);
- std::allocator<char>::allocator(v8);
- std::string::string(v9, s, v8);
- std::allocator<char>::~allocator(v8);
- std::string::string((std::string *)v7, (const std::string *)v9);
- started = start_quest((std::string *)v7);
- std::string::~string((std::string *)v7);
- if ( started == 4919 )
- {
- std::string::string((std::string *)v6, (const std::string *)v9);
- reward_strength(v6);
- std::string::~string((std::string *)v6);
- }
- ...
- }
在start_duest中有一大堆数据
- __int64 __fastcall start_quest(std::string *a1)
- {
- ......
- v10 = a1;
- if ( y26 >= 10 && ((((_BYTE)x25 - 1) * (_BYTE)x25) & 1) != 0 ) #恒为0无条件不跳转
- goto LABEL_13;
- while ( 1 )
- {
- v9 = &v2[-2];
- v8 = &v2[-2];
- v7 = (int *)&v2[-2];
- v6 = (std::string *)&v2[-2];
- std::vector<int>::push_back(&hero, &secret_100); //数据
- std::vector<int>::push_back(&hero, &secret_214);
- std::vector<int>::push_back(&hero, &secret_266);
- std::vector<int>::push_back(&hero, &secret_369);
- ......
很明显这跟flag有关,先看数据
- .data:000000000061013C 64 secret_100 db 64h ; d ; DATA XREF: start_quest(std::string)+5E↑o
- .data:000000000061013C ; start_quest(std::string)+8CD↑o
- .data:000000000061013D 00 db 0
- .data:000000000061013E 00 db 0
- .data:000000000061013F 00 db 0
- .data:0000000000610140 public secret_214
- .data:0000000000610140 D6 secret_214 db 0D6h ; DATA XREF: start_quest(std::string)+AF↑o
- .data:0000000000610140 ; start_quest(std::string)+8E6↑o
- .data:0000000000610141 00 db 0
- .data:0000000000610142 00 db 0
- .data:0000000000610143 00 db 0
- .data:0000000000610144 public secret_266
- .data:0000000000610144 0A secret_266 db 0Ah ; DATA XREF: start_quest(std::string)+C8↑o
- .data:0000000000610144 ; start_quest(std::string)+8FF↑o
- .data:0000000000610145 01 db 1
- .data:0000000000610146 00 db 0
- .data:0000000000610147 00 db 0
- .data:0000000000610148 public secret_369
- .data:0000000000610148 71 secret_369 db 71h ; q ; DATA XREF: start_quest(std::string)+E1↑o
- .data:0000000000610148 ; start_quest(std::string)+918↑o
- .data:0000000000610149 01 db 1
- .data:000000000061014A 00 db 0
- .data:000000000061014B 00 db 0
- .data:000000000061014C public secret_417
- .data:000000000061014C A1 secret_417 db 0A1h ; DATA XREF: start_quest(std::string)+FA↑o
- .data:000000000061014C ; start_quest(std::string)+931↑o
- .data:000000000061014D 01 db 1
- .data:000000000061014E 00 db 0
- .data:000000000061014F 00 db 0
- .data:0000000000610150 public secret_527
- .data:0000000000610150 0F secret_527 db 0Fh ; DATA XREF: start_quest(std::string)+113↑o
- .data:0000000000610150 ; start_quest(std::string)+94A↑o
- .data:0000000000610151 02 db 2
- .data:0000000000610152 00 db 0
- .data:0000000000610153 00 db 0
这些数据是64,d6,10a,171明显超过ascii的范围,并且是递增的,后边的算法也看不明白,试试后边减前边得到64,114,52这些都能转成ascii码,试着转一下
- a = [0,100,214,266,369,417,527,622,733,847,942,1054,1106,1222,1336,1441,1540,1589,1686,1796,1891,1996,2112,2165,2260,2336,2412,2498,2575]
- print(bytes([a[i]- a[i-1] for i in range(1, len(a))]))
- #dr4g0n_or_p4tric1an_it5_LLVM
- #flag{dr4g0n_or_p4tric1an_it5_LLVM}
确实得到的就是flag,有一定懵的成分。
这个整不出来,看了下WP也不大明白,对照代码一点点注释。
- int __cdecl main(int argc, const char **argv, const char **envp)
- {
- ...
- sub_140004DD0(std::cin, v52); // 读入
- v4 = v52;
- if ( v54 >= 0x10 )
- v4 = (void **)v52[0];
- v5 = (void **)((char *)v4 + v53);
- v20[0] = 9024;
- *(_QWORD *)v23 = &v22;
- v23[8] = byte_14000C7D7;
- v31 = *(__m128d *)v23;
- v32 = &unk_14000C7ED;
- v33 = &v31;
- *(_QWORD *)v23 = (char *)&v21 + 2;
- v34 = *(__m128d *)v23;
- v29[0] = (__int64)&unk_14000C7ED;
- v29[1] = (__int64)&v34;
- *(_QWORD *)v23 = &v21;
- v23[8] = byte_14000C7D7;
- v27[0] = (__int64)&unk_14000C7ED;
- v27[1] = (__int64)&v35;
- v28[0] = (__int64)v27;
- v28[1] = (__int64)v20;
- v30[0] = (__int64)v28;
- v30[1] = (__int64)v29;
- v36 = v30;
- v37 = (__int64)v20 + 1;
- v48 = (__int64)v4 + v53;
- *((_QWORD *)&v24 + 1) = *(_QWORD *)&v31.m128d_f64[0];
- *(__m128d *)&v46[8] = v34;
- *(_OWORD *)&v23[8] = *(_OWORD *)v23;
- v35 = *(__m128d *)&v23[8];
- v38 = *(_OWORD *)v23; // 数据结构 d1 @ d2 # d3
- v39 = *(_OWORD *)&_mm_unpackhi_pd(v35, v35);
- v40 = '@';
- v41 = *(_OWORD *)v46;
- v42 = *(_OWORD *)&_mm_unpackhi_pd(v34, v34);
- v43 = '#';
- v44 = v24;
- v45 = *(_OWORD *)&_mm_unpackhi_pd(v31, v31);
- v47 = v4;
- *(_QWORD *)&v24 = &v47;
- *((_QWORD *)&v24 + 1) = &v48;
- v25 = &unk_14000C808;
- v26 = &unk_14000C808;
- *(_QWORD *)v23 = &v38;
- if ( sub_140005910((__int64 *)v23, (__int64)&v21, (__int64 **)&v24) || v47 != v5 )// 判断结构 d1@d2#d3
- _exit(0);
- LODWORD(v47) = v21;
- WORD2(v47) = v22;
- sub_1400029B0(&v47); // d1 = 78
- sub_1400046A0(Block);
- if ( v50 != 5 )
- goto LABEL_40;
- v6 = time64(0i64);
- if ( v6 - v3 > 3 )
- goto LABEL_40;
- v7 = Block;
- if ( v51 >= 0x10 )
- v7 = (void **)Block[0];
- if ( aEqdtw91a0qwryu[*(char *)v7 - 48] != 68 )// d2 = 20637
- goto LABEL_39;
- v8 = Block;
- if ( v51 >= 0x10 )
- v8 = (void **)Block[0];
- if ( aEqdtw91a0qwryu[*((char *)v8 + 1) - 48] != 101 )
- goto LABEL_39;
- v9 = Block;
- if ( v51 >= 0x10 )
- v9 = (void **)Block[0];
- if ( aEqdtw91a0qwryu[*((char *)v9 + 2) - 48] != 49 )
- goto LABEL_39;
- v10 = Block;
- if ( v51 >= 0x10 )
- v10 = (void **)Block[0];
- if ( aEqdtw91a0qwryu[*((char *)v10 + 3) - 48] != 116 )
- goto LABEL_39;
- v11 = Block;
- if ( v51 >= 0x10 )
- v11 = (void **)Block[0];
- if ( aEqdtw91a0qwryu[*((char *)v11 + 4) - 48] != 97 )
- {
- LABEL_39:
- Sleep(5u);
- _exit(0);
- }
- if ( (int)(time64(0i64) - v6) > 2 )
- LABEL_40:
- _exit(0);
- if ( WORD2(v47) % (unsigned int)(unsigned __int16)v47 != 12 && WORD2(v47) / (unsigned int)(unsigned __int16)v47 != 3 )// v47[1] = v47[0]*3+12 d3=114
- {
- sub_1400041F0(std::cout, "You failed...again");
- _exit(0);
- }
- v12 = sub_1400041F0(std::cout, "Your flag is:");
- std::ostream::operator<<(v12, sub_1400043C0);
- v13 = sub_1400041F0(std::cout, "de1ctf{");
- v14 = v52;
- if ( v54 >= 0x10 )
- v14 = (void **)v52[0];
- v15 = sub_140004FC0(v13, v14, v53);
- v16 = sub_1400041F0(v15, "}");
- std::ostream::operator<<(v16, sub_1400043C0);
- if ( v51 >= 0x10 )
- {
- v17 = Block[0];
- if ( v51 + 1 >= 0x1000 )
- {
- v17 = (void *)*((_QWORD *)Block[0] - 1);
- if ( (unsigned __int64)(Block[0] - v17 - 8) > 0x1F )
- invalid_parameter_noinfo_noreturn();
- }
- j_j_free(v17);
- }
- v50 = 0i64;
- v51 = 15i64;
- LOBYTE(Block[0]) = 0;
- if ( v54 >= 0x10 )
- {
- v18 = v52[0];
- if ( v54 + 1 >= 0x1000 )
- {
- v18 = (void *)*((_QWORD *)v52[0] - 1);
- if ( (unsigned __int64)(v52[0] - v18 - 8) > 0x1F )
- invalid_parameter_noinfo_noreturn();
- }
- j_j_free(v18);
- }
- return 0;
- }
在输入后,找两个定位符@#然后逐个段时判断,d2容易点一比较就行,其它两个都是动调,没啥好想的,运行到那就出现了。结果就是这样
- #123@456#789
- #d1 = 78
- #d2 = 20637
- #d3 = 114
- #flag{78@20637#114}
附件里有970个小文件,还随机送了生成程序和解密程序solution.py 当时一楞,还给了解密程序。直接运行不就得了。不过可能那是python2写的,我这3有点小问题,于是小小改动一点。输出一大堆东西,在里边就有flag
到了1分以后难的是真难,但也有不少简单的一塌糊涂
- flag = b''
- for i in range(970):
- path = "binaries/binary{}".format(i)
- with open(path, "rb") as f:
- binary = f.read()
- operator = binary[0xca]
- key = binary[0xcb]
- check = binary[0xce]
-
- if operator == 0xc2: # add
- flag += bytes([(0x100+ check - key) & 0xff])
-
- elif operator == 0xea: # sub
- flag += bytes([(check + key) & 0xff])
-
- else:
- flag += bytes([check ^ key])
-
- print(flag)
- #改python3 chr改为bytes
- #flag{1s_th1s_4_pr0g_ch4ll_0r_4_r3ve3se_ch4ll?}
- '''
- b'\nLorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula dolor. Aenean massa. Cum sociis nato
- que penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu,
- pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.
- In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tin
- cidunt. Cras dapibus. The flag is CFI{1s_th1s_4_pr0g_ch4ll_0r_4_r3ve3se_ch4ll?}. Aenean vulputate eleifend tellus. Aenea
- n leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. I stole this idea directly from Defcon Quals 2016. Phase
- llus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur
- ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semp
- er libero, si'
- '''
这个题实在作不出来,就看了WP,发现原题有个提示就是给了md5值,呵呵。
hint md5('rctf{'+input+'}').digest.hex() == '5f8243a662cf71bf31d2b2602638dc1d'
题目在main里进行了几个操作
- __int64 __fastcall main(int a1, char **a2, char **a3)
- {
- ...
- memset(v11, 0, 0x100uLL);
- __printf_chk(1LL, "Input right flag you can got 'Bingo!' :");
- __isoc99_scanf("%31s", v11);
- v3 = &v11[strlen(v11)];
- if ( (unsigned __int64)(v3 - v11) > 0x10 )
- {
- puts("input is too long!");
- }
- else if ( v3 - v11 == 16 )
- {
- v4 = sub_C00((unsigned __int64)v11, 16, (char **)&ptr);// 16进制转数字
- if ( v4
- && (v5 = sub_1180(ptr, v4, (__int64)&unk_202010, 16, &v9), (v6 = v5) != 0LL)// tea
- && v9 > 0
- && (unsigned __int16)((__int64 (__fastcall *)(char *))sub_13D0)(v5) == 27106 )// crc校验
- {
- for ( i = 0LL; v9 > (int)i; ++i )
- v6[i] ^= 0x17u;
- puts(v6);
- if ( ptr )
- free(ptr);
- free(v6);
- }
- else
- {
- puts("input flag is wrong!");
- }
- }
- else
- {
- puts("input is too short!");
- }
- return 0LL;
- }
一开始就是说会输出Bingo,然后是16进制转换和tea加密(用findcrypt能搜出一堆来,看上去是标准加密)
然后是个自制的crc校验,其实这里一直也没逆出来,也就不逆了,从前边两项和md5值入手就行了。
tea解密后与0x17异或是Bingo但还少2字符,也就是这个需要爆破,tea的key在202010已经给出。
也就是将Bingo爆破两位与0x17异或后用tea加密,再求md5进行比较
- #hint md5('rctf{'+input+'}').digest.hex() == '5f8243a662cf71bf31d2b2602638dc1d' buu未给出
- import xxtea
- import hashlib
-
- key = [0xc7,0xe0,0xc7,0xe0,0xd7,0xd3,0xf1,0xc6,0xd3,0xc6,0xd3,0xc6,0xce,0xd2,0xd0,0xc4]
- text = [v^0x17 for v in b'Bingo!\0\0']
-
- for i in range(0x100):
- for j in range(0x100):
- text[6] = i
- text[7] = j
- c = xxtea.encrypt(bytes(text), bytes(key), padding=False ).hex()
- flag = 'rctf{'+c+'}'
- #print(flag)
- if hashlib.md5(flag.encode()).digest().hex() == '5f8243a662cf71bf31d2b2602638dc1d':
- print('OK:',flag)
- exit()
- #rctf{05e8a376e4e0446e}
- #flag{05e8a376e4e0446e}