淺析格式化串漏洞

                       作者:isno ([email protected])


                -----------------目錄-------------------

                  一.前言

                  二.基礎知識簡介

                  三.格式化串漏洞原理

                    (1)參數個數不固定造成訪問越界數據

                    (2)利用%n格式符寫入跳轉地址

                    (3)利用附加格式符控制跳轉地址的值

                    (4)總結

                  四.對wu-ftp 6.0格式化串漏洞的分析

                    (1)問題出現在哪裏

                    (2)wu-ftp漏洞的利用

                  五.後記

                ----------------------------------------

一.前言

    最近許多軟件被發現存在格式化串漏洞,其中最著名的是wu-ftp6.0和rpc.statd,由於
相當多的網站缺省安裝了這兩種軟件,而且網上針對這兩個漏洞的攻擊程序很多很好用,所
以由這兩個漏洞而被攻破的網站非常之多。因此非常有必要認真研究一下格式化串漏洞,但
網上介紹格式化串漏洞的中文文章卻特別少,就我知道的只有一篇warning3寫的和另一篇
xuzq翻譯的文章,我又參考了幾篇英文文章,費了半天工夫看的頭疼了才搞明白這種漏洞的
機理。

    由於那幾篇文章寫的較爲深奧,像我這樣的普通初學者看起來很費勁,我想就我的理解
寫一篇淺顯一點的文章,使其他像我一樣的菜鳥免受頭疼之苦,同時也把這篇文章作爲備忘
材料,等我以後忘了再回過頭來看看:-)由於本人水平有限,謬誤之處再所難免,歡迎多多
指教。

二.基礎知識簡介

    在瞭解格式化串漏洞之前有必要複習一下關於堆棧的基礎知識,網上介紹緩衝區溢出的
文章很多,其中大多都介紹了堆棧的知識,讀者可以自行參考那些文章,我在這裏只是簡單
的介紹一下。

    一個程序的動態數據通過一塊叫做堆棧的區域來存放。堆棧處於內存的高端,它有個特
性:後進先出。當程序中調用子函數時,計算機首先把參數依次壓入堆棧,然後把指令寄存
器(EIP)中的內容做爲返回地址(RET)壓入堆棧,第三個壓入堆棧的是基址寄存器(EBP),然後
把當前的棧頂指針(ESP)拷貝到EBP,做爲新的基地址。最後把ESP減去一定的數值,用來爲本
地變量留出一定空間。
    
    普通的緩衝區溢出就是利用了堆棧生長方向和數據存儲方向相反的特點,用後存入的數
據覆蓋先前壓棧的數據,一般是覆蓋返回地址,從而改變程序的流程,這樣子函數返回時就跳到了
黑客指定的地址,就可以按照黑客意願做任何事情了。
    
    格式化串漏洞和普通的緩衝溢出有相似之處,但又有所不同,它們都是利用了程序員的
疏忽大意來改變程序運行的正常流程。下面詳細介紹格式化串漏洞的原理,最後對wu-ftp6.0
格式化串漏洞進行一下分析。

三.格式化串漏洞原理
    
    所謂格式化串,就是在*printf()系列函數中按照一定的格式對數據進行輸出,可以輸出
到標準輸出,即printf(),也可以輸出到文件句柄,字符串等,對應的函數有fprintf,sprintf,
snprintf,vprintf,vfprintf,vsprintf,vsnprintf等。能被黑客利用的地方也就出在這一系列
的*printf()函數中,可能有人會問:這些函數只是把數據輸出了,怎麼能造成安全隱患呢?
在正常情況下當然不會造成什麼問題,但是*printf()系列函數有三條特殊的性質,這些特殊
性質如果被黑客結合起來利用,就會形成漏洞。

(注:以下測試環境爲RedHat Linux 6.0)

#可以被黑客利用的*printf()系列函數的三個特性:

(1)參數個數不固定造成訪問越界數據

    首先第一個可以被利用的性質是:*printf()系列函數的參數的個數是不固定的。拿printf()
函數舉例來說,如果我們要依次輸出3個整型數據和1個字符串,可以用以下程序:

#include 
int main(void)
{
int i=1,j=2,k=3;
char buf[]="test";
printf("%s %d %d %d\n",buf,i,j,k);
return 0;
}

  這是正常的使用方法,程序會輸出:

test 1 2 3

    這個printf()函數共有5個參數,第一個是格式化串"%s %d %d %d\n",第二個是字符串buf的
地址,%s對應buf,其後的三個%d分別對應i,j,k,這樣就把數據輸出了。但是如果我們減少printf()
函數的參數個數,寫成這樣:

printf("%s %d %d %d\n",buf,i,j);

    格式化輸出符號仍然是4個,但對應的數據卻只剩下3個了(buf,i,j)了,那麼情況會怎樣呢?
我們編譯運行一下看看,這個程序輸出:

test 1 2 1953719668

    我們可以清楚的看到,儘管沒有給最後一個%d提供對應的數據,但是它還是輸出了一個10位的
整數1953719668,這個大整數到底是什麼呢?我們再修改源程序,把輸出的語句改爲:

printf("%s %d %d %x\n",buf,i,j);

    即按照16進制輸出最後一個參數,這時輸出的結果就是:

test 1 2 74736574

    也就是說,當沒有給printf()函數的格式化串提供足夠的對應參數時,printf()並沒有報錯,而
是把內存中某個4字節的內容打印了出來,這四個字節的內容是74736574。

    那麼74736574究竟是什麼玩意呢?如果你對ASCII碼熟悉的話應該可以反映過來,字符串在內存
當中是以ASCII碼的形式存儲的,它們有如下對應關係:

十六進制             十進制            字符
    74   ---------->   116  --------->  t
    73   ---------->   115  --------->  s
    65   ---------->   101  --------->  e

    74736574對應的字符串恰好是tset,由於字符串在內存當中是以反序排列的,74736574對應的
實際字符串應該是:test。是不是看起來有點眼熟?翻回前面再看看那個程序,對了,就是我們在程序
中定義的字符串buf[]的內容。這決不是偶然的,回憶一下前面說過的堆棧的工作流程,我們可以想象
到這個程序在堆棧中的情況:

i)  調用main()函數之前首先把返回地址壓棧;
ii) 然後壓入的是EBP,並把ESP拷貝到EBP;
iii)把ESP減去一定的數量,也就是把堆棧擴大,給變量i,j,k,buf留出空間;
iv) 開始調用printf(),把printf()的4個參數j,i,buf和格式串"%s %d %d %x\n"依次壓入堆棧;
v)  壓入printf()的返回地址;
vi) 壓入此時的EBP;
vii)開始執行printf()。

    這時候的堆棧看起來應該是這個樣子的:

棧頂                                                                      棧底
  --------------------------------------------------------------------------
  | EBP | EIP | 格式串| buf地址| i | j |buf內容| \0 | k | j | i | EBP | EIP|
  --------------------------------------------------------------------------

    看到堆棧的實際內容,就不難理解爲什麼會打印出74736574即"test"了,printf()首先找到第
一個參數格式串"%s %d %d %x\n",然後就開始按照對應關係依次打印前面堆棧中內容,%s對應
buf地址,也就打印出了buf[]的內容,第一個%d對應i,第二個%d對應j,%x本來是應該對應k的,可是
由於我們提供給printf()的參數中沒有k,而j前面正好是buf內容,所以就把buf的內容作爲16進制
數輸出了,也就是我們看到的74736574。可以預測,如果提供給printf()的格式串中再多幾個%x的話,
printf()還會繼續打印前面堆棧裏的"\0"(buf的結束符),k,j,i,EBP,EIP等內容。

    說到這裏,已經把產生格式化串漏洞的根源揭露出來了:因爲*printf()系列函數的參數的個數
是不固定的,如果其第一個參數即格式串是由用戶來提供的話,那麼用戶就可以訪問到格式串前面的
堆棧裏的任何內容了。

    之所以會出現格式化串漏洞,就是因爲程序員把printf()的第一個參數即格式串,交給用戶來
提供,如果用戶提供特定數量的%x(或%d,%f,隨你的便啦),就可以訪問到特定地址的堆棧內容。

    有些人會說:"靠!你費了這麼半天勁,就只是爲了打印出了前邊堆棧裏的內容啊?"我們當然不
只是爲了看看堆棧裏的內容,我們是要改變堆棧的內容,改變返回地址,使程序跳去執行我們提供的
代碼,這就需要聯繫上*printf()系列函數的第二個特殊的性質。

(2)利用%n格式符寫入跳轉地址

    到目前爲止我們都只是顯示內存的內容而沒有改變它,但是利用*printf()的一個特殊的格式符
%n,我們就向內存中寫入內容。

    %n是一個在編程中不經常用到的格式符,它的作用是把前面已經打印的長度寫入某個內存地址,
爲了搞清其具體用法和性質,我們看一看下面的例程:

#include 
int main(void)
{
int num;
int i=1,j=2,k=3;
printf("%d%d%d%n\n",i,j,k,&num);
printf("%d\n",num);
return 0;
}

運行顯示:
123
3

    可以看出,%n的作用就是把已經打印出來字符的數量保存到對應的內存地址當中,這裏是num當中。
注意,這裏必須對應一個內存地址,%n把字符數寫入到這個地址的內存。如果把上述語句改成:

printf("%d%d%d%n\n",i,j,k,num);

    這樣就會出現段訪問錯誤。強調這一點很重要,因爲在實際利用某個漏洞時,並不是直接把跳轉
地址寫入函數的返回地址單元,而是寫入一個存放着函數的返回地址的地址當中,即經常說的retloc,
這個存放函數的返回地址的地址通常在我們提供的字符串的前面,這麼說可能有點繞,換種說法,就是
說我們並不直接覆蓋返回地址,而是通過地址來間接的改寫返回地址,這一點經常有人混淆,如果你還沒
理解的話,可以仔細體會一下C語言中指針的用法,它們之間有相似之處。

    好的,到目前爲止我們已經知道可以利用提交格式串來訪問格式串前面堆棧裏的內容,並且利用
%n可以向一個內存單元中的地址去寫入一個值,既然我們可以訪問到我們提交的串,就可以在我們提
交的串當中放上某個函數的返回地址的地址,這樣就可以利用%n來改寫這個返回地址。

    但是但是,%n向內存中寫入的值並不是隨意的,它只能寫入前面打印過的字符數量,而我們需要的
是寫入我們存放shellcode的地址,就象普通溢出做的那樣。這個問題實在是麻煩,可能有人會想:那
就用和跳轉地址的數值相同多的數量的%d放在%n的前面不就行了?這樣做理論上可行,但實際卻不行,
因爲堆棧在內存的高端,堆棧裏面的內存地址也是一個相當大的數,如果我們用一個%d來對應4字節內容
即一個整型的話,首先數量太多就是一個問題,而且每4個字節的內存單元作爲整數打印出來的話,它的
實際長度也是沒法確定的,有的可能打印出一位的'1',有的則可能打印出5位的'45367',這是我們沒法
預料的。

    這時就需要利用到*printf()系列函數的第三個"良好"的性質了。

(3)利用附加格式符控制跳轉地址的值

    *printf()系列函數有個性質是:程序員可以定義打印字符的寬度。學過C語言的人肯定都知道這一點,
就是在格式符的中間加上一個整數,*printf()就會把這個數值作爲輸出寬度,如果輸出的實際大於指定寬
度仍按實際寬度輸出,如果小於指定寬度,則按指定寬度輸出。例如我們可以用下面語句以100個字符的寬
度輸出整數i:

printf("%100d",i);

    而用printf("%.100f",i)的形式則是以100位的小數輸出i。而printf("%.f",i)不是以1位小數輸出,而是
以總共8位的帶小數的數輸出i。如果i等於1的話,輸出應該是1.000000。由於這個"%.f"一次能向前推進8位,所
以它經常在實際攻擊時被放在提交的格式串的中間,用來快速地到達返回地址處。


    我們就可以利用這個特性來用很少的格式符來輸出一個很大的數值到%n,而且這個數值是可以由我們
來指定的。我們所要做的就是作一些計算,把要返回的地址轉化爲整數,放到%n前面的格式串中。例如我們要
把200放入num中,則可以利用如下語句:

printf("%.200d%n",i,&num);

    當這條語句執行完後,num的值就變成200了。如果用跳轉地址的值代替200,那麼%n就可以把跳轉地址寫入
num了。

(4)總結

    好了,到這裏已經把格式化串漏洞的所有理論依據都介紹完了。讓我們再來回顧一下:

    首先,如果程序中的*printf()系列函數中的格式串參數是由用戶來提供的話,我們就可以提交給它一串
%d(或%f,%u等)來訪問堆棧中格式串前面的任意內存單元。

    在我們提交的格式串的後面加上一個%n格式符,我們就可以向堆棧中格式串前面的某個內存單元中寫入
已經打印的字符數量。在實際攻擊時通常是在提交的格式串前面放上存放着某個函數的返回地址的地址,然後
讓%n恰好對應着這個地址值,這樣寫入到數值,就存放到函數返回地址當中了。

    我們通過附加格式符來控制向函數返回地址中寫入的值,一般是利用%n前面的最後一個格式符來控制
這個數值,這通常需要一些計算,計算方法一般是用shellcode的地址減去最後一個格式串前的所有格式串的
打印長度。這樣%n寫入的數值就恰好是shellcode的地址了。

    理論上講的只是理想情況,在實際攻擊某個程序時往往會出現更多的問題。首先要解決的是必須使%n正好
對應存放函數返回地址的地址,否則無法改變返回地址的值。這一點對於一些可重複啓動且能夠返回格式串的
程序來說是比較容易解決的,我們可以先不放入跳轉地址,而是在提交的格式串前填上幾個特殊字符,例如"abcd",
然後在提交的格式串最後用%x代替%n來顯示這個特殊字符串,我們要做的只是不斷增加格式串中間的格式符的個
數,直到程序返回的值恰好是我們提交的特殊字符的ASCII碼爲止。這樣我們就知道用來存放函數返回地址的地
址在什麼位置了,然後我們再把真正的存放函數返回地址的地址放在格式串的前面,用%n作爲格式串的結尾,這
樣就可以把跳轉地址正確的寫進函數返回地址了。

    當然這種方法需要被攻擊的程序可以不斷的啓動而且能夠返回提交的格式串的打印內容,wu-ftp6.0就是
這樣的程序,所以針對wu-ftp6.0的格式串漏洞的攻擊比較容易成功的。而有些程序則不行,例如cfengine,這
個程序被發現存在格式串漏洞已經很久了,但是一直沒有可成功的攻擊程序發佈,主要就是因爲一但向cfengine
發送格式串它就當掉了,所以無法多次猜測。對這種程序必須一次攻擊就必須猜中存放函數返回地址的地址和
跳轉地址,所以在現實的攻擊中往往是不容易成功的。

    另外一個難以解決的問題是,需要把精確的函數返回地址填在格式串開頭,這樣%n才能把跳轉地址寫進
正確的位置了。這個函數的返回地址一般是在寫攻擊程序時測試得到的,這在測試的機器上固然可以成功,但是
在不同機器上這個返回地址的值是根據環境變量和編譯選項的不同而不同的,例如用rpm安裝的wu-ftp和從源
代碼編譯安裝的wu-ftp,它們的返回地址往往就是不同的,需要根據被攻擊主機的實際情況進行調整。這隻有根
據經驗來確定,或者乾脆用暴力法了猜測返回地址,但根據實際測試,即使用暴力法猜測返回地址,成功的概率
也是不大的。

    還有就是跳轉地址的問題,即shellcode的地址。因爲我們要改寫函數的返回地址,使它跳轉去執行shellcode。
所以必須要知道shellcode的地址。這個問題相對容易解決,我們可以像普通溢出做的那樣,在shellcode前面
填上一串NOP,這樣只需要知道一個大概的地址範圍就可以了,只要跳轉到NOP的範圍當中就可以執行shellcode
了。所以一般的攻擊程序裏的跳轉地址往往是不需要調整的,因爲只要在一個大概的地址範圍當中就可以了。

    好了,理論就討論這麼多,下面我們來看看Wu-ftp6.0的格式化串漏洞和利用的方法。

四.對wu-ftp 6.0格式化串漏洞的分析

(1)問題出現在哪裏

    wu-ftp(Washington University ftp server)是一個非常流行的unix/linux系統ftp服務器,它的6.0版本
存在格式化串漏洞。由於在大多數Linux系統中它是默認安裝的,所以相當多的網站都受這個漏洞的影響,針對它
的攻擊也是非常普遍的。

    下面我們看看wu-ftp的源代碼,究竟是哪裏出現了可以被黑客利用的漏洞?

    用戶提交的"site exec"命令是由一個名爲 void site_exec(char *cmd) 的函數來處理的,其中cmd是用戶
提交的命令。在這個函數中有這麼一個語句:

--------------ftpcmd.y文件 第1929行----------------------

lreply(200, cmd);

-----------------------cut here--------------------------

    site_exec()函數把用戶提交的命令交給lreply()函數來處理了,我們再看看lreply()函數的定義:

--------------ftpd.c文件 第5343行------------------------

void lreply(int n, char *fmt,...)
{
    VA_LOCAL_DECL

    if (!dolreplies)
    return;

    VA_START(fmt);

    /* send the reply */
    vreply(USE_REPLY_LONG, n, fmt, ap);

    VA_END;
}

-----------------------cut here--------------------------

    顯然lreply()的第二個參數char *fmt應該是格式串,而在前面的調用中卻把它交由用戶命令來
提供,這就是造成問題的地方。繼而lreply()有把fmt交給vreply()函數來處理,我們再來看看vreply()
的定義:

--------------ftpd.c文件 第5275行------------------------

void vreply(long flags, int n, char *fmt, va_list ap)
{
    char buf[BUFSIZ];

    flags &= USE_REPLY_NOTFMT | USE_REPLY_LONG;

    if (n)
    sprintf(buf, "%03d%c", n, flags & USE_REPLY_LONG ? '-' : ' ');

    /* This is somewhat of a kludge for autospout.  I personally think that
     * autospout should be done differently, but that's not my department. -Kev
     */
    if (flags & USE_REPLY_NOTFMT)
    snprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), "%s", fmt);
    else
    vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap);
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                     !!!注意這一句!!!

    if (debug)
    syslog(LOG_DEBUG, "<--- %s", buf);

    /* Yes, you want the debugging output before the client output; wrapping
     * stuff goes here, you see, and you want to log the cleartext and send
     * the wrapped text to the client.
     */

    printf("%s\r\n", buf);    /* and send it to the client */
#ifdef TRANSFER_COUNT
    byte_count_total += strlen(buf);
    byte_count_out += strlen(buf);
#endif
    fflush(stdout);
}

-----------------------cut here-----------------------------------------------

    由於提交給vreply()的第一個參數(即flags)是USE_REPLY_LONG,所以經過&=操作
之後flags仍然爲USE_REPLY_LONG。這樣(flags & USE_REPLY_NOTFMT)的值就爲0。所以下面的
判斷語句會進入else執行:

if (flags & USE_REPLY_NOTFMT)
    snprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), "%s", fmt);
    else
    vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap);
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                     !!!注意這一句!!!

    注意看要執行的vsnprintf()函數,它把fmt放在了格式串的參數位置上了。而這個fmt
正是由用戶提交的命令cmd。回憶一下前面講過的格式化串漏洞原理:如果*printf()系列
函數的格式串參數是由我們來提交的話,那麼我們就可以用一串格式符來訪問格式串前面
堆棧裏的內容了,如果我們能訪問到自己提交的內容,就可以在這內容的前面放上存放某個
函數返回地址的地址,然後就可以用%n來改寫這個返回地址,使它跳轉去執行我們提供
的shellcode。

(2)wu-ftp漏洞的利用

    所以我們要想利用這個漏洞,需要先登陸上FTP服務器(編程實現,不用我們自己去登陸了),
用匿名用戶(ftp,anonymous)登陸就可以,然後提交一個如下形式的site exec命令:

    site| exe|c aa|retloc|%.f...%.f|%.(ret)d|%n

    注意:其中的'|'符號是爲了使讀者看清楚字符串結構而加上的分隔符,實際提交的串裏沒有'|'。

    這個由我們提交的格式串前面是site exec命令,後跟的"aa"的作用是爲了使retloc以4字節
爲單位對齊,就是我們通常所說的align。

    然後緊跟着的retloc就是我們要寫入的存放函數返回地址的地址,呆會兒我們要用%n來對應
它,就可以把跳轉地址寫入函數返回地址了。一般我們要改寫的函數應該是最近的一個函數,這裏
可以改寫vreply()的返回地址,使它返回時跳去執行我們的shellcode。

    在retloc後面放上一串%.f,前面說過%.f一次顯示8位數字,在這裏它的作用是顯示在我們提
交的命令串之後壓入堆棧的局部變量,使%n能夠正好對應retloc。

    然後緊跟着的%.(ret)d的作用是把打印的字符數量調整爲正好是shellcode地址的值,使得%n
能夠把已打印的字符數量,即shellcode的地址正好寫進vreply()函數的返回地址當中。注意,這裏
的ret並不是shellcode的地址,而應該是[shellcode地址-(%.f個數*8)-16]。其中的16是前面的
"site| exe|c aa|retloc|"的字符數。這樣在%n之前打印的總符數就應該恰好爲shellcode的地址
了。

    最後的%n的作用當然就是爲了把跳轉地址(shellcode地址)寫入vreply()的返回地址,使它返
回時跳轉去執行shellcode。正如在講格式化串漏洞原理時提到過的那樣:%n並不是直接對應着返回
地址進行修改,而是對應着存放返回地址的地址,間接的修改了返回地址。因此雖然vreply()的返回
地址是比格式串後壓入堆棧的,但是我們仍然可以改寫它的內容。

    還有重要的一點忘記說了。shellcode是什麼時候提交的呢?我們可以把shellcode放進用戶的密
碼裏提交給wu-ftpd,由於匿名用戶的密碼是可以隨便指定的,所以這樣做並不影響我們使用匿名登
陸進服務器。此時shellcode應該作爲全局變量被存放在程序的Heap/BSS段裏,而不是存放在堆棧裏。
在本地機器上調試一下可以知道shellcode的大致地址爲0x80756xx,這是在RedHat 6.0上的值,在其
它系統中會有所不同。由於我們可以在shellcode之前放上一堆NOP,所以也沒必要知道shellcode的
精確位置,只要讓程序跳轉到NOP範圍裏就行了。

    需要說明的是,這裏的shellcode必須是帶有突破chroot()功能的,因爲如果用匿名用戶登陸的
話,只能訪問被chroot()保護的目錄,即匿名用戶登陸後的目錄,這樣就沒法綁定/bin/sh了。所以要
在shellcode中先chroot()到根目錄。網上有很多寫得非常好的chroot shellcode,直接拿來用就可
以了。

    以上就是攻擊wu-ftp6.0格式化串漏洞的方法,現在流傳着很多寫得非常好的wu-ftp6.0攻擊程
序,我本來想找其中一個來逐條語句解釋出來的,但解釋了幾句之後發現太TMD麻煩了。而且也沒有必
要解釋攻擊程序了,因爲我已經把攻擊這個漏洞的步驟都在前面解釋過了。

    這裏只把我用過的幾個比較好的攻擊程序介紹給大家:

    (1)攻擊以rpm安裝wuftp6.0的RedHat 6.0, 6.1, 6.2比較有效的程序:
    
    http://go6.163.com/~antiroot/exploit/wu-lnx.c

    (2)攻擊安裝wuftp6.0的FreeBSD和SuSe 6.3, 6.4比較有效的程序:
    
    http://go6.163.com/~antiroot/exploit/wuftpd-god.c

    (3)攻擊安裝wuftp6.0的solaris 2.x比較有效的程序:

    http://go6.163.com/~antiroot/exploit/ftpd.c

    根據我的經驗,以rpm方式安裝wuftp6.0的RedHat 6.0, 6.1, 6.2是最容易攻擊成功的了,可能是
因爲大多數攻擊程序都是在這種系統環境下調試編寫的。而用源代碼編譯安裝的wuftp不容易攻擊成功,
需要攻擊者調整攻擊程序中的某些參數,主要要修改的是retloc,也就是存放函數返回地址的地址,有
時候需要反覆調整這個值才能攻擊成功。

五.後記

    本文的目的是把格式化串漏洞儘量用通俗易懂的語言解釋出來,所以我在解釋原理時沒有用gdb等
工具的調試結果來講,而是儘量地只講原理本身,這樣對於某些不熟悉調試器的讀者來說可能比較容易
理解。也許有人還是會覺得本文寫得不是很易懂,因爲格式化串漏洞本身就是個比較複雜的東西,起碼
需要讀者瞭解一些關於C語言以及堆棧溢出的基本知識,這樣才有助於理解本文。

    也許有些人會說:"爲什麼非要搞清楚這個漏洞的技術細節呢?我不懂細節直接用攻擊程序攻擊一下
也能成功。"沒錯,在實際攻擊的時候我們一般都是不怎麼考慮細節問題的,但是但是但是,你的目標是成
爲一個Hacker呢?還是一個Scriptkid?如果你的目標是前者,那毫無疑問你需要了解技術細節。如果是
後者,你同樣也需要了解技術細節!!因爲了解的細節越多,你攻擊的成功率就越大了。


感謝warning3等nsfocus的同志,他們寫的文章使我受益非淺。

參考文獻:

<<*printf()格式化串安全漏洞分析>>---warning3
<<格式化字符串攻擊>>---Tim Newsham(xuzq譯)
<< Format Bugs:What are they,Where did they come from,...How to exploit them >>---lamagra


歡迎訪問我的主頁:http://isno.yeah.net

(*轉載請保持文章完整*)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章