ikari
在index.php里面有这么一段代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
try
{
require ROOT_PATH . 'Engine/Loader.php';
E\Loader::getInstance()->init(); // Load necessary classes
$aParams = ['ctrl' => (!empty($_GET['p']) ? $_GET['p'] : 'blog'), 'act' => (!empty($_GET['a']) ? $_GET['a'] : 'index'), 'template' => (!empty($_GET['t']) ? $_GET['t'] : 'pc'), 'ns' => (!empty($_GET['n']) ? $_GET['n'] : 'TestProject\Controller\\')];
E\Router::run($aParams);
}
catch (\Exception $oE)
{
echo $oE->getMessage();
}
?>
|
我们跟进Router去看看
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
|
class Router
{
public static function run (array $aParams)
{
if($aParams['ns'] != 'TestProject\Controller\\'){
$sNamespace = $aParams['ns'];
$sCtrlPath = $sNamespace;
}else{
$sNamespace = 'TestProject\Controller\\';
$sDefCtrl = $sNamespace . 'Blog';
$sCtrlPath = ROOT_PATH . 'Controller/';
}
if($aParams['ns'] == '\\'){
$aParams['ctrl'] = '\\system';
if(preg_match('/^[a-zA-Z0-9\/]*$/',$aParams['act'])){
$aParams['act'] = 'ls /www/'.$aParams['act'];
}else{
$aParams['act'] = 'ls /www/';
}
}
$sTemplatePath = str_replace(array(".","\/"), "", ROOT_PATH . 'Template/' . $aParams['template']);
include $sTemplatePath;
$sCtrl = ucfirst($aParams['ctrl']);
if (is_file($sCtrlPath . $sCtrl . '.php') || (substr($sCtrl, 0, 1) === '\\'))
{
$sCtrl = $sNamespace . str_replace('\\','',$sCtrl);
if(class_exists($sCtrl)){
$oCtrl = new $sCtrl;
}else{
call_user_func($sCtrl, $aParams['act']);
exit();
}
if ((new \ReflectionClass($oCtrl))->hasMethod($aParams['act']) && (new \ReflectionMethod($oCtrl, $aParams['act']))->isPublic())
call_user_func(array($oCtrl, $aParams['act']));
else
call_user_func(array($oCtrl, 'notFound'));
}
else
{
call_user_func(array(new $sDefCtrl, 'notFound'));
}
}
}
|
这里是对路由的操作,这里,我们可以通过控制参数p选择路由,a选择路由的功能
在utils里面有这个,这里可以进行文件的包含
1
2
3
4
5
6
7
8
9
10
|
public function __get($name)
{
if(stripos($name,'php') || stripos($name,'Upload') || stripos($name,'flag') || stripos($name,':')){
die('Dangerous Operation.');
}else{
if(file_exists($name)){
require $name;
}
}
}
|
而这个函数的入口在blog路由下的post方法,这里传一个参数f,进行包含,因为这里对参数有过滤,所以我们选择session_upload进行竞争包含
1
2
3
4
5
6
7
8
9
10
11
|
public function post()
{
if($this->render == 0){
$this->oUtil->oPost = $this->oModel->getById($this->_iId); // Get the data of the post
$this->oUtil->getView('post');
}else{
$this->oUtil->__get($_GET['f']);
}
}
|
xxxcloud
源码中有admin的密码hash,但是解不开,这里的做法是找到token的构造算法,进行手动token生成
找到token计算算法
找到systempassword
计算出token
然后伪造登录获取flag
这里的防守不知道应该用什么方式进行防守,如果是伪造token的话,那我们无法避免,因为token算法是写死的,我尝试把这个系统的一个任意文件删除修复,但是任然是exp攻击成功
后来直接上了个awd的通防waf,没想到还防住了。。
template
任意文件读取 + 读 opcache缓存
这里传入tpl参数,赋值为$tpl
1
2
3
|
if (isset($_GET['tpl']) && is_string($_GET['tpl'])):
$tpl = $_GET['tpl'];
endif;
|
然后在check方法里面,先进行后缀检测,这里过滤的是挺严的
如果通过了过滤,那么就会进行file_get_contents函数,进行文件读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private function check() {
try{
$arr = explode('.', $this->tpl);
$ext = end($arr);
if (in_array($ext, ['php', 'php2', 'php3', 'php4', 'php5', 'php6', 'php7', 'phtml'])):
return false;
endif;
$content = file_get_contents($this->tpl);
if (!$content):
return false;
endif;
if ( preg_match('/script|<\?/i', $content) ):
return false;
endif;
} catch (Exception $e) {
return false;
}
return true;
}
|
那我们就可以进行除了php文件外,任意文件的读取了
这里如果tpl是debug的话,会给phpinfo
这里是开启了zend_opcache,这里我们利用包含opcache进行攻击
首先得计算systemid,这个的计算可以利用工具https://github.com/GoSecure/php7-opcache-override/
然后就是任意文件读取了
http://172.16.9.44:8091/?tpl=..//…//…//…//…/var/www/cache/1116d566fdc53f79abce6c01e3a0308d/var/www/html/flag.php.bin