《0day安全》——格式化字符串漏洞

printf中的缺陷

来个简单的程序,体验体验漏洞

#include <stdio.h>
int main(){
int a=44, b=77;
printf("a=%d, b=%d\n", a, b);
printf("a=%d, b=%d\n");
return 0;
}

release版本编译运行

blob.png

第一个的结果是正确的,第二个有点奇怪,因为我们没给他传递参数,所以它会自己从栈上找数据输出

载入od看看

blob.png

代码也是简单,毕竟是release版本

也可以看到调用了第二个printf之后才进行栈平衡操作(printf等库函数是要调用者清理堆栈的)

第一次调用,栈内容如下(参数从右到左入栈)

blob.png

确实这样的

blob.png

当到第二个的时候就变成

blob.png

blob.png

那么最终就是

printf("a=%d, b=%d\n",&"a=%d, b=%d\n", 0x2c );

那么为什么这样输出我们就明白了

这里还有一个问题就是应该是编译器优化的问题,他不会调用一次printf函数就平衡一次栈,最后才平衡,这样可以提高一丁丁的效率

blob.png

那么这样不就可能泄露栈上的数据了吗

用printf读取内存数据

其实上面的程序只能算是一个bug,如果我们能控制字符串格式控制符,那么就是格式化串漏洞了

比如下面的

#include <stdio.h>
int main(int argc, char** argv){
printf(argv[1]);
return 0;
}

看看我们能用那些控制符

blob.png

用p的话就可以输出栈上的数据了

我们测试一下吧

blob.png

用printf向内存写数据

有个很少人知道的控制符%n,他是将当前输出的所有的数据的长度写回一个变量中去

#include <stdio.h>
int main(int argc, char** argv){
int len_printf = 0;
printf("before write: length=%d\n", len_printf);
printf("giantbranch:%d%n\n", len_printf, &len_printf);
printf("after write: length=%d\n", len_printf);
return 0;
}

blob.png

可以看到%n前面输出了13个字符(giantbranch:0),所以最终len_printf的长度被改了

加入我们要精确控制len_printf的值,那么在%加个数字就好了

blob.png

检测与防范

直接静态代码扫描,看看第一个参数是否用户可控就行了,

据说vs2005在编译级别对参数做了更好的检查,而且默认情况关闭了对%n控制符的使用

打赏
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

发表评论