🌸 連接方式
PHP + Mysql連接方式:
- Mysql(廢棄)
- Mysqli
- PDO
🌸 常見過濾
intval/addslashes/mysql_real_escape
mysqli_escape_string/mysqli_real_escape_string/mysqli::escape_string
PDO::quote
參數化查詢
addslashes/mysql_real_escape
- 可能會導致寬字節注入(如果使用的是GBK編碼)
- 尋找字符串轉換函數來繞過
urldecode
simple_xml_loadstring
stripshales
json_decode
iconv
base64_decode
mysqli::escape_string / PDO::quote
- 與
addslashes
差別:是否會主動加引號包裹 - 寬字節注入
- 與
- 參數化查詢
- 尋找非SQL值位置
SELECT `name` FROM `users` WHERE `id` = ? ORDER BY `login_time` LIMIT 1
,在這個SQL語句中除了?的位置是SQL值的位置,其他的位置都不是,要找非SQL值(可控)的位置
🌸 思路總結
??開發者容易遺漏的輸入點:
HTTP頭
X-Forwarded-For
User-Agent
Referer
PHP_SELF
REQUEST_URI
文件名 $_FILES[][name]
php://input
引入單引號(轉義符)的方法
stirpslashes
base64_decode
urldecode
substr
iconv
str_replace('0','',$sql)
xml
json_encode
案例
🍂 1.php
<?php
include_once 'common.php';try {$name = $_GET["name"];$query = "SELECT name,age,email,country FROM user_details WHERE name = '{$name}';";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????這種寫法是最簡單的寫法,也是最基本的SQL注入類型。漏洞原因就是將name
變量直接拼接在了SQL
查詢語句中!
????????直接使用單引號進行閉合,利用報錯注入,獲取數據庫信息。
' and updatexml(1,concat(0x7e,database()),1)%23
🍂 2.php
<?php
include_once './common.php';try {$name = $_GET["name"];$name = addslashes($name);$query = "SELECT name,age,email,country FROM user_details WHERE name = '{$name}';";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該代碼使用了addslashes()
函數進行了相關的轉義。利用php
在線文檔進行查詢該函數:
????????該函數會將單引號、雙引號、反斜線、NUL字節進行轉義。(但是并不能防止SQL注入漏洞的出現!)在這個代碼中若不考慮GBK編碼的問題,是不存在SQL注入漏洞的!
出現SQL
注入漏洞的情況:
寬字節注入
urldecode/base64_decode/iconv等函數的出現
🍂 3.php
<?php
include_once './common.php';try {$name = htmlspecialchars($_GET['name']);$query = "SELECT name,age,email,country FROM user_details WHERE name = '{$name}';";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該代碼中出現了新的函數:htmlspecialchars()
,依然是通過php手冊,查詢相關的功能:
????????該函數的作用是將特殊字符轉換為HTML實體。可以看到幫助文檔中提到,設置了ENT_QUOTES后
,單引號就會被轉為為'... 可以看到該函數是在第二個參數重進行設置。
????????然而在該代碼中沒有第二個參數,所以說,這里是不會將單引號進行轉換為HTML
實體的!依然存在漏洞。
🍂 4.php
<?php
include_once './common.php';try {$id = addslashes($_GET['id']);$query = "SELECT name,age,email,country FROM user_details WHERE id > $id;";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該代碼中接收的參數變成了id參數,沒有單引號進行包裹,所以這里的addslashes()
函數也就失效了。
🍂 5.php
<?php
include_once './common.php';try {$name = preg_replace("/'/", "\\'", $_GET['name']);$query = "SELECT name,age,email,country FROM user_details WHERE name = '{$name}';";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該代碼中使用了新的函數preg_replace()
,同樣是查詢手冊:
????????簡單來說就是替換!只要用戶輸入的數據中出現了單引號,那就替換為\'
。看似是實現了過濾,但是依然存在SQL注入漏洞
。
????????原因是,當用戶輸入的是:\'
的時候,那么就會替換為:\\'
,而第一個\
把第二個\
給轉義了,使得'
逃脫!
payload: Bob\%27%20and%20updatexml(1,concat(0x7e,database()),1)%23
🍂 6.php
<?php
include_once './common.php';try {$id = intval($_GET['id']);$query = "SELECT name,age,email,country FROM user_details WHERE id = {$id};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該代碼采用了intval函數
,將用戶輸入的內容轉換為整數,雖然還是直接拼接了SQL語句
,但是并不存在SQL注入漏洞。
????????用戶輸入:1'
,就會轉換為1
,可以理解為從第一個不是數字的地方開始,全部舍棄。
🍂 7.php
<?php
include_once './common.php';try {if (intval($_GET["id"])) {$query = "SELECT name,age,email,country FROM user_details WHERE id = {$_GET['id']};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}}
}catch (PDOException $e){echo $e->getMessage();
}
????????也算是比較經典的漏洞了,看似存在intval函數
,但是后面拼接的時候,并不是intval()
轉換之后的結果,而是$_GET['id']
!從而導致了SQL注入
漏洞的出現。
🍂 8.php
<?php
include_once './common.php';try {if (!is_numeric($_GET['id'])) {header('Status: 404 Not Found');}$query = "SELECT name,age,email,country FROM user_details WHERE id = {$_GET['id']};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該版本采用了新的函數is_numeric()
判斷傳遞的參數是否是數字!如果不是數字的話,那就通過header
頭發送原生的http
頭。
????????但是這里存在一個很大的問題,當用戶輸入的不是數字的時候,雖然進入了if條件,但是并沒有exit/die
函數的出現,所以整個代碼還是會繼續往下執行。
id=1%20and%20sleep(3)#
此時就可以通過延時注入來進行測試:
🍂 9.php
<?phpinclude_once './common.php';try {$order = addslashes($_GET['order']);if(!preg_match('/DESC|ASC/i', $order)) {exit("Bad order");}$query = "SELECT name,age,email,country FROM user_details order by id {$order};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????雖然這里出現了addslashes()
函數,但是并不需要單引號進行包裹~ 同時還出現了正則匹配,匹配$order
中是不是存在DESC/ASC
。所以注入的相關語句可以寫成:ASC; DROP TABLE user_details--
🍂 10.php
<?php
include_once './common.php';try {if (!is_numeric($_GET['id'])) {header('Status: 404 Not Found');exit();}$query = "SELECT name,age,email,country FROM user_details WHERE id = {$_GET['id']};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????該版本的代碼就是針對8.php
,做出了相關的修復。
🍂 11.php
<?php
include_once './common.php';try {$order = addslashes($_GET['order']);$query = "SELECT name,age,email,country FROM user_details ORDER BY id {$order};";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????同樣存在SQL注入漏洞,無需單引號閉合:
🍂 12.php
<?php
include_once './common.php';try {$name = $_GET["name"];$query = "SELECT name,age,email,country FROM user_details WHERE name= ?;";$stmt = $conn->prepare($query);$stmt->bindValue(1, $name);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????這種寫法就是預編譯的寫法了,是不存在SQL注入
漏洞的。
🍂 13.php
<?php
include_once './common.php';try {$name = addslashes($_GET["name"]);$name = urldecode($name);$query = "SELECT name,age,email,country FROM user_details WHERE name= '{$name}';";$stmt = $conn->prepare($query);$stmt->execute();$stmt->bindColumn('email', $email);while ($row = $stmt->fetch(PDO::FETCH_BOUND)) {echo "$email" . "<br>";}
}catch (PDOException $e){echo $e->getMessage();
}
????????本來如果只有addslashes()
函數的話,就不會存在SQL注入
漏洞,但是addslashes函數
后面存在一個urldecode()
函數,從而導致了SQL注入
漏洞的出現,同時若是出現base64_decode iconv
等函數時,也是存在SQL注入
漏洞的!
????????因為經過一次編碼之后,addslashes()
函數是檢測不到單引號的存在~
????????在URL中發送payload的時候,會自動進行一次URLEncode
,服務器收到請求之后,再自動的進行URLDecode
,然后代碼中存在一次URLDecode
,所以Payload
就需要進行兩次URLEncode
。
🦄 數據庫配置
CREATE DATABASE example;CREATE TABLE `example`.`user_details` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(256) NOT NULL,
`email` VARCHAR(256) NOT NULL,
`age` INT NULL,
`country` VAR CHAR(256) NULL,
PRIMARYKEY (`id`)
) ENGINE = MyISAM;INSERT INTO `user_details`(`id`,`name`,`email`,`age`,`country`) VALUES('1','Bob','bob@example.com','22','China');
INSERT INTO `user_details`(`id`,`name`,`email`,`age`,`country`) VALUES('2','Alice','Alice@example.com','25','En');