经过激烈角逐,第二届 "长城杯" 信息安全铁人三项赛(防护赛)总决赛圆满落幕!作为行业参与者,我们禾信智安的奉天安全团队在赛后第一时间整理并公布逆向题解题思路!
Time_Capsule
这个ELF文件后缀被改成了.zip,先删除后缀然后使用ExePeinfo查壳发现存在UPX壳
尝试用UPX脱壳工具脱壳发现提示没有检测到有UPX壳,这是因为区段名被修改了
我们可以看到原本的UPX被替换成了QQQ,我们将所有的QQQ替换成UPX
可以发现此时已经脱壳成功了
放进IDA里分析一下由于程序没有符号我大致分析了一下代码恢复了一些函数符号如下,首先进行反调试如果检测到调试直接退出否则提示用户输入并且判断长度是否为24以及flag开头是不是flag{,最后我们的输入和32、37循环xor后进行比较
EXP
enc = [ 0x46, 0x49, 0x41, 0x42, 0x5B, 0x40, 0x4E, 0x4F, 0x4F, 0x5C, 0x7F, 0x57, 0x13, 0x53, 0x13, 0x57, 0x53, 0x40, 0x7F, 0x17, 0x10, 0x11, 0x19, 0x58]key = [32,37]for i in range(len(enc)): enc[i] ^= key[i % 2]print(bytes(enc).decode())
icrypt
描述:安全研究员Alice的电脑被植入了一个神秘的ELF加密程序,她发现所有文件都被该程序加密成.icrypt格式且无法打开。程序运行后会立即自毁,所幸她在内存中捕获了加密后的文件flag.jpg.icrypt和加密程序icrypt的副本。已知该程序采用分组加密,你能逆向加密逻辑,从flag.jpg.icrypt中还原出被隐藏的图片吗?
题目没有符号大致分析一下首先这里是判断命令行参数个数是否为2,如果不为2就提示用户输入要加密的路径
这一段首先路径进行拷贝到v9中,然后判断这个路径是一个文件还是目录,sub_407FB4函数里面是加密的KEY和IV存放到了v7数组中,最后调用sub_407BD2函数进行加密
整个红框里面的函数的作用就是读取文件内容到v3数组中,然后将内容和IV、key传入进行加密
这个加密函数会判断是128、192还是256并且还会填充为16的倍数,很明显的AES-CBC模式的加密
继续往下分析直到sub_4074FA函数加密后才对AES-CBC加密后的结果进一步操作了
通过比对密文的前后差异发现就是对AES加密后的结果的前八字节倒叙了
EXP
from Crypto.Cipher import AES import binascii with open(f"C:UsersZhuanZDesktopicryptflag.jpg.icrypt","rb") as f: data = f.read() enc = b""key = bytes([0x7C, 0x32, 0x41, 0x45, 0x23, 0x26, 0x58, 0x3B, 0x48, 0x22, 0x36, 0x29, 0x26, 0x22, 0x75, 0x73]) iv = bytes([0x37, 0x7A, 0x33, 0x48, 0x2F, 0x75, 0x3B, 0x5D, 0x5D, 0x76, 0x72, 0x6B, 0x65, 0x63, 0x24, 0x3F]) for i in range(0,len(data),16): line = data[i:i+16] enc += line[:8][::-1] + line[8:] a = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) res = a.decrypt(enc) with open("1.jpg","wb") as f1: f1.write(res)
vm_challenge
一个C++实现的简单的VM,首先提示用户输入flag,再将一些输入存储再V21数组
创建一个int类型的vector后将v21数组里面的内容拷贝进去后,循环将opcode以及操作数push进一个vector中
调用run实现虚拟机加密输入
通过调试整个加密流程为读取输入的每一个字节和之前的操作数66进行异或后再和之前v21数组里面的值进行比较
EXP
v21 = [0] * 18 v21[0] = 0x24 v21[1] = 0x2E v21[0] = 0x24 v21[1] = 0x2E v21[2] = 35 v21[3] = 37 v21[4] = 57 v21[5] = 52 v21[6] = 47 v21[7] = 29 v21[8] = 48 v21[9] = 39 v21[10] = 52 v21[11] = 39 v21[12] = 48 v21[13] = 49 v21[14] = 43 v21[15] = 44 v21[16] = 37 v21[17] = 63 for i in range(len(v21)): print(chr(v21[i] ^ 0x42),end="")
CrackMe
描述 :这程序怎么不对劲,沙箱机制(seccomp)造成的扰动,找到真正需要逆向的地方
主程序是一个DES加密,但是发现解密不出查看题目描述发现这里原来不是真正需要逆向的地方
查看init发现有三个函数
第一个函数没用,第二个函数进行了SMC解密
第三个函数是禁止所有未允许的系统调用,允许了一些程序所要使用的系统调用函数如read write mmap等,并且进行了反调试。
使用IDA Python手动解密一下SMC
得到真正的主逻辑,提示用户输入后比较输入长度是否为32
调用AES加密后进行比较
通过分析代码可以发现这是一个白盒AES加密
根据程序修改白盒AES的源码通过调试提取出TyiBoxes和TBoxes得到缺陷数据构造出来的密文
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <string.h>#include <stdlib.h>#include <time.h>#define ROTL8(x, shift) ((u8) ((x) << (shift)) | ((x) >> (8 - (shift))))typedef unsigned char u8;typedef unsigned int u32;u8 DFA = 0;u8 xorTable[9][96][16][16];u32 TyiBoxes[9][16][256];unsigned char TBoxes[16][256];unsigned int mixBijOut[9][16][256];void GetxorTable() {for (int i = 0; i < 9; i++) {for (int j = 0; j < 96; j++) {for (int x = 0; x < 16; x++) { //2的4次方=16for (int y = 0; y < 16; y++) { xorTable[i][j][x][y] = x ^ y; } } } }}void shiftRows(u8 state[16]) { u8 out[16]; int shiftTab[16] = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 };for (int i = 0; i < 16; i++) { out[i] = state[shiftTab[i]]; } memcpy(state, out, sizeof(out));}void AES_128_encrypt(unsigned char ciphertext[16], unsigned char plaintext[16], int white) { u8 input[16] = { 0 }; u8 output[16] = { 0 }; memcpy(input, plaintext, 16); u32 a, b, c, d, aa, bb, cc, dd;for (int i = 0; i < 9; i++) { // DFA Attackif (white != -1 && i == 8) { input[white] = 11; } shiftRows(input);for (int j = 0; j < 4; j++) { a = TyiBoxes[i][4 * j + 0][input[4 * j + 0]]; b = TyiBoxes[i][4 * j + 1][input[4 * j + 1]]; c = TyiBoxes[i][4 * j + 2][input[4 * j + 2]]; d = TyiBoxes[i][4 * j + 3][input[4 * j + 3]]; aa = xorTable[i][24 * j + 0][(a >> 28) & 0xf][(b >> 28) & 0xf]; bb = xorTable[i][24 * j + 1][(c >> 28) & 0xf][(d >> 28) & 0xf]; cc = xorTable[i][24 * j + 2][(a >> 24) & 0xf][(b >> 24) & 0xf]; dd = xorTable[i][24 * j + 3][(c >> 24) & 0xf][(d >> 24) & 0xf]; input[4 * j + 0] = (xorTable[i][24 * j + 4][aa][bb] << 4) | xorTable[i][24 * j + 5][cc][dd]; aa = xorTable[i][24 * j + 6][(a >> 20) & 0xf][(b >> 20) & 0xf]; bb = xorTable[i][24 * j + 7][(c >> 20) & 0xf][(d >> 20) & 0xf]; cc = xorTable[i][24 * j + 8][(a >> 16) & 0xf][(b >> 16) & 0xf]; dd = xorTable[i][24 * j + 9][(c >> 16) & 0xf][(d >> 16) & 0xf]; input[4 * j + 1] = (xorTable[i][24 * j + 10][aa][bb] << 4) | xorTable[i][24 * j + 11][cc][dd]; aa = xorTable[i][24 * j + 12][(a >> 12) & 0xf][(b >> 12) & 0xf]; bb = xorTable[i][24 * j + 13][(c >> 12) & 0xf][(d >> 12) & 0xf]; cc = xorTable[i][24 * j + 14][(a >> 8) & 0xf][(b >> 8) & 0xf]; dd = xorTable[i][24 * j + 15][(c >> 8) & 0xf][(d >> 8) & 0xf]; input[4 * j + 2] = (xorTable[i][24 * j + 16][aa][bb] << 4) | xorTable[i][24 * j + 17][cc][dd]; aa = xorTable[i][24 * j + 18][(a >> 4) & 0xf][(b >> 4) & 0xf]; bb = xorTable[i][24 * j + 19][(c >> 4) & 0xf][(d >> 4) & 0xf]; cc = xorTable[i][24 * j + 20][(a >> 0) & 0xf][(b >> 0) & 0xf]; dd = xorTable[i][24 * j + 21][(c >> 0) & 0xf][(d >> 0) & 0xf]; input[4 * j + 3] = (xorTable[i][24 * j + 22][aa][bb] << 4) | xorTable[i][24 * j + 23][cc][dd]; } } shiftRows(input);for (int j = 0; j < 16; j++) { input[j] = TBoxes[j][input[j]]; }for (int i = 0; i < 16; i++) output[i] = input[i]; memcpy(ciphertext, output, 16);}void wpadBoxes() { const char* PATH1 = "TBoxes"; FILE* fp = fopen(PATH1, "rb"); fread(TyiBoxes, sizeof(TyiBoxes), 1, fp); fclose(fp); const char* PATH2 = "mixBijOut"; fp = fopen(PATH2, "rb"); fread(TBoxes, sizeof(TBoxes), 1, fp); fclose(fp); const char* PATH3 = "TBoxes"; fp = fopen(PATH3, "rb"); fread(mixBijOut, sizeof(mixBijOut), 1, fp); fclose(fp);}int main() { srand(time(0)); GetxorTable(); wpadBoxes();for (int i = -1; i < 16; ++i) { DFA = i != 0; char flag[16] = { 0 }; memset(flag, 0x11, 16); unsigned char ciphertext[16] = { 0 }; AES_128_encrypt(ciphertext, (unsigned char *)flag, i);for (int i = 0; i < 16; ++i) {printf("%02X", ciphertext[i]); } puts(""); }return 0;}
使用缺陷数据构造的密文求出k10,从而得出主密钥
import phoenixAESdata = """E976EFF0A00EBAA7B531632909A3D25F3F76EFF0A00EBA04B531CA290908D25FE976EFC8A00E2CA7B5C86329DAA3D25FE97676F0A023BAA7F331632909A3D28EE93FEFF0B80EBAA7B53163E909A3965FE9B3EFF0460EBAA7B531630B09A3F45F0A76EFF0A00EBA27B531F42909FAD25FE976EF13A00EB8A7B5606329DEA3D25FE976BBF0A077BAA75A31632909A3D2C0E97696F0A080BAA76431632909A3D265E9B8EFF0140EBAA7B531635409A36E5FD276EFF0A00EBA84B531C02909DED25FE976EFABA00EAEA7B5566329DFA3D25FE976EF9CA00E54A7B5AA632944A3D25FE976A3F0A02BBAA78331632909A3D215E930EFF07A0EBAA7B531631809A34F5F7576EFF0A00EBA3AB531B0290984D25F"""with open('crackfile', 'w') as fp: fp.write(data)phoenixAES.crack_file('crackfile', [], True, False, verbose=0)from aeskeyschedule import *base_key = reverse_key_schedule(bytes.fromhex('195DCE39EC1CA57F35584891CB6378DE'),10)print(base_key.hex())
得出密钥为16个x01,正常AES求解
enc = b""enc_list = [0xCB8B76E4F2BD9924,0x8032310BC994A2E2,0x333172B6DF852A61,0x2F3A3E003C36088A]key =b"x01" * 16for i in range(4): enc += enc_list[i].to_bytes(8,"little")a = AES.new(key=key,mode=AES.MODE_ECB)print(a.decrypt(enc))
原文始发于微信公众号(奉天安全团队):第二届“长城杯”信息安全铁人三项赛(防护赛)总决赛-逆向题解
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论