栈上的格式化字符串漏洞利用
通过格式化字符串漏洞可以进行任意内存的读写。由于函数参数通过栈进行传递,因此使用%X$p(X为任意正整数)可以泄露栈上的数据。并且,在能对栈上数据进行控制的情况下,可以事先将想泄露的地址写在栈上,再使用%X$p,就可以以字符串格式输出想泄露的地址。
除此之外,由于"%n"可以将已经成功输出的字符的个数写入对应的整型指针参数所指引的变量,因此事先在栈上布置想要写入的内存的地址。再通过"%Yc%X$n"(Y为想要写入的数据)就可以进行任意的内存写。
32位格式化字符串的利用
main函数是一个无限循环的格式化字符串漏洞:
2、泄露出地址后,复写got表,需要查看格式化字符串(比如printf函数)和system函数的地址相差几字节,然后修改不同的部分即可
64位格式化字符串的利用
思路
如果got表不可写(即因开启了保护无法劫持got表),我们可以劫持malloc_hook/free_hook
,劫持返回地址。
在堆还没有初始化的时候,malloc_hook指向malloc_hook_ini这个函数将malloc_hook置为NULL,并初始化堆,因此默认情况下当初始化堆后malloc_hook是空的。如果能修改malloc_hook为onegadget,那么当调用malloc时就会执行malloc_hook指向的函数。
printf函数在输出的内容时候会调用malloc分配缓冲区,因此我们可以在将malloc_hook劫持到onegadget后再使用%10000c这样的格式化字符串来输出大量字符触发onegadget
操作步骤
泄露地址,方法与32位类似
劫持malloc_hook
修改malloc_hook地址。由于malloc_hook是空的,64位程序中onegadget的有效地址有6个字节,一次写入两字节我们需要写3次,一次写一字节则需要写6次。又因为是6个字节,所以构造时不能像上面那样地址在前,payload在后(最高位的2字节必然为0x00,会让字符串读取时停止)。当地址在后时,%xc%y$hn里的y就需要我们根据payload来决定了
劫持返回地址
修改栈顶的值,当printf函数执行结束后跳转到onegadget,劫持hijack
自动化格式化字符串漏洞利用
在pwntools中有一个fmtstr类,其中有一个fmstr_payload方法,可以自动生成payload:
pwnlib.fmtstr_payload(offset,writes,numbwritten=0,write_size='byte')
其中:
-
offset为用户输入数据在printf中的偏移
-
writes为一个字典,{addr:value,addr2:value2}
-
numbwritten为printf已经输出的字符数
-
write_size为一次性写入多少字节,有byte、short和int三种,分别对应hhn、hn和hn
注意:在使用fmtstr_payload之前需要声明程序的架构,context.arch='i386/amd64...'