0×01起因是有一天我师弟在群里说了上传%00截断的一些问题,想起来之前踩过这个问题,我记得我之前的flag说我想写一篇文章,但是一直没写出来,现在来填坑了。0×02了解源码后://test.php//1.txtphp版本低于5.3.4的情况下,会输出helloworld。从php的内核执行过程来看,PHP是通过php_execute_script来执行PHP脚本的,下面是一些相关的代码,可以看这里:第10行,我们可以看到他调用了zend_execute_scripts来解析脚本,这个函数在Zend/zend.c,截取的相关代码部分如下:从PHP核心来看,其实分为两部分,一是编译过程,二是执行过程。第一部分:编译过程我们可以看到这里的代码逻辑是通过zend_compile_file获取文件内容的,zend_compile_file是一个函数指针,它的声明在/Zend/zend_compile.c中ZEND_APIzend_op_array(zend_compile_file)(zend_file_handle*file_handle,int类型TSRMLS_DC);引擎初始化时,compile_file函数的地址会被赋值给zend_compile_file。/Zend/zend_language_scanner.l中定义了compile_file函数,截取了一些相关代码。简单总结一下以上部分代码的逻辑:zend_compile_file函数首先调用open_file_for_scanning读取文件,然后在第17行通过zendparse进行语法和词法分析。而zendparse使用lex_scan扫描token并进行语法分析。第二部分:execute执行过程zend_execute也是一个函数指针,声明在/Zend/zend_execute.h中。ZEND_APIexternvoid(zend_execute)(zend_op_arrayop_arrayTSRMLS_DC);当引擎初始化时,execute函数的地址会被赋值给zend_execute。execute的定义在/Zend/zend_vm_execute.h中,根据我们的理解,zend_execute是通过ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER函数进行include的实际处理,即include要包含的文件。对比修复代码,找到漏洞的触发点:提取部分修复代码:先看一下带漏洞的调试结果:修复代码的Z_STRVAL_P(inc_filename)就是上图中的val,即“1.txt”,strlen的长度为5。而Z_STRLEN_P(inc_filename)就是上图中的len,为10。这里实际解析出来的文件名是1.txt。无漏洞调试结果:%00被截断后,include文件名由“1.txt%00.jpg”经过url转码后变为“1.txt000.jpg”,然后通过php语法词法分析器解析这个string会被解析成字符串,然后使用zend_scan_escape_string对字符串进行转码。如图,输入zend_scan_escape_string的内容是:只要文件名的strlen长度和语法分析的长度不一样,就说明存在内部截断字符,从而输出打开失败文件。PHP版本低于5.3.4%00截断的使用方法有两种用例1.在url中添加%00,比如http://xxxx/shell.php%00.jpg2.在burpsuite的十六进制编辑工具将“shell.php.jpg”(带空格)之间的空格由20改为00。1中,url中的%00(如%xx),web服务器将其视为十六进制处理,然后将十六进制数据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()函数在转换编码时会截断非法字符串。通过fuzz找到,其中iconv("UTF-8","gbk",$a)或者iconv("UTF-8","gb2313",$a)都会到达chr(128)Chr(255)被截断了,这样的结果是shell.php在php5.4.0版本没有这个问题。实际例子关于刚才我们提到的iconv()截断的问题,其实sitestarpro就是一个典型的例子。我们来看一个例子:漏洞触发点在module/mod_tool.php中的img_create()函数中,截取的部分代码如下:这部分有一段代码引起了我的注意if(!preg_match('/.('.PIC_ALLOW_EXT.')$/i',$file_info["name"])){Notice::set('mod_marquee/msg',__('文件类型错误!'));内容::重定向(Html::uriquery('mod_marquee','upload_img'));允许使用gif|jpg|png|bmp等文件。即如果文件名末尾不是gif|jpg|png|bmp,会提示文件类型错误。所以这部分正则表达式的作用应该是检查文件扩展名。继续跟进这部分代码,有一串代码如下:if(!$this->_savelinkimg($file_info)){Notice::set('mod_marquee/msg',__('Linkimageupload失败的!'));内容::redirect(Html::uriquery('mod_marquee','upload_img'));}这部分代码应该是保存文件的功能。在这个函数之后还有一个核心函数_savelinkimg。第二行的问题$struct_file['name']=iconv("UTF-8","gb2312",$struct_file['name']);在iconv转码的过程中,utf->gb2312(其他部分编码之间的转换也存在这个问题)会导致字符串被截断,如:$filename=”shell.php(hex).jpg”;(hex为0×80-0×99),iconv转码后会变成$filename=”shell.php”;所以,在iconv之后,$struct_file['name'])就是shell.php,所以利用这个逻辑缺陷我们可以成功上传shell.php(前提是上传的文件名为shell.php{%80-%99}。.jpg).0×03SummaryofresultsTruncation可以应用于以下几种情况:1.include(require);2.文件获取内容;3.文件存在;4、url中的所有参数都可以通过%00来控制。0×04参考资料从源码层面理解PHP%00截断原理。隐藏在PHP手册背后的特性和漏洞(见雪峰专题)作者:CanMengBlog来源:CSDN原文:https://blog.csdn.net/weixin_..。版权声明:本文为博主原创文章,转载请附上博文链接!
