学习静态代码审计_第二站:简单解读php-parser项目


前言

  第一次接触这个项目,是2021强网杯的pop-master题目,也是这道题目给我打开了静态代码审计的大门。后来了解静态代码审计的发展历程,又接触到了昆仑镜项目。php-parse项目都下载三遍了,这次要搞定她!

  PHP Parser 是由 nikic 开发的一款 php 抽象语法树(AST)解析工具。项目地址:https://github.com/nikic/PHP-Parser。

  项目作者:nikic是PHP的主要contributor,目前在Jetbrains旗下的PhpStorm项目工作,并且开发过许多PHP的开源库。他也是LLVM项目的开发者。


项目官方说明

0x01 PHP解析器

  使用PHP编写的适合 php5.2 - php8.0 的解析器,用于简化静态代码分析和操作。

  Documentation for version 4.x (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.0),文档地址:https://github.com/nikic/PHP-Parser/tree/master/doc.

  Documentation for version 3.x (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2),文档地址:https://github.com/nikic/PHP-Parser/tree/3.x/doc.

0x02 功能

  主要功能如下,其中前4项功能相比之下更具可用性。

  • 把PHP5、PHP7、PHP8的代码转换成抽象语法树AST
    • 无效代码会被解析成一个部分抽象语法树
    • 生成的AST包含准确的位置信息
  • 使用可读性高的表格转储生成的AST
  • 把AST转回PHP代码
    • 试验的:修改后的AST可以被格式化保存
  • 遍历和修改AST
  • 命名空间名称的解析
  • 常数表达式的求值
  • 简化AST结构的构造器 for 代码生成
  • 来回转换抽象语法树和Json数据
0x03 快速开始

  (1)使用PHP依赖管理工具Composer安装,由于本地Composer是全局配置,所以命令有所不同。

	composer require nikic/php-parser
	
	cd ./vendor/nikic/php-parser/

在这里插入图片描述

  (2)运行测试代码:nikic给出的代码示例如下,使用该软件,只需要把需要审计的代码放到$code变量中即可。

<?php
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;

$code = <<<'CODE'
<?php

function test($foo)
{
    var_dump($foo);
}
CODE;

$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}\n";
    return;
}

$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";

  测试:在nikic/php-parser/lib/目录下新建MyASTest.php,复制粘贴示例代码,使用测试代码替换7-12行的代码。

<?php
var_dump(token_get_all('<?php echo "hello"; $a=1;echo $a+$a;?>'));
?>

  报错信息:使用PHPStorm打开MyASTest.php文件,使用PHP7.3.24命令解释器,发生报错。(完全使用示例代码也有相同报错)
在这里插入图片描述

PHP Fatal error:  Uncaught Error: Class 'PhpParser\ParserFactory' not found in /Users/didi/Downloads/vendor/nikic/php-parser/lib/MyASTest.php:15
Stack trace:
#0 {main}
  thrown in xxx.php on line 15

  网上并没有找到明确的解决方式,但通过查阅use相关资料得知,use只是使用了命名空间,但是要想调用类,必须要加载类文件,或者自动加载。根据报错信息可以知道,use起作用了,但class类没有引用成功。

  尝试1-可行:使用自动加载。autoload.php文件在vendor目录下,通过目录遍历去包含该文件。比如本地在MyASTest.php文件第5行添加的代码如下,运行结果如图。

	require_once __DIR__ . '/../../../autoload.php';

在这里插入图片描述

  (3)遍历AST并执行某种操作,比如丢掉抽象语法树AST中某个函数的所有函数体。
  使用Class NodeTraverser,运行相关代码之后可以发现,只保留了Stmt_Function数组,stmts数组的内容被清空了。这意味着,我们可以对得到的AST语法树进行比较灵活的操作。(我愿称之为二次操作)

  (4)把得到的AST语法树转回PHP代码。
这个步骤很适合Webshell检测。使用PrettyPrinter文件中的Class PrettyPrinter\Standard,但是作者给出的例子中,却没有还原函数体,具体点就是 减去了函数中var_dump()的调用。该软件还原代码的可用性有多大,还需要进一步探究。

  (5)For a more comprehensive introduction, see the documentation。

0x04 文档

  基础组件的使用这部分,其实相当重要。但持恒之初忌深远,贪多嚼不烂,后续更文再深入分析,一定记得多读文档方能熟能生巧。

  1. Introduction
  2. Usage of basic components

  下一篇学习研究PHP-Parse的使用,通过聚焦某些应用场景,比如基于该软件寻找强网杯pop-master题目的RCE pop链、探索该软件在代码审计领域的应用场景等。希望可以做到对这个PHP编写的PHP解析器项目烂熟于心,成为迈向代码安全领域坚实的一步。


参考

  《PHP Parser 简介和应用 - 为你的代码自动补全单元测试》,文章地址:https://learnku.com/articles/21846。

  《PHP中的use、命名空间、引入类文件、自动加载类的理解》,文章地址:https://blog.csdn.net/github_37767025/article/details/68064974

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个早期的 PHP 解析器,相当于实现了 PHPPHP 脚本的解析。示例代码:<?php // Autoload required classes require "vendor/autoload.php"; // Instantiate new parser instance $parser = new PhpParser\Parser(); // Return and print an AST from string contents $astNode = $parser->parseSourceFile('<?php /* comment */ echo "hi!"'); var_dump($astNode); // Gets and prints errors from AST Node. The parser handles errors gracefully, // so it can be used in IDE usage scenarios (where code is often incomplete). $errors = PhpParser\Utilities::getDiagnostics($astNode); var_dump(iterator_to_array($errors)); // Traverse all Node descendants of $astNode foreach ($astNode->getDescendantNodes() as $descendant) {     if ($descendant instanceof \PhpParser\Node\StringLiteral) {         // Print the Node text (without whitespace or comments)         var_dump($descendant->getText());         // All Nodes link back to their parents, so it's easy to navigate the tree.         $grandParent = $descendant->getParent()->getParent();         var_dump($grandParent->getNodeKindName());         // The AST is fully-representative, and round-trippable to the original source.         // This enables consumers to build reliable formatting and refactoring tools.         var_dump($grandParent->getLeadingCommentAndWhitespaceText());     }     // In addition to retrieving all children or descendants of a Node,     // Nodes expose properties specific to the Node type.     if ($descendant instanceof \PhpParser\Node\Expression\EchoExpression) {         $echoKeywordStartPosition = $descendant->echoKeyword->getStartPosition();         // To cut down on memory consumption, positions are represented as a single integer          // index into the document, but their line and character positions are easily retrieved.         $lineCharacterPosition = \PhpParser\Utilities::getLineCharacterPositionFromPosition(             $echoKeywordStartPosition         );         echo "line: $lineCharacterPosition->line, character: $lineCharacterPosition->character";     } } 标签:Tolerant

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值