格式化字符串攻击原理及示例

一、类printf函数簇实现原理

类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型。

对于这种情况,可以使用省略号指定参数表。

带有省略号的函数定义中,参数表分为两部分,前半部分是确定个数、确定类型的参数,第二部分就是省略号,代表数目和类型都不确定的参数表,省略号参数表中参数的个数和参数的类型是事先的约定计算出来的,每个实参的地址(指针)是根据确定参数表中最后一个实参的地址算出来的。

这里涉及到函数调用时的栈操作。函数栈的栈底是高地址,栈顶是底地址。在函数调用

时函数实参是从最后一个参数(最右边的参数)到第一个参数(最左边的参数)依次被压入栈顶方向。也就是说函数调用时,函数实参的地址是相连的,并且从左到右地址是依次增加的。如:

 #include <stdio.h>
#include <stdlib.h> void fun(int a, ...)
{
int i;
int *temp = &a;
temp++;
for (i = ; i < a; ++i)
{
printf("%d ",*temp);
temp++;
}
printf("/n");
} int main()
{
int a = ;
int b = ;
int c = ;
int d = ;
fun(, a, b, c, d);
return ;
}

在上面的例子中,void fun(int a, ...)函数约定第一个确定参数表示省略号参数表中参数的个数,省略号参数表中的参数全都是int 类型的,这样fun函数就可以正常工作了。

类printf函数簇的工作原理和fun函数是一样的,只不过更为复杂和精巧。

如printf的函数形式为 int printf(const char *fmt, …)。

由于printf函数实现的功能比较复杂,我们来看一个我们自己实现的myprintf函数,改函数不涉及低层系统io操作。

 #include <stdio.h>
#include <stdlib.h> void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型
{
char* pArg=NULL; //等价于printf原始实现的va_list
char c;
pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
pArg += sizeof(fmt); //等价于原来的va_start do
{
c =*fmt;
if (c != '%')
{
putchar(c); //照原样输出字符
}
else
{
//按格式字符输出数据
switch(*++fmt)
{
case 'd':
printf("%d",*((int*)pArg));
break;
case 'x':
printf("%#x",*((int*)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等价于原来的va_arg
}
++fmt;
}while (*fmt != '/0');
pArg = NULL; //等价于va_end
return;
} int main(int argc, char* argv[])
{
int i = ;
int j = ;
myprintf("the first test:i=%d/n",i,j);
myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);
return ;
}

myprintf函数中也有类似的约定,确定参数表中最后一个参数是一个const char* 类型的字符串,在这个字符串中出现“%d”和“%x”次数的和就是省略号参数表中参数的个数,省略号参数表中的参数类型也都是int类型。

同样的,实际的printf函数也有这样的约定:确定参数表中最后一个参数是一个const char* 类型的字符串,省略号参数表中参数个数就是这个字符串中出现的“%d”,“%x”,“%s”…次数的和,省略号参数表中参数的类型也是由“%d”,“%x”,“%s”……等格式化字符来指示的。

因此,类printf函数中省略号参数表中参数的个数和类型都是由类printf函数中的那个格式化字符串来决定的。

二、格式化字符串攻击原理

因为类printf函数中省略号参数表中参数的个数和类型都是由类printf函数中的那个格式化字符串来决定的,所以攻击者可以利用编程者的疏忽或漏洞,巧妙构造格式化字符串,达到攻击目的。

如果一个程序员的任务是:打印输出一个字符串或者把这个串拷贝到某缓冲区内。他可以写出如下的代码:printf("%s", str);但是为了节约时间和提高效率,并在源码中少输入6个字节,他会这样写:printf(str);

为什么程序员写的是错误的呢?他传入了一个他想要逐字打印的字符串。实际上该字符串被printf函数解释为一个格式化字符(formatstring),printf就会根据该字符串来决定printf函数中省略号参数表中参数的格式和类型,如果这个程序员想要打印的字符串中刚好有“%d”,“%x”之类的格式化字符,那么一个变量的参数值就从堆栈中取出。

比如:

 #include <stdio.h>
#include <stdlib.h> int main(int argc, char* argv[])
{
if(argc != )
return ;
printf(argv[]);
return ;
}

当./a.out “hello world”时一切正常,但是当./a.out “%x”时,就会有莫名其妙的数字被打印出来了。

很明显,攻击者至少可以通过打印出堆栈中的这些值来偷看程序的内存。但是有些事情就不那么明显了,这个简单的错误允许向运行中程序的内存里写入任意值。

printf有一个比较另类的用法:%n,当在格式化字符串中碰到"%n"的时候,在%n域之前输出的字符个数会保存到下一个参数里。例如,为了获取在两个格式化的数字之间空间的偏量:

 int main(int argc, char* argv[])
{
int pos, x = , y = ;
printf("%d %n%d/n", x, &pos, y);
printf("The offset was %d/n", pos);
return ;
}

输出4(“235 ”的长度)

%n格式返回应该被输出的字符数目,而不是实际输出的字符数目。当把一个字符串格式化输出到一个定长缓冲区内时,输出字符串可能被截短。不考虑截短的影响,%n格式表示如果不被截短的偏量值(输出字符数目)。为了说明这一点,下面的代码会输出100而不是20:

 int main()
{
char buf[];
int pos, x = ;
snprintf(buf, sizeof(buf), "%.100d%n", x, &pos);
printf("position: %d/n", pos);
return ;
}

而%n和%d,%x,%s的显著的不同就是%n是会改变变量的值的,这也就是格式化字符串攻击的爆破点。

三、一个实际的例子

下面这个例子至少可以X86的Redhat和arch Linux下面进行演示。

 #include <stdio.h>
#include <stdlib.h>
#include <string.h> char daddr[]; int main(int argc, char **argv)
{
char buf[];
int x;
x = ;
memset(daddr,'/0',);
printf("before format string x is %d/%#x (@ %p)/n", x, x, &x);
strncpy(daddr,"PPPPPPP%n",);
snprintf(buf,sizeof(buf),daddr); //实施格式化字符串攻击 buf[sizeof(buf) - ] = ;
printf("after format string x is %d/%#x (@ %p)/n", x, x, &x);
return ;
}

运行的结果是:x被成功的改成了7。

上面的例子利用了linux函数调用时的内存残像,来实现格式化字符串攻击的。(参考的经典文章是用猜地址的方法来实现的,猜的一头雾水)

这里我们来分析一下main函数中的堆栈变化情况:

如上图所示,在调用snprintf函数之前,首先调用了printf函数,printf的函数第四个参数是&x,这样在main函数的堆栈内存中留下了&x的内存残像。当调用snprintf时,系统本来只给snprintf准备了3个参数,但是由于格式化字符串攻击,使得snprinf认为应该有四个参数传给它,这样snprintf就私自把&x的内存残像作为第4个参数读走了,而snprintf所谓的第4个参数对应的“%n”,于是snprintf就成功的修改了变量x的值。

而在实际网络环境中可利用的格式化字符串攻击也是很多的。下图就是一个实际网络攻击的截图。

文.RP.URL:http://blog.csdn.net/immcss/article/details/6267849

格式化字符串攻击原理及示例.RP的更多相关文章

  1. XSS攻击原理、示例和防范措施

    XSS攻击 XSS(Cross-Site Scripting,跨站脚本)攻击历史悠久,是危害范围非常广的攻击方式. Cross-Site Stripting的缩写本应该是CSS,但是为了避免和Casc ...

  2. web安全防范之SQL注入攻击、攻击原理和防范措施

    SQL注入 攻击原理 在编写SQL语句时,如果直接将用户传入的数据作为参数使用字符串拼接的方式插入到SQL查询中,那么攻击者可以通过注入其他语句来执行攻击操作,这些攻击包括可以通过SQL语句做的任何事 ...

  3. 网络XSS攻击和CSRF攻击原理及防范

    网络XSS攻击和CSRF攻击原理及防范 原文地址:http://www.freebuf.com/articles/web/39234.html 随着Web2.0.社交网络.微博等等一系列新型的互联网产 ...

  4. 通过格式化字符串漏洞绕过canary

    1.1    canary内存保护机制 1.1.1    canary工作原理 canary保护机制类似于/GS保护机制,是Linux下gcc编译器的安全保护机制之一,在栈中的结构如下图所示: 在函数 ...

  5. 详谈Format String(格式化字符串)漏洞

    格式化字符串漏洞由于目前编译器的默认禁止敏感格式控制符,而且容易通过代码审计中发现,所以此类漏洞极少出现,一直没有笔者本人的引起重视.最近捣鼓pwn题,遇上了不少,决定好好总结了一下. 格式化字符串漏 ...

  6. 148.CSRF攻击原理分析、防御、装饰器、中间件、IFrame以及js实现csrf攻击

    CSRF攻击概述: CSRF(Cross Site Request Forgery 跨站域请求伪造)是一种网站攻击的方式,它在2007年曾被列为互联网20大安全隐患之一.其他的安全隐患,比如SQL脚本 ...

  7. .NET中DateTime.Now.ToString的格式化字符串

    .NET中DateTime.Now.ToString显示毫秒:DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") DateTime.N ...

  8. 飘逸的python - 增强的格式化字符串format函数

    自python2.6开始,新增了一种格式化字符串的函数str.format(),可谓威力十足.那么,他跟之前的%型格式化字符串相比,有什么优越的存在呢?让我们来揭开它羞答答的面纱. 语法 它通过{}和 ...

  9. [转]:C#的ToString如何格式化字符串

    C 货币 2.5.ToString("C") ¥2.50 D 十进制数 25.ToString("D5") 00025 E 科学型 25000.ToString ...

随机推荐

  1. uva10780(分解质因数)

    可以直接用高精度来暴力求. 也可以不用高精度: 把m分解质因数,记录每个因数和它的次数.然后计算每个因数在n的阶乘里出现了多少次,再把这个次数除以它在m中的次数,就是可能的k值.取最小的k. #inc ...

  2. vector map迭代器失效解决方案

    vector : iter = container.erase(iter);    //erase的返回值是删除元素下一个元素的迭代器 vector<int>::iterator it = ...

  3. windows文件名非法字符过滤检测-正则表达式

    过滤文件名非法字符 windows现在已知的文件名非法字符有 \ / : * ? " < > | var reg = new RegExp('[\\\\/:*?\"&l ...

  4. mount: error mounting /dev/root on /sysroot as ext3: Invalid argument

    /************************************************************************ * mount: error mounting /d ...

  5. Linux命令学习(17):ifconfig命令

    版权声明更新:2017-05-22博主:LuckyAlan联系:liuwenvip163@163.com声明:吃水不忘挖井人,转载请注明出处! 1 文章介绍 我们知道,在windows中,除了在图形界 ...

  6. Python 2.7_利用xpath语法爬取豆瓣图书top250信息_20170129

    大年初二,忙完家里一些事,顺带有人交流爬取豆瓣图书top250 1.构造urls列表 urls=['https://book.douban.com/top250?start={}'.format(st ...

  7. LeetCode Count Binary Substrings

    原题链接在这里:https://leetcode.com/problems/count-binary-substrings/description/ 题目: Give a string s, coun ...

  8. 技术总监Sycx的故事

    其实我在各种演讲里,线下吹牛里面无数次提及过他,讲过他的故事,但是总还是没有任何一次认认真真的详细讲过,所以,今天就讲讲他的故事吧. Sycx是福建漳州人,我经常开玩笑说,你生于一个著名的骗子之乡,为 ...

  9. Spring Boot自定义配置与加载

    Spring Boot自定义配置与加载 application.properties主要用来配置数据库连接.日志相关配置等.除了这些配置内容之外,还可以自定义一些配置项,如: my.config.ms ...

  10. 机器学习:评价分类结果(Precision - Recall 的平衡、P - R 曲线)

    一.Precision - Recall 的平衡 1)基础理论 调整阈值的大小,可以调节精准率和召回率的比重: 阈值:threshold,分类边界值,score > threshold 时分类为 ...