格式化字符串漏洞原理
pwn题中,有形如下述代码的形式就是格式化字符串漏洞
1 | char str[100]; |
也许使用者的目的只是直接输出字符串,但是这段字符串来源于可控的输入,就造成了漏洞。
示例程序如下
1 |
|
编译:gcc -m32 -o str str.c
输入:%2$x
原因是如果直接printf(“占位符”)这种形式,就会把栈上的偏移当做数据输出出来。通过构造格式化串,就可以实现任意地址读和任意地址写。
任意地址读
事实上,我们在scanf(或者read)来输入字符串的时候,字符串就已经在栈中了,如图,可以看出偏移为6。如果我们构造出addr(4字节)%6$s
,就能读取这个地址的值了。
我们尝试一下,输入AAAA%6$s
,当然不可能真的读到地址为41414141的内存值,不过从下图我框起来的内容就知道,如果我们输入一个合法的值,就可以读了。
任意地址写
和上面的任意地址读是同理的,只不过利用了格式化字符串的一个比较冷门的特性,%n。
这个占位符可以把它前面输出的字符的数量,写入指定的地址。
比如
1 | printf("abc%n", &val); |
val的值就被改变为3。
pwntools
fmtstr
上面说过我们要利用格式化串漏洞就要得到格式化串的偏移,pwntools有自动化代码可以得到这个偏移。
1 | # -*- coding: utf-8 -*- |
fmtstr_payload
生成任意地址写的payload的函数.
1 | fmtstr_payload(offset, {key: value}) |
fmtstr_payload有两个参数
- 第一个参数是int,用于表示取参数的偏移个数
- 第二个参数是字典,字典的意义是往key的地址,写入value的值
赛题链接
https://github.com/eternalsakura/ctf_pwn/tree/master/湖湘杯2017/pwn200
打开IDA跟入调试
形如
1 | char buf[100] |
找到格式化字符串漏洞
利用漏洞
checksec查看保护
tips1
查看本机ASLR
so地址变动,确定本机开启了aslr
关闭ASLRecho 0 > /proc/sys/kernel/randomize_va_space
确认关闭
利用思路
1 | printf(&buf); |
看到printf(&buf)之后
read(buf)
atoi(buf)
所以我们的思路就是,利用格式化字符串漏洞的任意地址读,先leak出puts函数的地址puts_addr
到利用格式化字符串漏洞的任意地址写,去将atoi函数在got.plt表中的地址改为system函数的地址,然后通过read去控制buf,传入”/bin/sh”,构造出system(“bin/sh”),获取shell。
关于覆盖got表,不知道为什么的话,参考下面的文章。
https://www.jianshu.com/p/0ac63c3744dd
http://rickgray.me/use-gdb-to-study-got-and-plt
leak出puts函数的地址
任意地址读:https://ctf-wiki.github.io/ctf-wiki/pwn/fmtstr/fmtstr_exploit.html
调试找到puts的地址在栈中的位置。
在gdb中调试(这里我使用了gef插件),可以看出地址在7个参数(仔细分析一下AAAA%7$x
,把AAAA换掉就是地址,把%x换成%s就可以打印出内容)
计算system地址
libc.symbols['system'] - libc.symbols['puts'] + u32(puts_addr)
覆盖got表中atoi的内容为system地址
原理
1 | printf("abc%nabc\n", &val); |
输出为
abcabc
val = 3
这就告诉我们,%n可以把其前面输出的字符个数,写入&val指向的地址。
如果还不理解的话可以参考:
https://ctf-wiki.github.io/ctf-wiki/pwn/fmtstr/fmtstr_exploit.html
http://www.cnblogs.com/Ox9A82/p/5429099.html
之前我们已经调试过了”AAAA”就在第7个参数,所以只需构造{addr}{适当的写入值}{%7$n
}即可。
这里pwntools提供了fmtstr_payload函数来自动生成格式化串。
fmtstr_payload(参数偏移,{xxx_got_addr: system_addr})
getshell
exp
1 | # coding:utf-8 |