2022DASCTF Apr X FATE复现
warmup-php
一到反序列化的题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php spl_autoload_register(function($class){ require("./class/".$class.".php"); }); highlight_file(__FILE__); error_reporting(0); $action = $_GET['action']; $properties = $_POST['properties']; class Action{
public function __construct($action,$properties){
$object=new $action(); foreach($properties as $name=>$value) $object->$name=$value; $object->run(); } }
new Action($action,$properties); ?>
|
index.php会新建一个类,类名我们可控,但是该类中需要有run方法,并且我们还可以传入一个数组给类中的属性赋值,接下来看看class文件
filter.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| <?php
class Filter extends Base {
public $lastModified;
public $lastModifiedExpression;
public $etagSeed;
public $etagSeedExpression; public $cacheControl='max-age=3600, public';
public function preFilter($filterChain) {
$lastModified=$this->getLastModifiedValue(); $etag=$this->getEtagValue();
if($etag===false&&$lastModified===false) return true;
if($etag) header('ETag: '.$etag);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])&&isset($_SERVER['HTTP_IF_NONE_MATCH'])) { if($this->checkLastModified($lastModified)&&$this->checkEtag($etag)) { $this->send304Header(); $this->sendCacheControlHeader(); return false; } } elseif(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { if($this->checkLastModified($lastModified)) { $this->send304Header(); $this->sendCacheControlHeader(); return false; } } elseif(isset($_SERVER['HTTP_IF_NONE_MATCH'])) { if($this->checkEtag($etag)) { $this->send304Header(); $this->sendCacheControlHeader(); return false; }
}
if($lastModified) header('Last-Modified: '.gmdate('D, d M Y H:i:s', $lastModified).' GMT');
$this->sendCacheControlHeader(); return true; }
protected function getLastModifiedValue() { if($this->lastModifiedExpression) { $value=$this->evaluateExpression($this->lastModifiedExpression); if(is_numeric($value)&&$value==(int)$value) return $value; elseif(($lastModified=strtotime($value))===false) throw new Exception("error"); return $lastModified; }
if($this->lastModified) { if(is_numeric($this->lastModified)&&$this->lastModified==(int)$this->lastModified) return $this->lastModified; elseif(($lastModified=strtotime($this->lastModified))===false) throw new Exception("error"); return $lastModified; } return false; }
protected function getEtagValue() { if($this->etagSeedExpression) return $this->generateEtag($this->evaluateExpression($this->etagSeedExpression)); elseif($this->etagSeed) return $this->generateEtag($this->etagSeed); return false; }
protected function checkEtag($etag) { return isset($_SERVER['HTTP_IF_NONE_MATCH'])&&$_SERVER['HTTP_IF_NONE_MATCH']==$etag; }
protected function checkLastModified($lastModified) { return isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])&&@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$lastModified; }
protected function send304Header() { header('HTTP/1.1 304 Not Modified'); } protected function generateEtag($seed) { return '"'.base64_encode(sha1(serialize($seed),true)).'"'; } }
|
没有什么用,就是混淆视听的
ListView.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?php
abstract class ListView extends Base {
public $tagName='div'; public $template;
public function __construct() { }
public function run() {
echo "<".$this->tagName.">\n"; $this->renderContent(); echo "<".$this->tagName.">\n"; }
public function renderContent() {
ob_start(); echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template); ob_end_flush(); }
protected function renderSection($matches) { $method='render'.$matches[1];
if(method_exists($this,$method)) { $this->$method(); $html=ob_get_contents(); ob_clean(); return $html; } else return $matches[0]; } }
|
是个抽象类有run方法并且继承了base类
base.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <?php
class Base {
public function __get($name) { $getter = 'get' . $name; if (method_exists($this, $getter)) { return $this->$getter(); } else { throw new Exception("error property {$name}"); } }
public function __set($name, $value) { $setter = 'set' . $name; echo $setter; if (method_exists($this, $setter)) { return $this->$setter($value); } else { throw new Exception("error property {$name}"); }
}
public function __isset($name) { $getter = 'get' . $name; if (method_exists($this, $getter)) return $this->$getter() !== null;
return false; }
public function __unset($name) { $setter = 'set' . $name; if (method_exists($this, $setter)) $this->$setter(null);
} public function evaluateExpression($_expression_,$_data_=array()) { if(is_string($_expression_)) { extract($_data_); return eval('return '.$_expression_.';'); } else { $_data_[]=$this; return call_user_func_array($_expression_, $_data_); } }
}
|
在evaluateExpression方法中可以rce。
TestView.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| <?php
class TestView extends ListView { const FILTER_POS_HEADER='header'; const FILTER_POS_BODY='body';
public $columns=array(); public $rowCssClass=array('odd','even'); public $rowCssClassExpression; public $rowHtmlOptionsExpression; public $selectableRows=1;
public $data=array(); public $filterSelector='{filter}'; public $filterCssClass='filters'; public $filterPosition='body'; public $filter; public $hideHeader=false;
public function renderTableHeader() { if(!$this->hideHeader) { echo "<thead>\n";
if($this->filterPosition===self::FILTER_POS_HEADER) $this->renderFilter();
if($this->filterPosition===self::FILTER_POS_BODY) $this->renderFilter();
echo "</thead>\n"; } elseif($this->filter!==null && ($this->filterPosition===self::FILTER_POS_HEADER || $this->filterPosition===self::FILTER_POS_BODY)) { echo "<thead>\n"; $this->renderFilter(); echo "</thead>\n"; } } public function renderFilter() { if($this->filter!==null) { echo "<tr class=\"{$this->filterCssClass}\">\n";
echo "</tr>\n"; } } public function renderTableRow($row) { $htmlOptions=array(); if($this->rowHtmlOptionsExpression!==null) { $data=$this->data[$row]; $options=$this->evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data)); if(is_array($options)) $htmlOptions = $options; }
if($this->rowCssClassExpression!==null) { $data=$this->dataProvider->data[$row]; $class=$this->evaluateExpression($this->rowCssClassExpression,array('row'=>$row,'data'=>$data)); } elseif(is_array($this->rowCssClass) && ($n=count($this->rowCssClass))>0) $class=$this->rowCssClass[$row%$n];
if(!empty($class)) { if(isset($htmlOptions['class'])) $htmlOptions['class'].=' '.$class; else $htmlOptions['class']=$class; } } public function renderTableBody() {
$data=$this->data; var_dump($data); $n=count($data); echo "<tbody>\n";
if($n>0) { for($row=0;$row<$n;++$row) $this->renderTableRow($row); } else { echo '<tr><td colspan="'.count($this->columns).'" class="empty">';
echo "</td></tr>\n"; } echo "</tbody>\n"; }
}
|
继承了listview抽象类,有run方法并且也调用了evaluateExpression,是我们可以利用的类。

run方法中调用了rendercontent方法,我们跟进看看这个方法

看到有一个preg_replace_callback函数,该函数会把匹配到的内容作为回调函数的参数,在这里回调函数是rendersection继续跟进

该方法会把参数进行字符串拼接,在前端拼接上reander,并且会把拼接好的字符串当作函数调用,接下来就是找rander开头的函数有没有能利用的地方

这个函数就满足我们的条件,render开头,调用了evaluateExpression并且参数可控,但是这个函数需要一个参数才能调用,而rendersection的调用是不带参数的,我们得早另一个方法去调用他

调用这个方法就可以解决我们的问题

成功rec

读到flag