格式化字符串初识
格式化字符串
函数:
int printf(const char *format, [argument]);
printf()函数的调用格式为: printf(“<格式化字符串>”, <参量表>)。
基本的格式化字符串参数
%c:输出字符,配上%n可用于向指定地址写数据。
%d:输出十进制整数,配上%n可用于向指定地址写数据。
%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。
%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。
%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。
%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100x10$n (%100d10$n)表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。
%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。
源码
|
|
生成文件gcc -m32 -no-pie -fno-stack-protector -z execstack -o fmt fmt.c
分析
ida调试,因为a,b都是全局变量,找到a,b的地址
gdb调试
确定输入的是第6个参数
exp
|
|
这个exp解释一下:%12x表示填充12个字节。特别payloada的时候,%8$n是字符输入的,所以占4个字节,所以地址是在第8个参数,但是输出不会打印出来,所以下面的构造payloadb时候,%x$n不算在字节。
格式化的利用
程序
链接:http://pan.baidu.com/s/1mil6uTE 密码:7ff0
有canary保护,感觉现在还不会
分析程序
我们去尝试运行,摸清了基本上的程序的功能。
用ida打开看看,发现有system函数,很开心.发现main函数有格式化漏洞
printf(&v5)这里是格式化漏洞,继续看发现getnline函数
strlen(s)这里是计算输入进来字符的长度,只有一个参数,这里是不是很熟悉啊,没错。我们的system函数 也只有一个参数system(s),那么我们可以把将system的plt覆盖到strlen的got,再将参数’/bin/sh’传入,这里我们需要做两件事情
1、将strlen_got=system_plt
2、回到mian函数,将’/bin/sh’输入,这时就能到达system(‘/bin/sh’)
写exp
这里的问题是,格式化执行一次后程序就会退出了,这里了解一下elf程序运行机制,当程序执行main函数前会执行一些准备开始的程序,比如:。初始化节 .init_array 和 .init
结束时也会运行一些结束的程序,比如:动态库还可提供终止节。终止节 .fini_array
这就意味着我们可以利用终止节回到mian再次执行。
查看终止节objdump -s pwn3 |grep fini
发现存在终止节
接着利用pwn3直接得到
exp:
|
|
offset=12为什么是12,这是我手动的,当然pwntools提供了找偏移的个数的函数Fmtstr,
FmtStr和fmtstr_payload函数参考:
https://esebanana.github.io/2017/10/09/pwn6/#more
修改exp:
上面的那个exp本地运行成功,但如果连上网的话,由于fmtstr_payload函数是产生大量的数据写入,如要写入的地址时0x8040506,那么就会生成0x8040506的无用字符传过去,在本地也需要运行一会儿,在上网根本不行,这里得用字节写入,这里可以参考:
https://esebanana.github.io/2017/09/19/pwn3/#more
中的修改b,贴上修改后的exp
这个exp是两字节写入
FmtStr函数的利用
FmtStr函数
FmtStr函数可以获取偏移参数的个数offset
例如:
FmtStr函数利用
结果:
fmtstr_payload函数
fmtstr_payload是pwntools提供的函数,用于自动生成格式化字符串。
fmtstr_payload有两个参数
第一个参数是int,用于表示取参数的偏移个数offset=7
第二个参数是字典,字典的意义是往key的地址,写入value的值fmtstr_payload(7, {printf_got: system_add})
这个函数调用会往printf_got中写入system_add
文学修养
我本楚狂人,凤歌笑孔丘
奈何许!天下人何限?慊慊只为汝!