知识星球2023年10月PHP函数小挑战

本文介绍了PHP脚本的执行过程,重点分析了函数所在作用域对opline的影响,以及如何绕过trim过滤。在PHP7.4环境下,由于函数在非顶级作用域编译时生成不同的函数名,导致调用方式的变化。通过理解这一机制,成功解决了一个代码审计挑战,而在PHP8.1及以上版本,这一特性已被删除,以避免内存泄漏问题。
摘要由CSDN通过智能技术生成

本文于10月13日发表在我的博客。

前几天在『代码审计』知识星球里发了一个小挑战:https://t.zsxq.com/13bFX1N8F

<?php
$password = trim($_REQUEST['password'] ?? '');
$name = trim($_REQUEST['name'] ?? 'viewsource');
function viewsource() {show_source(__FILE__);}

if (strcmp(hash('sha256', $password), 'ca572756809c324632167240d208681a03b4bd483036581a6190789165e1387a') === 0) {
    function readflag() {
        echo 'flag';
    }
}

$name();
?>

执行环境是PHP7.4,目标是读取到flag。

这段代码非常简单,我加了一些迷惑因素,比如trimstrcmphash之类的函数,但实际上核心与这些干扰因素没关系,我们来简单做个分析。

PHP脚本执行过程理解

我并不是C语言和PHP底层原理的专家,这里只能用一些简单的语言来描述PHP脚本编译执行的过程。

就如其他大部分脚本语言一样,PHP的执行分为两部分:

  • 源代码编译成Zend虚拟机指令(PHP中叫opline)的过程

  • Zend虚拟机执行机器指令的过程

其中前者又会被分为下面几个步骤:

  • 调用zendparse完成词法分析、语法分析,生成AST树

  • 调用init_op_array, zend_compile_top_stmt来完成AST到opline数组的转化

  • 调用pass_two完成编译时到运行时信息的转化,设置每个opcode对应的handler

后者拿到编译完成后的opline array,依次执行每个opcode,其实就是执行每个opcode对应的handler,完成PHP脚本的执行。我们参考我在『代码审计』星球里分享过的远程调试ZendVM的方法,找到zend_execute_scripts函数,你即可看到大致的逻辑:

94d74ef91f8081d3678b85a269174007.png

我们要关注的是PHP代码的编译阶段。PHP在编译“函数定义”的时候,会使用zend_compile_func_decl函数:

void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {
   {
   { */
{
    ...
    zend_ast_decl *decl = (zend_ast_decl *) ast;
    zend_bool is_method = decl->kind == ZEND_AST_METHOD;
    if (is_method) {
  zend_bool has_body = stmt_ast != NULL;
  zend_begin_method_decl(op_array, decl->name, has_body);
 } else {
  zend_begin_func_decl(result, op_array, decl, toplevel);
  if (decl->kind == ZEND_AST_ARROW_FUNC) {
   find_implicit_binds(&
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值