0x00 前言
哈哈哈,太懒了,时隔一年半又来更新博客了。
0x01 漏洞原理
最近做pwn的练习,发现格式化字符串漏洞会经常遇到,不太理解,只能抽出时间来研究一下这古老的漏洞。
首先,我们来看一下常见的样式,(题目为“攻防世界”的string)ida反编译后在某个函数中看到如下的代码:
通常使用printf函数,根据cdcel调用约定,从右到左逐个压栈,如果传入字符串,则压入该字符串指针。由于printf无法知道被调用之前有多少参数被压入栈中,所以需要使用format参数用以指定到底有多少参数。
format类型
参数 | 表示 |
---|---|
%d | 表示输出十进制整数 |
%n | 输出所有打印的字符数 |
%x | 输出十六进制数 |
%s | 从内存中读取字符串 |
%p | 显示一个指针 |
# 特性一: printf()函数的参数个数不固定
当我们写作printf(str)时,此时参数可控,我们在控制了format参数之后结合printf()函数的特性就可以进行相应的攻击。
当我们输入format参数大于存在参数时,printf会读取内存内的数据,如图
此时我们只有3个参数,而我们使用printf(“%s %d %d %d %x\n”,buf,a,b,c)读取时,需要读取4个数据,此时便会读取到下一个地址的内容。
以下例进行分析
1 |
|
当我们在fgets时输入AAAA%08x%08x%08x%08x%08x%08x(%08x为格式化为8位16进制数,左对齐,空位补0),调用printf时输出数值时便会读取到内存中的0x41414141(A的16进制为0x41),输出结果为AAAA000000c8b7fc1c20b7e25438080482100000000141414141。
如果我们将AAAA修改为某一敏感地址,使用%s读取地址里面的数据,如读取0x41414141地址的数据\x41\x41\x41\x41%08x%08x%08x%08x%08x%s
# 特性二:利用%n格式符写入数据
%n的作用是把前面已经打印的长度写入某个内存地址,(同一个printf中的输出长度)
如下代码
1 |
|
此时%n成功修改了num的值,输出结果为:
1 | Before: num = 66666666 |
# 特性三:自定义打印字符串宽度
此时我们修改printf("%d%n\n", num, &num);
为printf("%100d%n\n", num, &num);
时,最终num输出为100。若改为%.100d或%0100d,则会在6之前输出92个0。
当我们要把0x8048000地址写入内存时,此时,我们可以使用如下方式
1 | printf("%.134512640d%n\n", num, &num); |
此外还需要$的配合,如下的代码
1 | printf("7th: %7$d, 4th: %4$05d\n", 10, 20, 30, 40, 50, 60, 70, 80); |
会打印输出
1 | 7th: 70, 4th: 00040 |
即%7$d 获取的将是参数列表中第7个元素的值,%4$05d 获取的是第四个参数的值,且有效位长度是5。
0x02 其他相关
在Stack中Canary found绕过问题:
系统产生一个随机数,在程序开始和结尾时会进行检查,如果发生改变就会抛出异常。
某些题目需要将其先泄漏出来,再填充到结束的位置