summer14

C代码审计之格式化字符串

格式化输出函数

printf()   写入标准输出
fprintf()  写入流
sprintf()  写入数组
snprintf() 等同于sprintf(),但是指定了可写入字符的最大值

 

格式化字符串函数的原理

例:printf("test%d%s", 20, "tom");
函数运行时部分栈:
    低地址    &"test%d%s"
             0x14          
    高地址    &"tom"

函数执行时,低地址往高地址依次出栈,直到出栈的数据把格式化字符串的占位符填满为止

 

缓冲区溢出

  当格式化输出对一个数据结构进行越界写时就可能导致缓冲区溢出

复制代码
//脆弱性代码
int main(){
  char
outbuf[512];   char buffer[512];   sprintf(buffer, "ERR Wrong command: %.400s", input);   sprintf(outbuf, buffer);
}
//假设input构造成格式化字符串"%497d\x3c\xd3\xff\xbf" //执行第一个sprintf时,只是简单地把buffer赋值为"ERR Wrong command: %497d\x3c\xd3\xff\xbf" //执行第二个sprintf时,识别到buffer中的%497d,就会向outbuf写入497个字符(从程序栈中取),加上前面的普通字符在内,刚好能够占满outbuf和ebp //剩下的\x3c\xd3\xff\xbf就会覆盖返回地址,去执行攻击者在该地址写的恶意代码 修复方案: 第二个sprintf在这里只是起到简单的复制作用,但是其识别格式化字符串的特性让攻击者有机可乘。 第一个sprintf已经限制了输入的字符数,因此可以把第二个sprintf()替换成strcpy()
复制代码

被恶意利用后main函数的栈帧

 

    

 

查看内存

  占位符+字母对栈的操作

  %x  输出栈的数据
  %s  把栈中的数据当做地址,获取这个地址的数据。
//脆弱性代码
int main(){
  scanf("%s", s);   printf(s);
}
假设输入"\xdc\xf5\x42\x01%x%x%x%x%s",程序运行时
  printf函数的部分栈帧(低地址->高地址)
  ...
  &"\xdc\xf5\x42\x01%x%x%x%x%s"
  ...
  main函数的部分栈帧(低地址->高地址)
  ...
  0142f5dc    //小端存储
  &"%x%x"
  &"%x%x"
  &"%s\0"
  ...

因为在程序栈中,main函数(主调函数)的栈帧比printf函数(被调函数)的栈帧地址高

而格式化字符串从低地址往高地址按顺序取出数据

假设此时0142f5dc在栈中比格式化字符串&"\xdc\xf5\x42\x01%x%x%x%x%s"高5个栈,那么:

  %x%x%x%x依次取出4个数据

  %s根据0142f5dc这个地址,读取内存中该地址的数据

在上述过程中,"\xdc\xf5\x42\x01"是攻击者想要读取的内存地址,%x移动参数指针,%s获取内存地址的数据

 

缓解策略

1、限制输入的字节数
2、排除用户输入的格式化字符串

posted on   summer14  阅读(46)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示