linux最早的漏洞防護機制nx-stack剛剛出現后就有人想出了突破方法。那就是只有棧是不可執行,而除了棧以外的其他地方還是可以執行的,只要把返回地址執行別的地方就可以。
一.格式化字符串漏洞
格式化字符串漏洞在Windows下很難以利用,但是在Linux下的pwn題目中出現的頻率是很高的。
格式化字符串的“$”操作符,其允許我們從格式化字符串中選取一個作為特定的參數。例如,
printf("%3$s", 1, "b", "c", 4);
最終會顯示結果“c”。這是因為格式化字符串“%3$s”,它告訴計算機“把格式化字符串后面的第三個參數告訴我,然后將參數解釋為字符串”。所以,我們也可以這樣做
printf("AAAA%3$n");
printf函數將值“4”(輸入的A的數量)寫入第三個參數指向的地址。
x64的情況要刨除5個用寄存器傳遞的參數
格式化字符串還有%s
參數。那么,如果在棧中保存有指向我們感興趣數據的指針,我們就可以在打印指針的時候使用一個%s
來打印別的地方的內容。而且一般的程序都會將用戶輸入的數據儲存在棧上。這就給了我們一個構造指針的機會,再結合格式化字符串漏洞,幾乎可以得到所有內存數據。
%n
功能是將%n
之前printf
已經打印的字符個數賦值給傳入的指針。通過%n
我們就可以修改內存中的值了。和%s
leak內存一樣,只要棧中有我們需要修改的內存的地址就可以使用格式化字符串的漏洞修改它。
當然,如果需要修改的數據是相當大的數值時,我們可以使用%02333d這種形式。在打印數值右側用0補齊不足位數的方式來補齊足。
可以看出,格式化字符串可以修改的內存范圍更加廣。只要構造出指針,就可以改寫內存中的任何數值。和棧溢出的地毯轟炸不同。這種一次只能改寫一個dword大小的內存的攻擊方式更加精准而致命。
由此可見格式化字符串漏洞主要是:
1.泄漏任意地址的值,leak內存(比如leak出libc基地址)
2.寫任意地址,可用於修改got表
主要實現方式是利用格式化串本身也處於棧中,去用直接參數訪問找到這個棧中的格式化串。這個格式化串可以使用一個要寫入的內存地址。也就是直接參數訪問+%n格式符+長度表示=向任意地址寫入值,這個寫入是不能一次寫入4字節的,所以可以分兩次寫入2字節和分四次寫入1字節。其中hhn是寫一個字節,hn是寫兩個字節。n是寫四個字(注意是到目前的%n為止前面的所有)。
附送栗子一枚:http://www.cnblogs.com/Ox9A82/p/5483916.html
這部分來自icemakr的博客 32位 讀 '%{}$x'.format(index) // 讀4個字節 '%{}$p'.format(index) // 同上面 '${}$s'.format(index) 寫 '%{}$n'.format(index) // 解引用,寫入四個字節 '%{}$hn'.format(index) // 解引用,寫入兩個字節 '%{}$hhn'.format(index) // 解引用,寫入一個字節 '%{}$lln'.format(index) // 解引用,寫入八個字節 64位 讀 '%{}$x'.format(index, num) // 讀4個字節 '%{}$lx'.format(index, num) // 讀8個字節 '%{}$p'.format(index) // 讀8個字節 '${}$s'.format(index) 寫 '%{}$n'.format(index) // 解引用,寫入四個字節 '%{}$hn'.format(index) // 解引用,寫入兩個字節 '%{}$hhn'.format(index) // 解引用,寫入一個字節 '%{}$lln'.format(index) // 解引用,寫入八個字節 %1$lx: RSI %2$lx: RDX %3$lx: RCX %4$lx: R8 %5$lx: R9 %6$lx: 棧上的第一個QWORD
其它
這里記錄一些相關的姿勢
fmtstr_payload是pwntools提供的函數,用於自動生成格式化字符串。
fmtstr_payload有兩個參數
第一個參數是int,用於表示取參數的偏移個數
第二個參數是字典,字典的意義是往key的地址,寫入value的值
fmtstr_payload(7, {printf_got: system_add})
這個函數調用會往printf_got中寫入system_add
此外調用一次fsb函數並不意味着只能進行一次寫操作,實際上可以傳遞多個寫格式串以實現一次調用對多個地址寫的操作,注意寫入的值是“之前”輸入的字符總數。