首页
社区
课程
招聘
[原创]Pwn学习笔记:printf格式化字符串漏洞原理与利用
发表于: 2019-4-18 16:01 21695

[原创]Pwn学习笔记:printf格式化字符串漏洞原理与利用

2019-4-18 16:01
21695

在C语言中,我们常用的输出函数有printf、 fprintf、 vprintf、 vfprintf、 sprint等。对于这些输出函数,Format String是其第一个参数,我们一般称之为格式化字符串。

下面简单介绍格式化字符串如何在输出函数进行解析。

printf 接受变长的参数,其中第一个参数为格式化字符串,后面的参数在实际运行时将与格式化字符串中特定格式的子字符串进行对应,将格式化字符串中的特定子串,解析为相应的参数值。

举个例子来说:



在上面这行语句中, "Team Name: %s\tPoints: %d\n"为格式化字符串,"Whitzard"、999分别为第二个和第三个参数。在格式化字符串解析时,格式化字符串中的 "%s" 对应到第二个参数 "Whitzard" ,而 "%d" 则对应到第三个参数999。(\t \n在此处不做讲解。)

于是实际输出为如下结果:


在格式化字符串中,"%s"、"%d" 等类型的符号叫符号说明,这些符号说明的基本格式为 %parameterfield width[length]type 。相信大家对于简单的符号说明并不陌生,但如果要利用格式化字符串漏洞,我们还需要用到几个比较冷门的符号说明,如:




了解格式化字符串的解析方法后,我们还需要知道printf传参方式,以及格式化字符串的调用约定,才能实现对于格式化字符串的利用。

在x86(32-bit)系统中,printf的参数是按参数顺序依次存放在栈上的,我们举个栗子来演示printf的调用约定。

对于下面的程序,我们为printf传入一个格式化字符串和若干个参数。通过gdb调试将程序断点设在printf处可查看参数在栈上的分布。


Format String:




对于X86_64(64-bit)的系统,printf的调用约定与32-bit不同,64位系统中前6个参数是存放在寄存器中的。

我们依然按上面的方法对调用约定进行说明。利用gdb调试查看寄存器以及栈的情况。

Register :



Format String :




Stack :




可以发现,64位程序调用printf的传参约定为:

前六个参数按序存放在 RDI(指向format string的指针) 、RSI、RDX、 RCX、 R8以及R9(前5个变长参数)寄存器中,其余的变长参数依次存放在栈上。

在了解printf变长参数的特性之后,我们能够发现一些这个函数可能存在的漏洞。

我们已经知道,printf函数在执行时,首先进行格式化字符串的解析——从栈(或者寄存器)获取参数并与符号说明进行匹配,然后将匹配的结果输出到屏幕上。那么,如果格式化字符串中的符号声明与栈上参数不能正确匹配,比如参数个数少于符号声明个数时,就有可能造成泄露。

另外,printf也是一项有力的攻击武器,我们可以通过控制格式化字符串的值来实现更多的泄露或者完成更高级的利用。

由于格式化字符串变长参数的特性,在实际运行中,如果Format String的符号说明个数超过待匹配的参数个数,即有更多的符号说明需要被匹配时,printf会根据解析结果和调用约定去取栈上(reg)相应的值并输出。

我们以下面这段32位下的程序来说明如何利用printf完成泄露。

Output :

当我们不向printf提供更多参数时,printf会打出栈上本不应该被访问到信息,我们能够通过这种方式获取到很多有用的信息,例如通过泄露的栈变量计算参数偏移量、通过栈上的信息计算程序基地址以及libc基地址等。




而当我们可以控制printf的格式化字符串时,我们可以完成更多操作。

对于下面这段程序,开发者希望我们输入名字字符串,然后程序使用printf输出我们的名字进行一个交互。然而我们可以通过构造特殊的input,让程序完成一些开发者意料之外的事情。

当我们构造一个payload如下:


利用这个payload我们可以获得之前程序的结果,完成一个泄露。


现在我们考虑构造一个新的payload如下:


这个payload传递一个特殊的符号说明'%7$s',和使用pwntools中p32()处理后的scanf的got表地址。这个格式化字符串被printf接收后会将栈上的第7+1个参数进行%s的解析,读取该地址指向的字符串。而我们通过gdb调试可以发现,栈上第8个参数正好是我们构造的payload第二部分p32(code.got['__isoc99_scanf'])。这样实际上完成了对scanf的got表指向的libc地址进行字符串解析并输出,完成了libc的泄露。


具体情况如下所示。

Output :


Analysis : 


根据gdb调试结果可知,这次got表能泄露的关键在于源代码中将format string作为临时变量放在栈上。故用%7$s去解析栈上的0x804a018时,就泄露了got表。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-4-21 20:57 被0xbird编辑 ,原因: 排版修改
收藏
免费 3
支持
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  junkboy   +2.00 2019/04/18 感谢分享~
最新回复 (14)
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
666,楼主高产啊,赞一个
2019-4-18 16:35
0
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享  感觉很酷
2019-4-18 23:29
0
雪    币: 4
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
请问楼主的got命令是哪里的?peda里好像没有啊
2019-4-19 01:42
0
雪    币: 3051
活跃值: (1392)
能力值: ( LV13,RANK:480 )
在线值:
发帖
回帖
粉丝
5
pwndbg
2019-4-19 08:05
0
雪    币: 3683
活跃值: (35)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
6
感谢楼主分享,学习了
2019-4-19 08:31
0
雪    币: 3051
活跃值: (1392)
能力值: ( LV13,RANK:480 )
在线值:
发帖
回帖
粉丝
7
Editor 666,楼主高产啊,赞一个
怎么加目录  谁能告诉我
2019-4-19 11:34
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
8
hackerbirder 怎么加目录 谁能告诉我[em_2]
使用markdown格式的输入文本,在最前面加个[toc],有标题样式的题目就会自动生成目录了哈
2019-4-19 13:32
0
雪    币: 41
活跃值: (360)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
楼主能分享下参考文章的链接么
2019-4-19 15:38
0
雪    币: 355
活跃值: (15)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
10
DeepFantasy 楼主能分享下参考文章的链接么
这个是原文链接 https://mp.weixin.qq.com/s/iHjrePchZaryzbbc1sDCGg
2019-4-26 14:38
0
雪    币: 355
活跃值: (15)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
11
DeepFantasy 楼主能分享下参考文章的链接么
楼主这是直接把白泽战队文章的学习笔记 拿过来了?
2019-4-26 14:38
0
雪    币: 3051
活跃值: (1392)
能力值: ( LV13,RANK:480 )
在线值:
发帖
回帖
粉丝
12
Victorgg 楼主这是直接把白泽战队文章的学习笔记 拿过来了?
复现学习分享下,跟白泽同学说了
2019-4-26 19:20
0
雪    币: 3
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
代码行号都贴出来了,有些图也配错了,说实话,文章整的有点不走心。
2019-11-16 21:36
0
雪    币: 300
活跃值: (2472)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2019-11-17 00:40
0
雪    币: 177
活跃值: (278)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
有没有人碰到地址类似 0x40000010 的情况?地址含0,字符串无法解析
2021-5-31 10:39
0
游客
登录 | 注册 方可回帖
返回
//