php strlen遇0截断,聊下php下的截断问题

0x01 起因

有天在群里说起上传的%00截断的一些问题,就想起之前自己在这个问题踩过坑,想起了自己曾经的flag说要写文章,一直没写,现在来填坑了。

0x02 经过

源码理解1

2

3

4//test.php

include "1.txt\000.jpg";

?>

1

2

3

4//1.txt

echo 'helloworld';

?>

上面的示例代码在 php版本小于5.3.4 的情况下回输出 helloworld 。从php的内核执行过程来看,PHP通过 php_execute_script 来执行PHP的脚本,这里选取部分有关代码,具体可以看这里:

ac601ec48a215f1b25df7d2d08c4e70c.png

在 第10行 我们看到,他调用 zend_execute_scripts 来针对脚本进行解析,而这个函数是在Zend/zend.c里面,截取部分相关代码如下:

357e0c3358b7a7b2a4938f46952a268e.png

从PHP内核开来实际上是分为两块部分,一个是 compile编译过程 ,另一个是execute执行过程。

第一部分:compile编译过程

我们可以看到这里的代码逻辑通过 zend_compile_file 获取文件的内容,zend_compile_file是一个函数指针,其声明在/Zend/zend_compile.c中

1ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);

在引擎初始化的时候,会将 compile_file 函数的地址赋值给 zend_compile_file 。

12d4b251c83cda4c68e269881613c93b.png

1458926186ddef54df38b89d6a266b60.png

简单总结一下上面部分代码的逻辑:

zend_compile_file 函数首先调用 open_file_for_scanning 去读取文件,然后通过 第17行的zendparse 去进行语法和词法解析。而 zendparse 是通过 lex_scan 去扫描出token并进行语法分析。

第二部分:execute执行过程

zend_execute 也是一个函数指针,其声明在/Zend/zend_execute.h中。

1ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);

在引擎初始化的时候,会将 execute 函数的地址赋值给 zend_execute 。

e65bc7551444fe89dd56da363a7bdd5e.png

0a05af4ac9fd46799be09c77c3d57b0e.png

根据我们的了解,zend_execute 通过 ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER 函数来进行include的实际处理,即包含要包含的文件。

674501f16472425276f7be49aa10423a.png

对比修复代码找到漏洞触发点:

0ca57139e42bc3a0aebf91ef26f9ad50.png

摘出部分修复代码:

e0b3e1b4801265e16bc0cb8a45d630a0.png

我看下存在漏洞的调试运行结果:

cb8da8ca5bc6a484c8eef4403ba1f424.png

修复代码的 Z_STRVAL_P(inc_filename) 即上图中的val,即”1.txt”,strlen取得长度为5,而 Z_STRLEN_P(inc_filename) 即上图中的len即10。这里实际上解析到的文件名是1.txt。

不存在漏洞的调试运行结果:

一旦出现%00截断,include的文件名经过url转码由”1.txt%00.jpg”变为”1.txt\000.jpg”,进入php语法词法分析器解析后会将这个字符串解析成一个字符串,并使用 zend_scan_escape_string 进行字符串转码,如图,进入 zend_scan_escape_string 的内容为:

db2e6c11541a1fc9c8aae3a237a25488.png

只要比较发现文件名的strlen长度和语法分析出来的长度不一样,就说明内部存在截断的字符,因此输出了打开文件失败的信息。

利用方式

划重点 PHP版本低于5.3.4

%00截断有这么2种利用状况

在burpsuite的16进制编辑工具将”shell.php .jpg”(带空格的)中间的空格由20改成00

在1中,url中的%00(形如%xx),web server会把它当作十六进制处理,然后将该十六进制数据hex(00)“翻译”成统一的ascii码值“NUL(null)”,实现了截断。

在2中,burpsuite用burp自带的十六进制编辑工具将”shell.php .jpg”(中间有空格)中的空格由20改成00,如果burp中有二进制编辑工具。

延伸一下

其实关于截断相关问题,还有个很有趣的函数, iconv() 函数:

在了解 iconv() 函数漏洞之前,可能需要一点前置知识

在php中,所有的字符都是二进制的串,PHP本身并不认识任何编码,只是根据编码来显示内容。PHP中的chr() 函数从指定的 ASCII 值返回字符。ASCII 值可被指定为十进制值、八进制值或十六进制值。八进制值被定义为带前置 0,而十六进制值被定义为带前置 0x。

而在php5.4之前, iconv() 函数在转换编码的时候,遇到不合法的字符串的时候会将其截断。

1

2

3

4

5

6

7<?php

for($k=0;$k<=255;$k++)

{

$a='shell.php'.chr($k)."1.jpg";

echo 'k:'.$k.' '.'$a:'.$a.' '.'iconv("UTF-8","gbk",$a):'.iconv("UTF-8","gbk",$a)."\n";

}

?>

通过fuzz发现,其中 iconv(“UTF-8”,”gbk”,$a) 或是 iconv(“UTF-8”,”gb2313”,$a) 都会在chr(128)到chr(255)之间截断,使结果为shell.php

31d52adc37319daae2e604ece3eac955.png

在php5.4.0版本的时候就不存在这个问题了。

495a8662891d4bd26ab7b8843f73f2d8.png

实际例子

关于我们刚刚说的 iconv() 截断的问题,其实sitestar pro就是个典型例子,我们看个例子:

漏洞触发点在 module/mod_tool.php 中的 img_create() 函数,截取部分代码如下:

3ba0b6909f4f9079c53aa4c721d509cd.png

这部分中有段代码吸引了我的注意力

1

2

3

4if(!preg_match('/\.('.PIC_ALLOW_EXT.')$/i', $file_info["name"])) {

Notice::set('mod_marquee/msg', __('File type error!'));

Content::redirect(Html::uriquery('mod_marquee', 'upload_img'));

}

跟进一下这个 PIC_ALLOW_EXT 参数,发现其在数据库中写死了,只允许 gif|jpg|png|bmp 这类文件。即如果文件名最后不是 gif|jpg|png|bmp ,则提示文件类型错误。

017dcd393e0cb270e28216d3a9b34c67.png

所以这部分的正则表达式的功能应该是针对文件后缀名进行检查。

继续跟进这部分代码,有一串代码如下:

1

2

3

4if (!$this->_savelinkimg($file_info)) {

Notice::set('mod_marquee/msg', __('Link image upload failed!'));

Content::redirect(Html::uriquery('mod_marquee', 'upload_img'));

}

这部分代码应该是进行文件保存的功能,这个有个核心函数 _savelinkimg ,跟进这个函数。

aa5f1013b85e1b1761ddc78bab754107.png

第二行问题出现了

1$struct_file['name'] = iconv("UTF-8", "gb2312", $struct_file['name']);

在 iconv 转码的过程中, utf->gb2312 (其他部分编码之间转换同样存在这个问题)会导致字符串被截断,如:$filename="shell.php(hex).jpg; (hex为0x80-0x99),经过 iconv 转码后会变成 $filename="shell.php" ;

4cc2145b0df9dfa15af09d9dc9e24f26.png

0x03 结果

总结一下截断大概可以在以下情况适用

include(require)

file_get_contents

file_exists

所有url中参数可以用%00控制

0x04 参考文献

潜伏在PHP Manual背后的特性及漏洞(看雪峰会议题)

wooyun-2014-048293

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值