五月 11, 2022

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方法,我们跟进看看这个方法

image-20220511163008412

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

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

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

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

成功rec

读到flag