抽时间帮助学校的队伍打了下上海大学生信息安全竞赛,题目质量还可以,最后ak了web题;有一道题利用sprintf经典格式化字符串漏洞进行逃逸单引号达到盲注的效果,最开始没想出来是这个点,后来本地测试了下发现是可以绕过的,发生在password处;
sprintf函数将格式化的字符输入到变量中;
1 | sprintf(format,arg1,arg2,arg++) |
arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
下面举两个例子看:
1 |
|
1 |
|
这里因为我们百分号的数量大于我们需要传入参数的数量,所以这里需要使用占位符;占位符通常是由 数字+"\$"
组成;
鲜明的标志是补位符在单引号后面; ‘ 号(单引号)代表接下来要用补位类型
补位符有或者没有其实都问题不大;来看下面的两个对比的代码;
1 |
|
1 |
|
通过这两个例子可以鲜明的对比,补位符只是起到了补充未够位数的作用,例子二就是拿x作为补位符;从而实现了x进行补位以达到例子二的输出效果;
sprintf对未知格式的判断,造成的漏洞;先来看下文档,里面记述了sprintf可以采用那些格式:
那么除了这些格式之外,php如果遇到了未知的格式,那么就会舍弃,这点在php底层源码中有体现;
1 | switch (format[inpos]) { |
底层源码中对sprintf可以处理的字符格式做了鲜明的处理方法,但是对未知的字符格式则是直接进行了break;那么就会出现舍弃字符的现象,最后会导致引号逃逸(后续细说
看如下的例子:
1 |
|
上述的例子可以正常地回显,但是如果我们传入一个未知的字符处理格式,那又会如何呢,在底层源码中我们可以发现,php会进行break从而舍弃;看下实例;
1 |
|
上述例子的回显效果为s;说明了我们前面的非法的字符格式已经被break了,也就是我们的%1$a被舍弃,所以直接输出了s;写如下例子:
1 |
|
我们看到如上的例子,会输出aaa,不难分析,因为我们前面的非法处理类型遭到的break舍弃,所以只剩下%s,所以自然正确的格式化输出了aaa;
这里如果将我们的非法格式化类型换成数字,那么也就是规范的格式化,因为%1$10s表示的是最后格式化第一个参数,接着保留十位字符,格式为字符串;这里不在过多的赘述;这种写法也就和之前讲的填充字符结合到一起了;因为位数不够的时候自然是需要进行填充,但是也可以没有填充字符,没有的时候默认是用空格进行填充;
经过我们上述的描述,我们会发现sprintf会进行字符的break;并且没有对字符有任何危险的处理,就简单的break;那么漏洞的点就是在这里触发;
来看如下的代码;
1 |
|
通过这个例子我们可以看到,因为经过了addslashes函数的处理,我们的引号前会被默认的加入反斜杠进行转移,但是我们反斜杠在sprintf函数中可以错误引用从而被break掉;没有了反斜杠的转义,我们的引号自然会逃逸出来;从而进行sql的闭合达到sql injection;
构造单引号,利用sprintf的格式化特性,可以进行单引号的引入;简单来说如果后面格式化的参数为数字的话,我们可以通过%c来引入进行转码,从而进入单引号,
1 |
|
这种类型在有些专门设计的sql注入题中会有出现;简单的提一下,当作一个分享;
上海大学生信息安全竞赛的web2,是一个sql注入,盲注;漏洞点发生在class.php中
1 | $sql="select * from user where username='%s' andpassword='$password'"; |
通过表单提交username和password,然后后续php代码处理,发现sql中格式化输入了username,这里并没有漏洞点可利用,但是password是没有经过格式化传入的,我们可以在password处利用非法的字符格式使其break掉,然后逃逸出单引号;因为传入的数据经过了addslashes;所以漏洞利用就更加明显了,直接嵌入 %1$’ || 1=1#;
这里经过aaddslashes函数处理之后,会在单引号前加入反斜杠,那么对于sprintf函数那就成了非法的字符格式,根据底层代码实现,会将其整体break;最后也就自然留下了单引号;然后闭合进行注入;
贴上一张在比赛时候的盲注测试图:
回显登陆正确;原因看如下测试图:
可以让单引号逃逸出来,一个简单的分析,不是很难理解;
这个漏洞比较的老版,版本需要小于4.7.5,经过测试在4.7.5中已经做了修补;现实的攻击意义不大,但是还是讲述一下;首先漏洞发生在删除图片的地方;
从upload.php处入手,发现post_id_del未经过任何处理就进入了程序的运作;
1 | case 'delete': |
看到delete选块,简单理解代码,提交需要删除图片的id;然后进行权限检查,然后进入了一个未知的wp_delete_attachment函数,追踪函数,在post.php中发现了函数的定义;并且调用了个 delete_metadata 函数,
继续跟进delete_metadata函数; 漏洞触发点主要在wp-includes/meta.php 的 delete_metadata函数里面 ;
发现调用了prepare函数,继续跟进函数发现有段操作;贴出来;
1 | $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it |
这里分析一下,先将’%s’ 替换成了 %s 然后将 “%s”题换成了 %s 然后强制转化为浮点%F,把%s替换为’%s’,最后进行vsprintf格式化输入然后return;
我们拉到本地进行复现下
加入我们传入的meta_value 为 admin’ 进入这个语句;$wpdb->prepare( “ AND meta_value = %s”, $meta_value );
query经过prepare处理之后,最终变成 vsprintf(“ AND meta_value = ‘’%s’”, $meta_value);所以这里直接拼接后得到 and meta_value = ‘admin’;再return后;经过prepare函数处理后再得到;vsprintf( “SELECT $type_column FROM $table WHERE meta_key = ‘%s’ AND meta_value = ‘admin’”,’admin’)=> SELECT $type_column FROM $table WHERE meta_key = ‘admin’ AND meta_value = ‘admin’;语句无错误;
但是我们来看一下我们可控变量的走向; $post_id_del => $post_id => $meta_value => $args => $query
我们再回顾一下str_replace的替换,有一处是将’%s’替换为%s,然后再最后的时候又将%s替换成了’%s’,所以这两次替换没有什么实质性的干扰;
1 | if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) { |
这里我们看处理后的一段流程,先进行了一次prepare处理,,然后再次经过prepare处理之后进行二次拼接,这里我们可控的变量再二次拼接的时候依然是可控的,那么我们如果想造成sql注入,那么这里就需要引入引号,让其再$value_clause处进行闭合,然后执行sql语句;
本地测试,经过第一次替换之后,$value_clause为 AND meta_value = ‘$meta_value’;如下图所示;
然后第二次替换的时候我们可以想办法引入引号,引号的引入就在我们的prepare函数的处理方式处,其处理将%s替换为’%s’;这里就是我们引号的引入点,看如下实例分析;
这里如果我们引入一个非法的字符格式进行处理;在文章的最开始就讲述了非法的字符格式会被break掉,那么我们这里就可以利用;如果我们传入 %1$%s AND SLEEP(5)#
;这里引入的很巧妙,因为进行了替换的原因,我们在第二次的prepare处会变成 %1$'%s' AND SLEEP(5)#
;这里由于发生了替换导致%1$'
成为了非法的字符格式;从而导致其被break掉,那么只剩下%s;我们后边的引号自然的会逃逸出来,进行闭合,然后and sleep进行时间延迟;
其实这个漏洞言简意赅的总结一下,也就是利用vsprintf格式化字符串引入前后引号,然后利用非法的字符格式break掉前引号,然后后引号直接闭合继续后面拼接and sleep进行延时,sleep后的引号是再第一次的prepare处引入的,所以利用的很完美,造成时间延迟,可以进行注入;