xp0int Posted on Aug 27 2019 ``` X-NUCA 2019 线上赛 damnV 分值:323 已解答:12 Ahh.... So boring. ``` [下载链接](https://pan.baidu.com/s/14UdP7JNSbgiLG77NeLnYIQ)(提取码: 2um5) [备用下载](https://share.weiyun.com/5qaObW2) 其实这是一道很简单的逆向题,只是用了 KVM 虚拟机。本来可以更快做出来的,可惜时间都花在逆向虚拟机的运行参数上了... 程序要求输入233个80字节的 payload。每个 payload 要经过18次 check(check 代码运行在 KVM 虚拟机上)。每轮 check 成功后,打乱 check 的执行顺序,再重新 check 下一个 payload。 全局变量`check_list`数组记录了每次 check 的种类`id`、需要读入的 payload 长度`payload_size`和 check 时使用的数据`data`。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d65352aab64416887003470) ``` struct Check { __int32 id; __int32 payload_size; __int32 data[16]; }; ``` 一共有四种 check,代码开头位于`0x203530`。 第一种 check 是计算`payload`的 crc32 哈希值,检查是否等于`data[0]`,可以直接爆破: ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653757ab644168870034d7) 第二种 check 的算法看不懂,不过可以爆破: ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653766ab6441669100358b) 第三种 check 是自定义字母表的 base64: ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653773ab6441669100358e) 第四种 check 是简单的异或: ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653781ab644168870034e0) 程序先创建了一个 KVM 虚拟机。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d6538a5ab644166910035c2) ![图片标题](https://leanote.com/api/file/getImage?fileId=5d6538adab644166910035c4) ![图片标题](https://leanote.com/api/file/getImage?fileId=5d6538c5ab64416887003519) 具体的创建流程可以参考[这篇文章](https://david942j.blogspot.com/2018/10/note-learning-kvm-implement-your-own.html)。 创建后,在虚拟机内存地址`0`处下了一个断点。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d6538fcab64416887003522) 开始 check 前,将虚拟机`rip`设置为`0`,然后将 check 代码复制到虚拟机内存。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653954ab64416887003531) 从`check_list`载入 check 的信息。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653971ab644166910035e7) 运行虚拟机,并读入`payload_size`字节的`payload`。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653992ab644166910035ef) 虚拟机触发断点,返回程序。程序按照`id`将`rip`设置为 check 的入口地址,并将`payload`和`data`复制到虚拟机内存,然后重新运行虚拟机。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d6539efab6441688700354d) 虚拟机停机后,程序检查返回值。如果为`1`,说明 check 成功。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653a32ab6441669100360f) 每经过18次 check 后,从头到尾历遍`check_list`数组上的元素,用`rand`函数生成的随机数选择另一个元素,与其交换位置,从而改变 check 的执行顺序。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653aa8ab6441669100362b) 完成 233 轮 check 后,输出 flag。 ![图片标题](https://leanote.com/api/file/getImage?fileId=5d653ad9ab64416691003632) ``` from pwn import * from binascii import crc32 from ctypes import cdll from string import maketrans LIBC = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc-2.23.so") LIBC.srand(0x2a) context(log_level="debug") p = process("./damnV") # IDAPython # data = get_bytes(0x203020, 0x510) # open('check', 'wb').write(data) # check = open('check', 'rb').read() check_list = [] for i in range(0, len(check), 0x48): id = u32(check[i:i+4]) size = u32(check[i+4:i+8]) data = [u32(check[i+j:i+j+4]) for j in range(8, 0x48, 4)] check_list.append([id, size, data]) def check_0(data): for i in range(0x10000): I = p16(i) if crc32(I) & 0xFFFFFFFF == data: return I def check_1(data): payload = '' for value in data: i = 0 b = e = j = 0 a = 1 while True: c = (a + b) & 0xFFFFFFFF d = ((((j << 32) | a) + ((e << 32) | b)) >> 32) & 0xFFFFFFFF b = a e = j i += 1 if c == value: payload += p8(i) break a = c j = d return payload def check_2(data): my_base64chars = "zy0xw1vu2ts3rq4po5nm6lk7ji8hg9fedcba,.ZYXWVUTSRQPONMLKJIHGFEDCBA" std_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" s = ''.join(map(chr, data)) s += (4 - (len(s) % 4)) * '=' s = s.translate(maketrans(my_base64chars, std_base64chars)) return b64d(s) def check_3(data): words = "Bird, Bird, Bird, Bird is the word." payload = '' for i in range(len(data)): payload += chr(data[i] ^ ord(words[i])) return payload def start_check(): payload = '' for id, size, data in check_list: if id == 0: payload += check_0(data[0]) elif id == 1: payload += check_1(data[:size]) elif id == 2: data_ = [] for j in data: if not j : break data_.append(j) payload += check_2(data_) elif id == 3: payload += check_3(data[:size]) return payload def reset_check(): for i in range(18): R = LIBC.rand() I = ((100 * R) & 0xFFFFFFFF) % 18 check_list[I], check_list[i] = check_list[i], check_list[I] if __name__ == '__main__': for _ in range(233): p.recvuntil(":") p.sendline(start_check()) reset_check() # flag{The_Auth0r_was_stark_mad_and_keep_listening_surfin_bird_for_comfort} p.interactive() ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [PWN] childjs - xfiles [PWN] ls - cpt.shao、MF
没有帐号? 立即注册