C/C++典型漏洞產生原理與Demo
轉載:
https://blog.csdn.net/u010651541/article/details/76165216
本篇主要是自己學習筆記,快速讀了下泉哥的《漏洞戰爭》的讀書筆記。這裡只涉及漏洞產生原理,即漏洞是怎麼寫出來。至於怎麼分析0Day,怎麼寫程式碼執行的exp,後續將做深入研究。
C/C++的程式碼執行漏洞,很多時候是劫持程式的控制流。具體來說:對於C程式,一般是控制函式的返回地址,讓程式跳轉到我們指定的地方執行。對於C++程式,除了覆蓋函式返回地址外,還可以覆蓋虛擬函式表,在呼叫虛擬函式的時候,程式將到指定記憶體處執行。
一、棧溢位漏洞
棧溢位漏洞原理十分簡單,只需要瞭解C語言函式呼叫時程式的記憶體結構即可。一句話總結,可控的引數覆蓋了函式的返回地址。
#include<stdio.h>
#include<string.h>
void vulfunc(char* str){
char src[10];
strcpy(src,str);
}
int main(){
char* str="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
vulfunc(str);
return;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
執行程式,程式將crash,並觸發Segmentation fault錯誤。
二、堆溢位漏洞
堆溢位漏洞和棧溢位漏洞原理類似,但是比棧溢位複雜得多,後面單獨介紹原理。
#include<stdlib.h>
#include<string.h>
int main(int argc,char* argv[]){
char* first,second;
first = malloc(100);
second = malloc(12);
if(argc>1){
strcpy(first,argv[1]);
}
free(fisrt);
free(second);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
同樣程式執行時,觸發Segmentation fault錯誤而crash掉。
三、整數溢位
C/C++非記憶體安全行語言,當輸入的整數超出整數的取值範圍時,編譯器並不會報出錯誤,但是程式執行時,可能造成嚴重的安全後果。不過最終導致程式碼執行,還是歸因到棧溢位或者堆溢位。
//棧的整數溢位
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv){
int i;
char buf[8];
unsigned short int size;
char overflow[65550];
memset(overflow,65,sizeof(overflow));
printf("please input size:\n");
scanf("%d",&i);
size =i;
printf("size:%d",size);
printf("i:%d",i);
if(size > 8){
return -1;
}
//stack overflow
memcpy(buf,overflow,i);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上面,size型別為unsigned int,最大取值為65535,當超過這個值時,截斷導致越過size檢查,然後,在memcpy()函式函式中造成棧溢位,程式crash,可能導致程式碼執行。堆上的整數溢位同理,只是溢位物件是堆資料,導致覆蓋的時候後面的堆結構。
四、格式化字串漏洞
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[]){
char buff[1024];
strncpy(buff,argv[1],sizeof(buff)-1);
printf(buff);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
當輸入引數為mmm%s或者ttt%x時,程式將產生意向不到的結果。printf()函式的基本形式為:
printf("格式化字串","引數列表");
- 1
這樣,當輸入ttt%s時,%s不會被當做資料,而是被當成了格式化字串處理,程式會讀取棧上argv[1]後面一個棧上的資料,造成了記憶體資料的洩露。當輸入中包含很多這樣的字元時,將可以遍歷棧上的資料。
五、UAF漏洞
#include<stdio.h>
#define size 32;
int main(int argc,char** argv){
char *buf1;
char *buf2;
buf1=(char*) malloc(size);
printf("buf1:0x%p\n",buf1);
free(buf1);
//buf1=null;
buf = (char*)malloc(size);
printf("buf2:0x%p\n",buf2);
memset(buf2,0,size);
printf("buf2:%d\n",*buf2);
printf("After free\n");
strncpy(buf1,"hack",5);
printf("buf2:%s\n\n",buf2);
free(buf2);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
理解這個漏洞的關鍵是系統對於記憶體的管理,(後面會深入分析)程式所能擁有的棧記憶體空間總是有限的,很多需要堆記憶體,對記憶體虛擬的記憶體空間中,堆記憶體是向上增長的。所以,上面buf1在被free之後,記憶體管理在再次分配buf2的時候,會認為buf1地址處的記憶體已經沒有用,會分配給buf2,即buf2地址指向了之前buf1地址處。關鍵是,沒有做buf1=NULL的操作,導致buf1成為”野指標”,依然有效。這樣,後續如果能夠操作到buf1的指標,就可以長生意想不到的結構。如果buf1處為執行指令,則導致了CE。
class CTest{
int m=1;
public:
virtual void vFunc1();
virtual void vFunc2();
int getM(){
return m;
}
}
void main(){
CTest test; // 例項化;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在C++程式碼中,UAF導致CE更加普遍,以為我們常常需要通過new來建立一個物件。如果後面再free()了對應的物件指標後(體現free),沒有對指標置NULL。那麼攻擊者可能通過”佔坑式”來讓一個指標指向物件的地址,然後利用此指標修改物件的虛擬函式表。此時,如果物件再次被使用,尤其是虛擬函式被呼叫時(體現use after),將導致CE。
六、Double Freee
#include<stdio.h>
int main(int argc,char **argv){
void * p1,p2,p3;
p1=malloc(100);
printf("malloc:%p\n",p1);
p2=malloc(100);
printf("malloc:%p\n",p2);
p3=malloc(100);
printf("malloc:%p\n",p3);
pritf("free p1\n");
free(p1);
pritf("free p3\n");
free(p3);
pritf("free p2\n");
free(p2);
printf("Double free\n");
free(p2);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
理解這個漏洞並不那麼容易,如何導致程式碼執行也會後面單獨來研究,這裡只吃別人吃剩的骨頭,記錄下結論,後面一定要自己研究!!!!!程式釋放了p1,p3之後,在釋放p2時,會發生堆記憶體的合併動作,將改變原有的堆頭資訊及前後向指標。再次釋放時,free動作其實應該是引用了之前的地址,所以導致了崩潰。這裡,後面研究時,應該要研究系統對堆記憶體的回收過程。按照泉哥的結論,這根本上是個UAF漏洞,UAF漏洞是比較容易理解的,所以理解記憶體回收應該是關鍵。
七、陣列越界
陣列越界通常包括讀越界和寫越界,寫越界會造成溢位漏洞。
#include<"stdio.h">
int main(){
int index;
int array[3]={111,222,333};
printf("please input index:\n");
scanf("%d",&index);
printf("array[%d]=%d\n",index,array[index]);
//寫越界,造成溢位;
a[index]=233;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
上面在讀取時會造成陣列讀的越界,賦值時造成(棧)溢位。
以上總結了常見的漏洞型別,其中還有很多知識點需要單獨補充:
- 堆溢位原理分析;
- linux對資料分配和回收的管理;
- double free漏洞深入分析;
初次之外,後續要研究的包括:ROP,堆噴,地址隨機化繞過,這些屬於漏洞利用的知識。