0x01 前言
Fofa:
请见文末
框架:ThinkPHP 5.0.24 Debug:True
0x01 前台任意文件上传漏洞
全局搜索Thinkphp的原生上传函数file(),能找到非常多的上传点,大部分都不需要鉴权,但是不返回文件上传地址
在 /home/controller/xxxx.php 控制器的xxxx方法中,上传之后通过Json_encode返回了Json编码后的文件上传地址.
//上传图片
public function xxxx(){
$file = request()->file('xxxx');
$qninfo = $this->qiniuupload($file);
if(!$qninfo){
message('图片错误','','error');
}
$data['img'] = $qninfo;
echo json_encode($data);
}
这里是经过qiniuupload()方法上传的,我们可以跟踪一下,看起来像是用七牛云上传了,但实际是本地上传直接Return返回了,下面还有个die().
public function qiniuupload($file)
{
$filePath = $file->getRealPath();
$ext = pathinfo($file->getInfo('name'), PATHINFO_EXTENSION); //后缀
$info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
if($info){
return '/uploads/'.$info->getSaveName();
}else{
return false;
}
die;
//获取当前控制器名称
//$controllerName=$this->getContro();
// 上传到七牛后保存的文件名
$key =substr(md5($file->getRealPath()) , 0, 5). date('YmdHis') . rand(0, 9999) . '.' . $ext;
require_once APP_PATH . '/../vendor/qiniu/autoload.php';
// 需要填写你的 Access Key 和 Secret Key
$accessKey = config('ACCESSKEY');
$secretKey = config('SECRETKEY');
// 构建鉴权对象
$auth = new Auth($accessKey, $secretKey);
// 要上传的空间
$bucket = config('BUCKET');
$domain = config('DOMAIN');
$token = $auth->uploadToken($bucket);
// 初始化 UploadManager 对象并进行文件的上传
$uploadMgr = new UploadManager();
// 调用 UploadManager 的 putFile 方法进行文件的上传
list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
if ($err !== null) {
return false;
} else {
return 'http://'.$domain.'/' . $ret['key'];
}
}
所以咱们构造Payload:
请见文末!
0x02 后台权限绕过漏洞
默认后台登录 /admin/auth/login.html
首先咱们看看登录接口 位于 /admin/controller/Auth.php 控制器的 Login 方法
先是通过验证密码是否正确,然后给Cookie赋值 Cookie::set('administrator',base64_encode($administrator)); 之后直接exit跳转到 /admin/index/index 中
public function login(){
$ip=getIp();
// if($ip == '115.62.210.41' || $ip == '229.153.90.235'){
// }else{
// die;
// }
if(request()->isAjax()){
$params = request()->post();
$result = $this->validate($params,'Administrator.login');
// if($result !== true){
// message($result,'','error');
// }
$administrator = Administrator::getInfoByUsername($params['username']);
if(empty($administrator)){
message('管理员信息不存在','','error');
}
if(!md5_password_check($params['password'],$administrator['password'],$administrator['salt'])){
message('密码输入错误,可前往71jc.cn获取账号密码','','error');
}
Cookie::set('administrator',base64_encode($administrator));
cache('DB_TREE_MENU_' . $administrator['id'], NULL);
message('登录成功', U('index/index'), 'success');
}
Cookie::delete('administrator');
return $this->fetch(__FUNCTION__);
}
基本上所有admin集合中的控制器都继承于Base控制器 所以实际鉴权函数在 /admin/controller/Base.php 控制器的 _checkLogin 方法中.
private function _checkLogin(){
if(Cookie::has('administrator')){
$this->administrator = (array)json_decode(base64_decode(Cookie::get('administrator')));
}
//未登录,当前控制器不属于免登录
if(!in_array(request()->controller(),['Auth']) && !check_array($this->administrator)){
if(request()->isAjax()){
message('请先登录', U('auth/login'), 'error');
}
$this->redirect(U('auth/login'));
}
}
仅仅是验证经过Base64解码后的Cookie值administrator 这里就非常可能存在权限绕过漏洞了.
咱们先模拟正常登录的流程,直接登录,然后就能看到被赋值的Cookie
咱们Base64解码一下 推荐用
https://www.toolhelper.cn/EncodeDecode/Base64 这个解码
可以发现是json编码的信息,经过验证我们发现仅需要 id username avatar 这三个字段即可绕过登录(其实少了avatar也行,但是大部分接口就不能用了,会报错)
咱们编码一下得到这样一串:
咱们将所有Cookie清除,退出登录,然后直接利用Hackbar伪造 Cookie 访问功能点,可以看到成功绕过进入后台了.
Payload:
请见文末
原文始发于微信公众号(星悦安全):某UI众人帮悬赏任务系统审计(0day)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论