linux最先的漏洞防禦機制nx-stack剛剛出現後就有人想出了突破方法。那就是隻有棧是不可執行,而除了棧之外的其餘地方仍是能夠執行的,只要把返回地址執行別的地方就能夠。html
格式化字符串漏洞在Windows下很難以利用,可是在Linux下的pwn題目中出現的頻率是很高的。linux
格式化字符串的「$」操做符,其容許咱們從格式化字符串中選取一個做爲特定的參數。例如,
printf("%3$s", 1, "b", "c", 4);
最終會顯示結果「c」。這是由於格式化字符串「%3$s」,它告訴計算機「把格式化字符串後面的第三個參數告訴我,而後將參數解釋爲字符串」。因此,咱們也能夠這樣作
printf("AAAA%3$n");
printf函數將值「4」(輸入的A的數量)寫入第三個參數指向的地址。函數
x64的狀況要刨除5個用寄存器傳遞的參數spa
格式化字符串還有%s
參數。那麼,若是在棧中保存有指向咱們感興趣數據的指針,咱們就能夠在打印指針的時候使用一個%s
來打印別的地方的內容。並且通常的程序都會將用戶輸入的數據儲存在棧上。這就給了咱們一個構造指針的機會,再結合格式化字符串漏洞,幾乎能夠獲得全部內存數據。3d
%n
功能是將%n
以前printf
已經打印的字符個數賦值給傳入的指針。經過%n
咱們就能夠修改內存中的值了。和%s
leak內存同樣,只要棧中有咱們須要修改的內存的地址就能夠使用格式化字符串的漏洞修改它。指針
固然,若是須要修改的數據是至關大的數值時,咱們能夠使用%02333d這種形式。在打印數值右側用0補齊不足位數的方式來補齊足。code
能夠看出,格式化字符串能夠修改的內存範圍更加廣。只要構造出指針,就能夠改寫內存中的任何數值。和棧溢出的地毯轟炸不一樣。這種一次只能改寫一個dword大小的內存的攻擊方式更加精準而致命。orm
因而可知格式化字符串漏洞主要是:htm
1.泄漏任意地址的值,leak內存(好比leak出libc基地址)blog
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函數並不意味着只能進行一次寫操做,實際上能夠傳遞多個寫格式串以實現一次調用對多個地址寫的操做,注意寫入的值是「以前」輸入的字符總數。