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

Format_String_Attack_Lab 软件安全课程实验:Format_String_Attack_Lab; 版本:SeedUbuntu20.04; 课本:计算机安全导论:深度实践; 阅读详情

一、类printf函数簇实现原理

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

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

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

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

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

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. void fun(int a, ...)  
  5. {  
  6.     int i;  
  7.     int *temp = &a;  
  8.     temp++;  
  9.     for (i = 0; i < a; ++i)  
  10.     {  
  11.         printf("%d ",*temp);  
  12.         temp++;  
  13.     }  
  14.     printf("/n");  
  15. }  
  16.   
  17. int main()  
  18. {  
  19.     int a = 1;  
  20.     int b = 2;  
  21.     int c = 3;  
  22.     int d = 4;  
  23.     fun(4, a, b, c, d);  
  24.     return 0;  
  25. }   

 

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

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

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

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

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型  
  5. {  
  6.     char* pArg=NULL; //等价于printf原始实现的va_list  
  7.     char c;  
  8.     pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值  
  9.      pArg += sizeof(fmt); //等价于原来的va_start  
  10.   
  11.     do  
  12.     {  
  13.         c =*fmt;  
  14.         if (c != '%')  
  15.         {  
  16.             putchar(c); //照原样输出字符  
  17.           }  
  18.         else  
  19.         {  
  20.             //按格式字符输出数据  
  21.                switch(*++fmt)  
  22.             {  
  23.                 case 'd':  
  24.                     printf("%d",*((int*)pArg));  
  25.                     break;  
  26.                 case 'x':  
  27.                     printf("%#x",*((int*)pArg));  
  28.                     break;  
  29.                 default:  
  30.                     break;  
  31.             }  
  32.             pArg += sizeof(int); //等价于原来的va_arg  
  33.         }  
  34.         ++fmt;  
  35.     }while (*fmt != '/0');  
  36.     pArg = NULL; //等价于va_end  
  37.     return;  
  38. }  
  39.   
  40. int main(int argc, char* argv[])  
  41. {  
  42.     int i = 1;  
  43.     int j = 2;  
  44.     myprintf("the first test:i=%d/n",i,j);  
  45.     myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j);   
  46.     return 0;  
  47. }  

 

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”之类的格式化字符,那么一个变量的参数值就从堆栈中取出。

比如:

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h>  
  2. #include <stdlib.h>   
  3.   
  4. int main(int argc, char* argv[])  
  5. {  
  6.     if(argc != 2)  
  7.         return 0;  
  8.     printf(argv[1]);  
  9.     return 0;  
  10. }  

 

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

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

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

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. int main(int argc, char* argv[])  
  2. {  
  3.     int pos, x = 235, y = 93;  
  4.     printf("%d %n%d/n", x, &pos, y);  
  5.     printf("The offset was %d/n", pos);  
  6.     return 0;  
  7. }  

 

输出4(“235 ”的长度)

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

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. int main()  
  2. {  
  3.     char buf[20];  
  4.     int pos, x = 0;  
  5.     snprintf(buf, sizeof(buf), "%.100d%n", x, &pos);  
  6.     printf("position: %d/n", pos);  
  7.     return 0;  
  8. }  

 

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

三、一个实际的例子

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

 

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. char daddr[16];  
  6.   
  7. int main(int argc, char **argv)  
  8. {  
  9.     char buf[100];  
  10.     int x;  
  11.     x = 1;  
  12.     memset(daddr,'/0',16);  
  13.     printf("before format string x is %d/%#x (@ %p)/n", x, x, &x);  
  14.     strncpy(daddr,"PPPPPPP%n",9);         
  15.     snprintf(buf,sizeof(buf),daddr);   //实施格式化字符串攻击  
  16.   
  17.     buf[sizeof(buf) - 1] = 0;  
  18.     printf("after format string x is %d/%#x (@ %p)/n", x, x, &x);  
  19.     return 0;  
  20. }  

 

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

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

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

绘图1

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

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

Screenshot

 

 

 

http://blog.youkuaiyun.com/sealyao/archive/2010/10/23/5961330.aspx

格式化字符串漏洞攻击 格式化字符串漏洞攻击,主要有以下三点。 1 拒绝服务攻击 2 查看内存攻击 3 写入任意内存攻击 我们这要说一下危害最大的任何写入内存攻击。 注意:最新版的Linux,先关闭地址随机化保护和利用GCC 编译时打开-z execstack 代码如下: 如大家所见,我把存在漏洞的函数规定为折构函数(程序结束后,协助程序完成一些期望的工作),这样子的化,我们不到那可以重写内存,还可以执 阅读详情

相关推荐

程序员必看:如何用DDR5和缓存优化技术让你的代码跑得更快

本文深入探讨了如何利用DDR5内存和缓存优化技术提升程序性能,特别针对游戏开发和高性能计算中的内存等待问题。通过分析DDR5的架构改进、缓存优化技巧及多线程环境下的内存访问策略,为程序员提供了实用的性能优化方案,有效解决Memory-bound stalls等瓶颈问题。

weixin_30733003的博客 438

格式化字符串漏洞攻击/串字符格式化漏洞是什么-手把手教网络安全教程

看雪论坛作者ID:PIG-007

2401_84915584的博客 631

格式化字符串漏洞及其利用技术的深入分析

本文深入探讨了格式化字符串漏洞的原理和利用方法。首先介绍了格式化字符串漏洞的基本概念,然后通过实际代码示例展示了如何利用%n格式参数来实现对程序的控制。通过fmt_vuln.c和fmt_uncommon.c的代码解析,我们了解到通过控制格式字符串,攻击者可以读取和写入任意内存地址,甚至控制程序流程。本文的目的是提高程序员对格式化字符串漏洞的认识,并指导他们如何防范此类安全漏洞。

weixin_42588555的博客 566

格式化字符串攻击

什么是格式化字符串攻击? format string attack https://www.owasp.org/index.php/Format_string_attack 首先攻击发生在 格式化字符串所涉及的函数(例如 printf), 其次用户输入字符串提交后作为格式化字符串参数执行。 攻击者可以执行代码, 读取栈空间、 写栈空间、 导致进行段错误(segment fault),或者导...

weixin_34088838的博客 625

IC 时钟设计核心问题与高质量系统设计指南(附源码)

本文深入探讨了IC时钟设计的核心问题与高质量系统设计方法。时钟系统作为IC的"心脏",其性能直接影响时序稳定性、功耗效率和可靠性。文章从五个维度展开分析:时钟源选择与性能匹配、时钟分配网络设计、抖动抑制、多时钟域管理和功耗优化。针对高质量时钟系统设计,提出了时钟源优化、时钟树结构优化、干扰抑制与仿真验证等具体方法。同时,系统阐述了时钟与复位的协同设计关系,强调异步复位同步释放等技术的重要性。文章还总结了高质量IC系统设计的综合要点,包括电源/信号完整性、时序功耗平衡、可靠性设计及全流程验

Open 1060

芯片硅前验证技术与驱动工程师实战指南

硅前验证是芯片开发流程中的关键环节,通过pre-silicon simulation等技术手段在流片前验证设计正确性。其核心技术包括软件仿真、FPGA原型验证和硬件仿真器解决方案,涉及Linux驱动开发、芯片架构理解和波形分析等核心技能。作为连接芯片设计与系统集成的桥梁,硅前验证能早期发现设计缺陷,节省数百万美元流片成本。以Cadence Palladium为代表的硬件仿真器,通过专用服务器系统实现比纯软件方案快100-1000倍的仿真速度,为驱动工程师提供完整的外设模型验证环境。掌握硅前验证技术可显著提升

weixin_42522857的博客 321

格式化字符串漏原理调试解析

本文从格式化字符串函数,到格式化字符串漏洞原理,再到格式化漏洞利用包括(获取栈变量数值、获取栈变量对应字符串、泄露任意地址内存、覆盖栈内存)方面与大家分享,不足之处请各位大佬指正,最开始在wiki上面学的格式字符串,里面的源码就直接用了wiki上面的例子,加一些调试等等 0x00格式化字符串(函数): 格式化字符串函数为接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数。格...

weixin_45948183的博客 974

ArcGIS实战:地形湿度指数(TWI)计算全流程解析

本文详细解析了ArcGIS中地形湿度指数(TWI)的计算全流程,从DEM数据获取、填洼处理到水流方向与汇流累积量计算,最终实现TWI的精准计算。通过实战步骤指导,帮助读者掌握水文建模和土壤湿度预测的关键技术,适用于农业规划、生态研究等领域。

weixin_29231725的博客 869

c语言printf相关函数 格式化字符串攻击 简介

目录 一、类printf函数簇实现原理 二、格式化字符串攻击原理 三、一个实际的例子 一、类printf函数簇实现原理 类printf函数的最大的特点就是,在函数定义的时候无法知道函数实参的数目和类型。 对于这种情况,可以使用省略号指定参数表。 带有省略号的函数定义中,参数表分为两部分,前半部分是确定个数、确定类型的参数,第二部分就是省略号,代表数目和类型都不确定的参数表,省略号参数表中参数的个数和参数的类型是事先的约定计算出来的,每个实参的地址(指针)是根据确定参数表中最后一个实参的地址算出

whatday的专栏 910

慕课软件质量保证与测试(习题集)

慕课金陵科技学院.软件质量保证与测试.期末复习.习题汇总0 总目录1 绪论1.1 软件质量保证与测试的产生与发展1.2 软件缺陷,软件错误,软件故障1.3 软件质量保证与测试的意义,原则和挑战1.4 单元测试1.5 课后作业2 软件测试策略2.1 软件测试的模型,过程和生命周期2.2 软件测试的方法和技术2.3 单元测试2.4 课后作业3 黑盒测试3.1 黑盒测试概述3.2 等价类划分测试3.3 边界值测试3.4 错误推测法3.5 判定表驱动法3.6 因果图法3.7 场景法3.8 正交实验法3.9 单元测试

weixin_42473228的博客 5万+

使用 MATLAB/Simulink + Simscape Electrical + SimPowerSystems + Stateflow,构建一个多能互补微电网系统的动态响应仿真模型

本文详细介绍了使用MATLAB/Simulink构建多能互补微电网动态响应仿真模型的方法。文章从系统架构设计入手,包含光伏、风电、柴油发电机、锂电池储能等多种组件,重点讲解了建模过程、控制策略(下垂控制、PLL、SOC管理)和模式切换逻辑(Stateflow)。通过四种典型场景(负荷突增、光伏出力骤降、并网/孤岛切换、电动机启动)的仿真分析,验证了微电网在动态响应中的关键性能指标,并提出了高可靠性设计建议。该模型为微电网的稳定性分析和优化设计提供了有效工具,特别强调了储能系统在快速功率支撑中的重要作用。

amy_mhd的博客 159

格式化字符串的简单学习

通俗来说,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。这样就能泄露栈上第 25 个字节处的值 也就是这道题的 canary。这个地方有异或操作,而且之后就跳转回 main 函数,所以 [rbp-0x8] 应该就是栈中存放 canary 的地址。对于 2,3 来说倒还无妨,但是对于对于 1 来说,如果提供了一个不可访问地址,比如 0,那么程序就会因此而崩溃。在进入 printf 函数的之前 (即还没有调用 printf),栈上的布局由高地址到低地址依次如下。

akdelt的博客 2034

15张图详解三方支付:总架构、5大核心业务处理流程、备付金管理

用户可以将银行卡与零钱账户绑定,建立银行结算账户与支付账户的连接,然后将银行账户资金充值到零钱账户中,或将零钱账号中的余额提现至绑定的银行卡。因此,用户在A支付机构的零钱一般只能支付给A支付机构的商户,如支付宝的零钱不能支付给微信的商户。这五大业务的协同运作,使得第三方支付机构这一庞大的支付服务平台能够为市场提供卓越的支付能力。到了结算时间,网联会向备付金集中存管账户提交结算指令,在第三方支付机构备付金集中存管账户内完成最终的结算,并将结算结果及额度变动情况通知给第三方支付机构,具体流程如图13所示。

银行信息系统应用架构详解 1044

【Web狗自虐系列2】Pwn入门之格式化字符串漏洞

格式化字符串(Fromat String):在编码过程中,允许编码人员通过特殊的占位符,将相关对应的信息整合或提取的规则字符串。格式化字符串包括格式化输入和格式化输出 以printf()为例,第一个参数就是格式化字符串:“字符串 %s, 整数 %d, 浮点数 %f”,然后printf函数会根据这个格式化字符串来解析对应的其他参数%d /// 十进制-输出十进制整数%s /// 字符串-从内存中读取字符串%lx /// 十六进制-输出十六进制数。

就这样吧 1278

Android 15.0 Launcher3中去掉Hotseat功能实现

在15.0系统产品定制化开发中,在Launcher3的定制化需求也是特别多的,由产品是小屏幕的产品,所以产品需求Launcher3 页面布局的原因,要求Launcher3 需要去掉Hotseat 不显示Hotseat下面几个图标,而做满屏app的显示,从而达到美观的效果,下面就来分析去掉Hotseat从而实现这个功能

安卓兼职framework和app工程师的博客 554

《C++等级认证CCF-GESP真题解析》专栏总目录

❤️ 专栏名称:《C++等级认证CCF-GESP真题解析》🌸 专栏介绍:中国计算机学会GESP《CCF编程能力等级认证》C++编程(1~8级)历届真题解析。🚀 订阅专栏:订阅后可阅读专栏内所有真题解析,真题持续更新中,欢迎订阅!

人生就是一个不断学习的过程 6287

格式化字符串漏洞(Format String Vulnerability)

格式化字符串漏洞(Format String Vulnerability)是程序中因不当处理格式化字符串参数而导致的一类安全漏洞,常被攻击者利用来读取内存数据、篡改程序执行流程,甚至执行任意代码。

a12sj_的博客 1306

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Linux下的格式化字符串漏洞利用姿势

linux最早的漏洞防护机制nx-stack刚刚出现后就有人想出了突破方法。那就是只有栈是不可执行,而除了栈以外的其他地方还是可以执行的,只要把返回地址执行别的地方就可以。 一.格式化字符串漏洞 格式化字符串漏洞在Windows下很难以利用,但是在Linux下的pwn题目中出现的频率是很高的。 格式化字符串的“$”操作符,其允许我们从格式化字符串中选取一个作为特定的参数。例如...

weixin_30642029的博客 96

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

深入解析sprintf格式化字符串漏洞

0x01 sprintf()讲解首先我们先了解sprintf()函数sprintf() 函数把格式化的字符串写入变量中。sprintf(format,arg1,arg2,arg++) a...

蚁景网安学院 728

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

缓冲区溢出分类

基于应用视角的缓冲区溢出检测技术 一.缓冲区分类: 按照缓冲区所在内存区域位置分类: 栈溢出 堆溢出 数据段溢出 按照导致溢出的内存操作函数分类: 字符串操作函数(strcpy) 格式化输出函数(sprintf) 按照溢出数据修改的关键值分类: 修改返回地址 修改函数指针 修改指针变量 二.栈溢出 基于栈溢出的攻击Stack Smashing:

阿航的博客 2691
上一篇: iptables/netfilter命令、实现及利用
下一篇: linux 下缓冲区溢出攻击原理及示例
ShenRui
博客等级 码龄20年 63粉丝 36原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值