安全漏洞--字符串格式化(FSV)漏洞分析
来源:互联网 发布:软件测试工程师教学 编辑:程序博客网 时间:2024/05/30 04:18
一 漏洞简介
格式化字符串漏洞(format string vulnerability),也是一种比较常见的漏洞类型。常出现于c语言格式化字符串一系列函数。比如printf,sprintf,fprintf等一系列家族函数。由于此函数对参数类型和个数过滤不严格,导致用户可以构造
任意数据,实现读取写入内存数据,从而实现代码执行。
二 原理分析
至于为什么会产生字符串溢出漏洞呢,我们来看看产生溢出的关键函数。比如说
_Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_ const char *_Format, ...);
这个输出控制字符串函数。解释一下参数:
Format是个格式化控制字符串,里面控制这个字符串输出的格式。
常见格式控制如下列表
...省略号代表是用户可以控制的参数类型和列表。
关键就在于这个位置,参数类型和数量的不确定性,如果允许用户自己定义这两个参数。
那么就可以经过精心够早实现对程序任意位置的数据读取和写入,那么就能构造出漏洞
利用程序,从而控制我们的程序流程。
涉及的关键控制符
%n功能
是将%n之前printf已经打印的字符个数赋值给传入的指针。通过%n我们就可以修改内存中
的值了。例如: printf("aaaaaaa%n\n",&a);
printf("%d\n",a);
可以发现a的值被printf函数修改为了7。这就是%n的功效了。这是一个不常用到的参数.
它的功能是将%n之前printf已经打印的字符个数赋值给传入的指针。通过%n我们就可以
修改内存中的值了。和%sleak内存一样,只要栈中有我们需要修改的内存的地址就可以
使用格式化字符串的漏洞修改它。当然,如果需要修改的数据是相当大的数值时,我们
可以使用%02001d或者%2001x这种形式。
%p功能
格式控制符“%p”中的p是pointer(指针)的缩写。指针的值是语言实现(编译程序)
相关的,但几乎所有实现中,指针的值都是一个表示地址空间中某个存储器单元的整数。
printf函数族中对于%p一般以十六进制整数方式读取输出指针的值,附加前缀0x。
%$功能
格式化字符串的“$”操作符,其允许我们从格式化字符串中选取一个作为特定的参数。
例如:printf("%3$s", 1, "b", "c", 4);
最终会显示结果“c”。这是因为格式化字符串“%3$s”,它告诉计算机“把格式化字
符串后面第三个参数读取告诉我,然后将参数解释为字符串”。所以,也可以这样做
printf("AAAA%3$n");
printf函数将值“4”(输入的A的数量)写入第三个参数指向的地址。
由此可见格式化字符串漏洞主要是:
1.读取任意地址的数值,泄露内存
2.给任意地址写入数据,修改内存
主要实现方式是利用格式化串本身也处于栈中,去用直接参数访问找到这个栈中的格式化串。
这个格式化串可以使用一个要写入的内存地址。也就是直接参数访问+%n格式符+长度表示=
向任意地址写入值,这个写入是不能一次写入4字节的,所以可以分两次写入2字节和分四次
写入1字节。其中hhn是写一个字节,hn是写两个字节。n是写四个字节。
三 漏洞测试
windows系统示例
int _tmain(int argc, _TCHAR* argv[]){char str_test1[100];char str_test2[100];scanf("%s",str_test1);printf("%s\n",str_test1);scanf("%s",str_test2);printf(str_test2);return 0;}
编译完成之后,命令行下面测试。发现同样的输入参数,却输出不同的结果。
而且输出了一部分我们不知道的参数和内存地址。
注意不同地方:
1 主要使用了%P参数控制,泄露了内存地址。
2 printf("%s\n",str_test1);//代码采用格式化进行了过滤,所以并没有输出内存数据。
printf(str_test2); //而这行代码却泄露了我们的内存数据。
Linux系统示例
此处的代码里面有三个整数变量,对应的三个值参数分别为10,20,30第四个没有参数。
但是格式化控制符却有参数控制,因此这个函数将会输出栈地址的30后面的数据也就
是栈的下方数据。
#include<stdio.h>int key=30;int main(int argc, char** argv) {int value=10;int num=20; printf("%d,%d,%d,%p\n",value,num,key); return 0;}
注意看此函数的参数,只有三个参数,格式化控制符却又四个控制,那么第四个%P输出的
是什么呢。
我们来跟进去看看printf函数调用之前的栈布局和数据
同样的原理,如果printf函数的所有参数可以被我们控制,自定义。
这样的话,我们就可以自己控制堆栈数据了。
比如代码这样编写。
#include<stdio.h>int main(int argc, char** argv){ printf(argv[1]); return 0;}
岂不很危险了。
四 漏洞利用
下面我们来做个例题吧,来熟悉练习一下这个漏洞原理吧。
先正向给大家看一下这道题目的源代码:
/* * format.c * Created on: Apr 12, 2017 * Author: 5t4rk */#include<stdio.h>#include<stdlib.h>int nsecret = 0x110;void give_flag(){system("cat /down/key.txt"); }int main(int argc, char** argv){int * pKey = &nsecret;printf(argv[1]);if (nsecret == 2017){give_flag();}return 1;}
阅读分析此源代码,发现有一个函数give_flag,如果实现调用此函数那么就可以直接获
取到机器的shell执行代码权限。但是此处的逻辑必须要满足nsecret==2017这个条件。
才能进入我们的flag获取函数里面,所以此处的关键是如何让nsecret=2017。然后并
没有我们用户直接赋值个nsecret变量的输入和接口,因此直接修改不可能。但是发现
有printf函数的直接调用,argv[1]参数。
大家还记前面讲的漏洞函数,看我们能否利用此函数参数进行修改nsecret变量的值为
2017,基于栈的构造原理,如果成功,那么就会直接进入这个函数。先编译此代码:
得到format这个可执行程序。
然后测试。
测试发现nsecret的变量地址是0x804a024
在对栈上的变量是第七个,于是我们知道只要修改第七个地址数据0x110为
2017就能实现我们的结果。
于是我们想到了前面学习的%n可以实现修改内存地址数据。
结合我们找到的位置,顺带采用%$实现准确覆盖,修改数据。
“%02017u%7$n”
在打印数值右侧用0补齐不足位数的方式来补齐足。
./format "$(python -c 'import sys;sys.stdout.write("%02017x%7$hn")')"
- 安全漏洞--字符串格式化(FSV)漏洞分析
- 格式化字符串漏洞执行任意代码分析
- 格式化字符串漏洞
- 格式化字符串漏洞实验
- 格式化字符串漏洞简介
- 实践格式化字符串漏洞
- 格式化字符串漏洞攻击
- 安全漏洞--整数溢出漏洞(IOV)分析
- 安全漏洞--释放重引用(UAF)漏洞分析
- *printf()格式化串安全漏洞分析(上)
- *printf()格式化串安全漏洞分析(下)
- *printf()格式化串安全漏洞分析(上)
- *printf()格式化串安全漏洞分析(下) (转)
- 格式化字符串漏洞利用 三、格式化字符串漏洞
- [转载]格式化字符串漏洞实验
- 格式化字符串漏洞泄露StackCanary
- Linux 格式化字符串漏洞利用
- 格式化字符串漏洞利用 二、格式化函数
- 国内首家中高端自由职业者共享平台——易分之一,即将上线运营
- 数据结构——链表(JavaScript)
- IOS 时间定时器
- 低成本多串口ARM9工控主板解决方案
- 使用onChange ,获取input标签的值
- 安全漏洞--字符串格式化(FSV)漏洞分析
- 爬取天眼查数据 附代码
- J. Java Beans
- 容器安全、物联网、区块链,中国一马当先:解读今年云计算趋势(7)
- 如何用O(1)的时间复杂度求栈中最小元素
- hive lateral view 与 explode详解
- Android--开发:由模块化到组件化
- http协议总结
- 高德地图定位SDK集成keystore遇坑及解决方案