牛骨文教育服务平台(让学习变的简单)
博文笔记

从源码级别了解PHP %00截断原理

创建时间:2017-10-10 投稿人: 浏览次数:872

    PHP的00截断是5.2.x版本的一个漏洞,当用户输入的url参数包含%00经过浏览器自动转码后截断后面字符。

    例如url输入的文件名1.txt%00.jpg经过url转码后会变为1.txt00.jpg,测试文件1.php如下
<?php
    include "1.txt00.jpg";
?>
    测试文件1.txt如下
<?php
    echo "fireXXX";
?>
    php5.2.x版本解析1.php时,会将1.txt00.jpg解释为1.txt     php5.2.17代码分析:     调用php去解析1.php文件。     主要的执行流程在Zend/zend.c的zend_execute_scripts函数中。该函数首先通过zend_compile_file获取1.php文件的内容,然后调用zend_execute解析读取到的文件内容。     zend_compile_file函数首先调用open_file_for_scanning去读取文件,然后通过zendparse去进行语法和词法解析,而zendparse是通过lex_scan去扫描出token并进行语法分析。可以通过调试器观察到include的文件名参数经过lex_scan后的数据:          因此实际上解析到的文件名是1.txt,长度为10(因为lex_scan扫描到了1.txt00.jpg实际是10个字符)     zend_execute通过ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER函数来进行include的实际处理,即包含要包含的文件。首先通过compile_filename来读取要读取文件的内容,然后继续调用zend_execute去执行要包含文件的代码。针对include "1.txt";即读取1.txt的内容,然后执行echo "fireXXX";代码。
    可以从php5.3.24代码中找到修复的方案。修复的代码位于ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER函数的开始处:
        if (Z_LVAL(opline->op2.u.constant) != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) {
		if (Z_LVAL(opline->op2.u.constant)==ZEND_INCLUDE_ONCE || Z_LVAL(opline->op2.u.constant)==ZEND_INCLUDE) {
			zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
		} else {
			zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename) TSRMLS_CC);
		}
	}
    代码
strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)
    中,Z_STRVAL_P(inc_filename)即上图中的val,即"1.txt",strlen取得长度为5,而Z_STRLEN_P(inc_filename)即上图中的len即10。     一旦出现%00截断,include的文件名经过url转码由"1.txt%00.jpg"变为"1.txt00.jpg",进入php语法词法分析器解析后会将这个字符串解析成一个字符串,并使用zend_scan_escape_string进行字符串转码,如图,进入zend_scan_escape_string的内容为:
    中间的\000还被解析为4个字符,转码中会将他当作八进制数据转成一个字符,因此最终1.txt00.jpg长度是10。     只要比较发现文件名的strlen长度和语法分析出来的长度不一样,就说明内部存在截断的字符,因此输出了打开文件失败的信息。
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。