您当前的位置: 首页 > 

合天网安实验室

暂无认证

  • 0浏览

    0关注

    748博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

合天网安实验室 发布时间:2020-03-24 10:33:31 ,浏览量:0

0x01 sprintf()讲解 首先我们先了解sprintf()函数

sprintf() 函数把格式化的字符串写入变量中。

sprintf(format,arg1,arg2,arg++)
arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 "\$" 组成。
通过几个例子回顾一下sprintf

例子1:


输出结果:
带有两位小数:123.00 
不带小数:123

例子2:




输出结果:
%b = 111010110111100110100010101
%c = 2  //注意var_dump('2')为string
%s = 123456789
%x = 75bcd15
%X = 75BCD15
0x02 sprintf注入原理 底层代码实现

我们来看一下sprintf()的底层实现方法

switch (format[inpos]) {
case 's': {
zend_string *t;
zend_string *str = zval_get_tmp_string(tmp, &t);
php_sprintf_appendstring(&result, &outpos,ZSTR_VAL(str),width, precision, padding,alignment,ZSTR_LEN(str),0, expprec, 0);
zend_tmp_string_release(t);
break;
    }
    case 'd':
        php_sprintf_appendint(&result, &outpos,
                              zval_get_long(tmp),
                              width, padding, alignment,
                              always_sign);
        break;


    case 'u':
        php_sprintf_appenduint(&result, &outpos,
                              zval_get_long(tmp),
                              width, padding, alignment);
        break;


    case 'g':
    case 'G':
    case 'e':
    case 'E':
    case 'f':
    case 'F':
        php_sprintf_appenddouble(&result, &outpos,
                                 zval_get_double(tmp),
                                 width, padding, alignment,
                                 precision, adjusting,
                                 format[inpos], always_sign
                                );
        break;


    case 'c':
        php_sprintf_appendchar(&result, &outpos,
                            (char) zval_get_long(tmp));
        break;


    case 'o':
        php_sprintf_append2n(&result, &outpos,
                             zval_get_long(tmp),
                             width, padding, alignment, 3,
                             hexchars, expprec);
        break;


    case 'x':
        php_sprintf_append2n(&result, &outpos,
                             zval_get_long(tmp),
                             width, padding, alignment, 4,
                             hexchars, expprec);
        break;


    case 'X':
        php_sprintf_append2n(&result, &outpos,
                             zval_get_long(tmp),
                             width, padding, alignment, 4,
                             HEXCHARS, expprec);
        break;


    case 'b':
        php_sprintf_append2n(&result, &outpos,
                             zval_get_long(tmp),
                             width, padding, alignment, 1,
                             hexchars, expprec);
        break;


    case '%':
        php_sprintf_appendchar(&result, &outpos, '%');


        break;
    default:
        break;
}

可以看到, php源码中只对15种类型做了匹配, 其他字符类型都直接break了,php未做任何处理,直接跳过,所以导致了这个问题:没做字符类型检测的最大危害就是它可以吃掉一个转义符, 如果%后面出现一个,那么php会把\当作一个格式化字符的类型而吃掉, 最后%\(或%1$\)被替换为空

因此sprintf注入,或者说php格式化字符串注入的原理为: 要明白%后的一个字符(除了%,%上面表格已经给出了)都会被当作字符型类型而被吃掉,也就是被当作一个类型进行匹配后面的变量,比如%c匹配asciii码,%d匹配整数,如果不在定义的也会匹配,匹配空,比如%\,这样我们的目的只有一个,使得单引号逃逸,也就是能够起到闭合的作用。

这里我们举两个例子 NO.1

不使用占位符号

            
关注
打赏
1665306545
查看更多评论
0.0402s