-
-
[原创]2022MT-CTF Re
-
2022-9-18 22:45 5976
-
2022MT-CTF逆向部分题解,这次题目比较有意思,一大一小,small and static。
small
很精致的のlf文件,IDA加载失败,但是代码足够小,用cutter进行识别。
关键代码逻辑如下,可见是个tea加密,魔改了delta和加密轮数。
翻译后的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 | void Tea_Encrypt(ut32 * src) { ut32 sum = 0 ; ut32 v0 = src[ 0 ]; ut32 v1 = src[ 1 ]; for ( int i = 0 ; i < 0x23 ; i + + ) { sum + = 0x67452301 ; v0 + = ((v1 << 4 ) + 1 ) ^ (v1 + sum ) ^ ((v1 >> 5 ) + 0x23 ); v1 + = ((v0 << 4 ) + 0x45 ) ^ (v0 + sum ) ^ ((v0 >> 5 ) + 0x67 ); } src[ 0 ] = v0; src[ 1 ] = v1; } |
接着是密文比对,是倒叙进行比对。
因为rsi在上面代码中每次会+8,所以实际的密文顺序不变,并且在偏移0x100f7处。
解密代码如下
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 | """ enc=list(bytes.fromhex('437108ded21bf9c4dcdaf6da4cd59e6de74eeb7504dc1d5dd90f1b51fb88dc51')) for i in range(0,len(enc),4): num=0 for j in range(4): num|=(enc[i+j]<<(8*j)) print(hex(num),end=',') """ #include<iostream> #define ut32 unsigned int #define delta 0x67452301 void Tea_Decrypt(ut32 * enc) { unsigned int sum = 0x67452301 * 0x23 ; / / 0x1e73c923 ; ut32 v0 = enc[ 0 ]; ut32 v1 = enc[ 1 ]; for ( int i = 0 ; i < 0x23 ; i + + ) { v1 - = ((v0 << 4 ) + 0x45 ) ^ (v0 + sum ) ^ ((v0 >> 5 ) + 0x67 ); v0 - = ((v1 << 4 ) + 1 ) ^ (v1 + sum ) ^ ((v1 >> 5 ) + 0x23 ); sum - = 0x67452301 ; } enc[ 0 ] = v0; enc[ 1 ] = v1; } void Tea_Encrypt(ut32 * src) { ut32 sum = 0 ; ut32 v0 = src[ 0 ]; ut32 v1 = src[ 1 ]; for ( int i = 0 ; i < 0x23 ; i + + ) { sum + = 0x67452301 ; v0 + = ((v1 << 4 ) + 1 ) ^ (v1 + sum ) ^ ((v1 >> 5 ) + 0x23 ); v1 + = ((v0 << 4 ) + 0x45 ) ^ (v0 + sum ) ^ ((v0 >> 5 ) + 0x67 ); } printf( "%08x\n" , sum ); src[ 0 ] = v0; src[ 1 ] = v1; } int main() { ut32 enc[ 8 ] = { 0xde087143 , 0xc4f91bd2 , 0xdaf6dadc , 0x6d9ed54c , 0x75eb4ee7 , 0x5d1ddc04 , 0x511b0fd9 , 0x51dc88fb }; for ( int i = 0 ; i < 8 ; i + = 2 ) { Tea_Decrypt(enc + i); } for ( int i = 0 ; i < 32 ; i + + ) { printf( "%c" , * ((unsigned char * )enc + i)); } / / 327a6c4304ad5938eaf0efb6cc3e53dc return 0 ; } |
static
采用静态编译,程序比较的臃肿,加载或者是恢复符号需要大量的时间,并且程序控制流走向不是很清晰。
因为符号恢复时间比较长,所以wp基于修复后的程序编写,符号恢复思路是全局使用Lumina,对于部分函数使用finger。
主函数处理如下,前部分是取出flag的内容,去掉"-"的符号
可是调试发现,在循环右移2后,走到vector_unicorn
的地方程序会退出,并且v10
是重复的两组显然不是需要的flag。
借助find_crypt
插件,发现程序中存在aes加密,所以猜测发生了重定位。用pintool
打印trace
,最好是code coverage
,因为比较轻量且会高亮控制流。
https://github.com/gaasedelen/lighthouse
测试输入flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa},走过的代码会被高亮。
结合对表的交叉引用,发现可疑处理函数。
继续交叉引用,直到sub_4064F0
发现该部分处理过32字节的字符串,分成两段处理,IDA调试发现该部分断下后rdi0
中存储的是输入flag{}中的内容。
第一段处理
结合常量表和10次异或,猜测是aes加密,并且轮秘钥已预先生成好写在了10个xmmword
变量中,所以第一个便是初始秘钥。
对于第二部分的加密,因为之前出现的unicorn
字符串,并且结构与unicorn
比较类似,搜索一个c调用的模板进行比对。
sub_406BBB
是uc_open
确定架构和模式,2对应着ARM64。
1 2 3 4 5 6 7 8 9 10 | typedef enum uc_arch { UC_ARCH_ARM = 1 , / / ARM architecture (including Thumb, Thumb - 2 ) UC_ARCH_ARM64, / / ARM - 64 , also called AArch64 UC_ARCH_MIPS, / / Mips architecture UC_ARCH_X86, / / X86 architecture (including x86 & x86 - 64 ) UC_ARCH_PPC, / / PowerPC architecture (currently unsupported) UC_ARCH_SPARC, / / Sparc architecture UC_ARCH_M68K, / / M68K architecture UC_ARCH_MAX, } uc_arch; |
字节码存在byte_19762c0
中,调试可知v2=0x5dc, dump出0x5dc个字节写入一个文件,ida用arm架构解析,结合代码可知该部分是对第二部分flag进行了处理。
1 2 3 4 5 6 7 8 | code = [ 0xFF , 0x43 , 0x01 , 0xD1 , 0xE0 , 0x27 , 0x00 , 0xF9 , 0xE8 , 0x27 , 0x40 , 0xF9 , 0x08 , 0x01 , 0x40 , 0x39 , 0xE9 , 0x27 , 0x40 , 0xF9 , 0x29 , 0x15 , 0x40 , 0x39 , 0x08 , 0x01 , 0x09 , 0x4A , 0xE9 , 0x27 , 0x40 , 0xF9 , 0x29 , 0x09 , 0x40 , 0x39 , 0x08 , 0x15 , 0x09 , 0x4A , 0xE9 , 0x27 , 0x40 , 0xF9 , 0x29 , 0x25 , 0x40 , 0x39 , 0x08 , 0x05 , 0x09 , 0x4A , 0xE9 , 0x27 , 0x40 , 0xF9 , 0x29 , 0x29 , 0x40 , 0x39 , 0x09 , 0x05 , 0x09 , 0x4A , ... 0xE8 , 0x03 , 0x00 , 0xB9 , 0xF2 , 0xFF , 0xFF , 0x17 , 0xFF , 0x43 , 0x01 , 0x91 ] f = open (r 'bin' , 'wb+' ) f.write(bytes(code)) |
ida加载bin
后第一部分如下,逻辑方程,可用z3约束求解。
第二部分是个xxtea
,delta为 -0x21524111(0xdeadbeef)
综上第一部分加密为aes_ecb,第二部分为逻辑方程+xxtea,接下来找到比对密文即可,根据trace可知真正比对密文为下面32字节。
解密如下
part1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from Crypto.Cipher.AES import * def getb(a: list ): res = b'' for i in a: res + = int .to_bytes(i, 8 , 'little' ) return res enc = [ 0x1624B3C3E0E4FEAA , 0xA0CAE19E13F75B4E , 0x4D26D4BBFB732728 , 0x26BAFC68DA122E3A ] c1 = getb(enc[: 2 ]) c2 = getb(enc[ 2 :]) #flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} k = [ 0x0B , 0x3A , 0xBA , 0x39 , 0xA2 , 0x64 , 0x27 , 0x1C , 0x36 , 0x31 , 0x98 , 0x80 , 0x9E , 0x77 , 0x9E , 0xEB ] aes = new(bytes(k),MODE_ECB) t = aes.decrypt(c1) print (t) ##2e64949cd16c4449 |
part2
解xxtea
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 | #include<iostream> #define ut32 unsigned int #define delta 0xdeadbeef using namespace std; void XXTea_Encrypt(ut32 * src, ut32 n, ut32 * key); void XXTea_Decrypt(ut32 * enc, ut32 n, ut32 * key); void output(ut32 * m, ut32 len ); void XXTea_Encrypt(ut32 * src, ut32 n, ut32 * key) { ut32 y, z, sum = 0 ; ut32 e, rounds; int p; / / 定义为无符号时 p - 1 > = 0 这个判断恒成立 rounds = 6 + 52 / n; do { z = src[n - 1 ]; sum + = delta; e = ( sum >> 2 ) & 3 ; for (p = 0 ; p < n - 1 ; p + + ) { y = src[p + 1 ]; src[p] + = (((z >> 5 ^ y << 2 ) + (y >> 3 ^ z << 4 )) ^ (( sum ^ y) + (key[(p & 3 ) ^ e] ^ z))); z = src[p]; } y = src[ 0 ]; src[n - 1 ] + = (((z >> 5 ^ y << 2 ) + (y >> 3 ^ z << 4 )) ^ (( sum ^ y) + (key[(p & 3 ) ^ e] ^ z))); } while ( - - rounds); } void XXTea_Decrypt(ut32 * enc, ut32 n, ut32 * key) { ut32 y, z, sum ; ut32 e, rounds; int p; rounds = 6 + 52 / n; sum = delta * rounds; do { e = ( sum >> 2 ) & 3 ; for (p = n - 1 ; p > 0 ; p - - ) { y = enc[(p + 1 ) % n]; z = enc[(p - 1 )]; enc[p] - = (((z >> 5 ^ y << 2 ) + (y >> 3 ^ z << 4 )) ^ (( sum ^ y) + (key[(p & 3 ) ^ e] ^ z))); } y = enc[ 1 ]; z = enc[n - 1 ]; enc[ 0 ] - = (((z >> 5 ^ y << 2 ) + (y >> 3 ^ z << 4 )) ^ (( sum ^ y) + (key[( 0 & 3 ) ^ e] ^ z))); sum - = delta; } while ( - - rounds); } void output(ut32 * m, ut32 len ) { for ( int i = 0 ; i < len ; i + + ) printf( "0x%08x " , m[i]); printf( "\n" ); } int main() { ut32 m[ 4 ] = { 0xFB732728 , 0x4D26D4BB , 0xDA122E3A , 0x26BAFC68 }; ut32 k[ 4 ] = { 12 , 34 , 56 , 78 }; XXTea_Decrypt(m, 4 , k); for ( int i = 0 ; i < 16 ; i + + ) { printf( "%d," , * ((unsigned char * )m + i)); } return 0 ; } |
z3约束求解
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 | from z3 import * s = Solver() enc = [ 53 , 0 , 27 , 154 , 177 , 235 , 146 , 141 , 130 , 127 , 222 , 7 , 180 , 208 , 151 , 164 ] m = [BitVec( 'm%d' % i, 8 ) for i in range ( 16 )] s.add(m[ 0 ] ^ m[ 5 ] ^ ( 32 * m[ 2 ]) ^ ( 2 * m[ 9 ]) ^ ( 2 * m[ 10 ]) = = enc[ 0 ] ) s.add(m[ 1 ] ^ (m[ 13 ] >> 2 ) ^ (m[ 12 ] >> 2 ) ^ m[ 15 ] ^ (m[ 4 ] >> 6 ) = = enc[ 1 ] ) s.add(m[ 2 ] ^ (m[ 1 ] << 7 ) ^ (m[ 15 ] >> 6 ) ^ ( 8 * m[ 14 ]) ^ (m[ 4 ] >> 1 ) = = enc[ 2 ] ) s.add(m[ 3 ] ^ ( 2 * m[ 10 ]) ^ (m[ 14 ] >> 4 ) ^ (m[ 6 ] >> 4 ) ^ ( 32 * m[ 13 ]) = = enc[ 3 ] ) s.add(m[ 4 ] ^ ( 4 * m[ 3 ]) ^ m[ 10 ] ^ ( 2 * m[ 0 ]) ^ (m[ 1 ] >> 2 ) = = enc[ 4 ] ) s.add(m[ 5 ] ^ (m[ 1 ] >> 3 ) ^ (m[ 13 ] << 7 ) ^ (m[ 2 ] >> 7 ) ^ ( 4 * m[ 8 ]) = = enc[ 5 ] ) s.add(m[ 6 ] ^ (m[ 8 ] >> 7 ) ^ ( 4 * m[ 5 ]) ^ ( 16 * m[ 3 ]) ^ (m[ 14 ] >> 3 ) = = enc[ 6 ] ) s.add(m[ 7 ] ^ (m[ 11 ] >> 6 ) ^ (m[ 2 ] >> 5 ) ^ (m[ 3 ] << 6 ) ^ ( 2 * m[ 1 ]) = = enc[ 7 ] ) s.add(m[ 8 ] ^ (m[ 11 ] << 7 ) ^ (m[ 5 ] >> 6 ) ^ ( 2 * m[ 4 ]) ^ ( 16 * m[ 6 ]) = = enc[ 8 ] ) s.add(m[ 9 ] ^ ( 8 * m[ 15 ]) ^ (m[ 4 ] >> 3 ) ^ ( 32 * m[ 12 ]) ^ m[ 2 ] = = enc[ 9 ] ) s.add(m[ 10 ] ^ ( m[ 0 ] >> 2 ) ^ ( 2 * m[ 9 ]) ^ (m[ 5 ] << 7 ) ^ (m[ 11 ] >> 7 ) = = enc[ 10 ] ) s.add(m[ 11 ] ^ m[ 5 ] ^ (m[ 10 ] >> 4 ) ^ (m[ 6 ] >> 6 ) ^ (m[ 3 ] >> 6 ) = = enc[ 11 ] ) s.add(m[ 12 ] ^ (m[ 4 ] << 6 ) ^ (m[ 2 ] >> 1 ) ^ (m[ 15 ] >> 1 ) ^ (m[ 11 ] << 7 ) = = enc[ 12 ] ) s.add(m[ 13 ] ^ (m[ 6 ] >> 3 ) ^ (m[ 9 ] >> 7 ) ^ ( 32 * m[ 1 ]) ^ (m[ 11 ] >> 7 ) = = enc[ 13 ] ) s.add(m[ 14 ] ^ (m[ 7 ] << 7 ) ^ ( 16 * m[ 9 ]) ^ (m[ 8 ] >> 1 ) ^ ( 16 * m[ 2 ]) = = enc[ 14 ] ) s.add(m[ 15 ] ^ ( m[ 0 ] >> 1 ) ^ (m[ 13 ] >> 6 ) ^ ( 4 * m[ 7 ]) ^ (m[ 11 ] >> 5 ) = = enc[ 15 ] ) print (s.check()) ans = s.model() for i in range ( 16 ): print ( chr (ans[m[i]].as_long()),end = '') #87320a0cc24e6667 |
最终提交uuid格式
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。