简介
格式化字符串漏洞(Format String Vulnerability)是一种常见的安全漏洞,它发生在程序使用用户提供的数据作为格式化字符串时。这种漏洞存在于各种编程语言中,尤其是在C语言中,因为C语言标准库中的格式化函数(如 printf)不会自动检查格式化字符串的有效性。
基本知识
格式化字符串漏洞不知道格式化字符串怎么行?以printf为例,printf的第一个参数用来格式化后面的参数(好像每次格式一个字长,比如32位下一个%p格式'aaaa'),称为格式化字符串参数,给出以下几个常见的格式化字符串参数中的字符:
尤其注意%n
字符,它可以修改内存,而其他字符仅仅是显示!
再引入一个m$
,它可以选定要格式化的参数位置,比如:
printf("the third is %3$d, the first is %1$d",1,2,3,4,5,6);
// the third is 3, the first is 1
漏洞原理
如果程序中没有给出第一个带引号("")
的参数,而直接输出后面的参数,而这些参数又被客户端掌控的时候,漏洞就产生了,这意味着客户端可以控制程序的第一个参数进而格式化后面所有的参数!(所有这个词很可怕,它意味着可以索引全部地址)
常见的出现方式
printf(s)
sprintf(s)
利用方式
在利用之前一定要首先确定输入点距离格式化参数的偏移,不要想当然的以为第一个输入的数就是第一个要被格式化的数。
通常使用以下字符串来进行检测:
aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
当看到输出为0x61的时候就是偏移距离m。
以下利用方式都是建立在偏移为m的基础上!
任意内存读取
printf(addr%m$s)
,addr为想要读取的地址。
ps:不过一般也没必要泄露,我都随便改了还泄露个集贸。
任意内存修改
printf(addr_payload_%m$n)
,addr为想要修改的内存的地址。
其中payload用来填充数据至你所希望的数值(一般是“一个字长”-“?很大的数”),但是会面临两种特殊情况,一种是需要覆盖的数字很大,或者很小。
修改为小数值(<一个字长4or8)
printf(payload1_%m$n_payload2_addr)
,addr为想要修改的内存的地址。
可以把payload分为两段,并把目标数值作为payload1的长度,用payload2和payload1组成一个完整的字长,把addr放后面(hh从来没有人要说过addr要放首位,请摆脱惯性思维~),但是这个情况下就要修改一下m(偏移距离)了,一般情况下是原有的基础上加1就可以了。
举例:32位下,修改0x12345678地址内存为2,偏移为8。
printf(aa%9$naa\x78\x56\x34\x12)
,当然使用pwntools可以使用p32(0x12345678)
打包。
修改为大数值(几w就很大了)
printf(addr_addr+1_addr+2_addr+3_payload1_%m$lln_payload2_%(m+1)$lln_payload3_%(m+2)$lln_payload4_%(m+3)$lln)
可以使用lln进行单字节输入而不是囫囵吞枣进行一整个字的输入,也可以ln进行双字节输入,但是要尽量保证两个部分都不是很大。需要注意的是进行单字节输入的时候要保证是从数值的大小顺序进行修改的而不是内存物理顺序。
这里就不举例了,遇到这种烧脑的我选择fmtstr(不烧脑也用,有工具不用是吗喽)
工具fmtstr
PS:其实从这里已经可以看出来,格式化字符串漏洞都有自己的一套解题模板,只有想要修改的内存地址、想要修改的目标内容和输入偏移不同,而且在进行修改任意内存为大数值或者小数值时格外容易晕,于是就有大能制作了这款工具专门用于解决格式化字符串漏洞--fmtstr。
一个集成工具,是pwntools的一部分,具体使用方法参考工具使用-fmtstr。