跳到主要內容

SQL Injection: bit-by-bit

暨『SQL Injection Get Username』這篇以後,同樣也是書上很有趣的手法。

bit-by-bit:逐步推論攻擊

主要需要使用到以下幾個功能:

  • IF
  • ASCII
  • SUBSTRING
  • 二進制運算


攻擊範例:

上面這是此攻擊的基本公式,當然不是絕對的,另外針對






這部份必須要先說明,他是指2的j次方,而非真的有此指令,這需要自己去換算,功用等一下會一併說明。



這段攻擊指令,需要分成幾個階段做說明,首先:
if(...,1,0)
if判斷,true回傳1,否則回傳0

ASCII(...)

取得字母的ASCII
SUBSTRING(... , i , 1)
取得字串的第i位起算,1個字母,以 hello 為例:
substring('hello', 1,1) => h
substring('hello', 2,1) => o
substring('hello', 3,1) => l
substring('hello', 3,2) => ll
substring('hello', 3,3) => llo

接下來是
&
這是mysql裡面的二進制運算符號,

運算邏輯(二進位運算)範例:
1&1 => 1
0&0 => 0
1&0 => 0
0&1 => 0

範例:
5&2^0 => 101 & 001 => 001
5&2^1 => 101 & 010 => 000
5&2^2 => 101 & 100 => 100
依照上面的範例,我們可以在不知道5的狀況下,透過運算,逐步推出每一位的結果,進而推論出5這個答案,這是這個攻擊很關鍵的地方!!

接下來我們就可以開始跑整個流程:
假設current_user()得到的是結果是: root@localhost
 1. substring("root@localhost",1,1) => r

 2. ascii('r') => 114

 3. 114&2^j <= 這邊就可以藉由前面所介紹的方式,找出114這個數字


接下來就是跑loop,把substring的位置慢慢往後,就可以拼湊出最後的答案。

可能就會有人要問,我要怎樣才能知道已經找到完整的名稱了?
這時候需要用到另外一個函數:
length()
先得到這個名稱的長度以後,再來做拆解的動作。

範例:
if(length(...) < L ,1 ,0 )
這裡L是我們要逐步設定的變數,也就是可能長度,我們以上面的例子為例:
if(length('root@localhost') < 32  ,1 ,0 ) => 1
if(length('root@localhost') < 16  ,1 ,0 ) => 1
if(length('root@localhost') <  8  ,1 ,0 ) => 0
if(length('root@localhost') < 12  ,1 ,0 ) => 0
if(length('root@localhost') = 15  ,1 ,0 ) => 0
if(length('root@localhost') = 14  ,1 ,0 ) => 1 (Answer)
透過二分法的方式,從很大的數字可以很快的收斂,並且找到確切的字串長度!
有人可能會問為什麼要從32開始,因為那是MySQL的Username的長度限制,不過這是針對username的部份,如果針對不同的資料庫,或是欄位這就需要調整,大不了就是從10,000開始總不會錯了吧XD 只是比較浪費時間就是了!

參考資料:


  • http://dev.mysql.com/doc/refman/5.7/en/user-names.html
  • https://zh.wikipedia.org/zh-tw/ASCII
  • https://en.wikipedia.org/wiki/Bitwise_operation



留言