0X01 普通注入php
SQL參數拼接,未作任何過濾html
<?php $con = mysql_connect("localhost","root","root"); if (!$con){die('Could not connect: ' . mysql_error());} mysql_select_db("test", $con); $id = stripcslashes($_REQUEST[ 'id' ]); $query = "SELECT * FROM users WHERE id = $id "; $result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>'); while($row = mysql_fetch_array($result)) { echo $row['0'] . " " . $row['1']; echo "<br />"; } echo "<br/>"; echo $query; mysql_close($con); ?>
測試語句:id=1 UNION SELECT user(),2,3,4 from usersmysql
0x02 寬字節注入git
A、MYSQL中的寬字符注入web
示例代碼:正則表達式
<?php $con = mysql_connect("localhost","root","root"); mysql_query("SET NAMES 'gbk'"); mysql_select_db("test", $con); $id = isset($_GET['id']) ? addslashes($_GET['id']) : 1; $query = "SELECT * FROM users WHERE id ='{$id}' "; $result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>'); while($row = mysql_fetch_array($result)) { echo $row['0'] . " " . $row['1']; echo "<br />"; } echo "<br/>"; echo $query; mysql_close($con); ?>
測試語句:%df%27sql
mysql的特性,由於gbk是多字節編碼,兩個字節表明一個漢字,因此%df和後面的\也就是%5c變成了一個漢字「運」,而’逃逸了出來。數據庫
根據gbk編碼,第一個字節ascii碼大於128,基本上就能夠了。好比咱們不用%df,用%a1也能夠.安全
gb2312編碼的取值範圍。它的高位範圍是0xA1~0xF7,低位範圍是0xA1~0xFE,而\是0x5c,是不在低位範圍中的。因此,0x5c根本不是gb2312中的編碼,因此不會形成寬字節注入。擴展到世界上全部多字節編碼,只要低位的範圍中含有0x5c的編碼,就能夠進行寬字符注入。微信
寬字符注入的修復:
方案一:指定php鏈接mysql的字符集
mysql_set_charset('gbk',$conn);
$id =mysql_real_escape_string($_GET['id']);
方案二:將character_set_client設置爲binary(二進制)
mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn);
將character_set_client設置成binary,就不存在寬字節或多字節的問題了,全部數據以二進制的形式傳遞,就能有效避免寬字符注入。
B、PHP 編碼轉換
示例代碼:
<?php $con = mysql_connect("localhost","root","root"); mysql_query("SET NAMES 'gbk'"); mysql_select_db("test", $con); mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $con); $id = isset($_GET['id']) ? addslashes($_GET['id']) : 1; $id=iconv('utf-8','gbk',$id); $query = "SELECT * FROM users WHERE id ='{$id}' "; $result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>'); while($row = mysql_fetch_array($result)) { echo $row['0'] . " " . $row['1']; echo "<br />"; } echo "<br/>"; echo $query; mysql_close($con); ?>
測試語句: 錦'
錦這個字:它的utf-8編碼是%e9%8c%a6,它的gbk編碼是%e5%5c
錦被iconv從utf-8轉換成gbk後,變成了%e5%5c,然後面的’被addslashes變成了%5c%27,這樣組合起來就是%e5%5c%5c%27,兩個%5c就是\\,正好把反斜槓轉義了,致使’逃逸出單引號,產生注入。
$id=iconv('gbk','utf-8',$id); //使用%df%27來測試
一個gbk漢字2字節,utf-8漢字3字節,若是咱們把gbk轉換成utf-8,則php會每兩個字節一轉換。因此,若是\’前面的字符是奇數的話,勢必會吞掉\,’逃出限制。
其餘函數:
mb_convert_encoding($id,'utf-8','gbk') //GBK To UTF-8與 iconv('gbk','utf-8',$id)同樣
參考文章:
PHP字符編碼繞過漏洞總結 http://www.cnblogs.com/Safe3/archive/2008/08/22/1274095.html
淺析白盒審計中的字符編碼及SQL注入 http://www.freebuf.com/articles/web/31537.html
0x03 編碼解碼
找一些編碼解碼的函數來繞過防禦,,如urldecode() 、rawurldecode()、base64_decode()
<?php $con = mysql_connect("localhost","root","root"); mysql_select_db("test", $con); $id = addslashes($_REQUEST['id']); $id = urldecode($id);//$id = base64_decode($id); $query = "SELECT * FROM users WHERE id = '{$id}'"; $result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>'); while($row = mysql_fetch_array($result)) { echo $row['0'] . " " . $row['1']; echo "<br />"; } echo "<br/>"; echo $query; mysql_close($con); ?>
測試語句:
1'union select 1,2,3,4%23 單引號Urlencode 1%2527union select 1,2,3,4%23
1'union select 1,2,3,4# Base64 Encode MSd1bmlvbiBzZWxlY3QgMSwyLDMsNCM=
0x04 二次注入
入庫後轉義符就會消失,變成hack',查詢出庫的就是hack',若是拼接到SQL語句,成功引入了單引號閉合前面字符,致使注入。
測試:
CREATE TABLE test (user VARCHAR(20) NOT NULL);
INSERT INTO test values('hack\'');
示例代碼:
//測試數據 create table test( id INT NOT NULL, user VARCHAR(100) NOT NULL, pass VARCHAR(100) NOT NULL ) INSERT INTO test values(1,'hack','hack'); //測試代碼 <?php $con = mysql_connect("localhost","root","root"); mysql_select_db("test", $con); mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $con); //update入庫 if (isset($_GET['key'])){ $key=addslashes($_REQUEST['key']); $query ="update test set user='{$key}' where id=1"; echo "INSERT SQL: ".$query."<br/>"; $result = mysql_query($query); } //select 出庫,並帶入查詢 $query = "SELECT * FROM test WHERE id = 1"; $result = mysql_query($query); $row = mysql_fetch_row($result); echo "<br/>"; $query = "SELECT * FROM test WHERE user = '{$row[1]}'"; print_r('SELECT SQL: '.$query.'<br/>'); $result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');; echo "<br/>"; print_r(mysql_fetch_row($result)); mysql_close($con); ?>
測試截圖:
0x05 全局防禦盲點
一、str_replace函數 過濾單引號等,可能形成注入;
二、stripslashes() 函數刪除由 addslashes() 函數添加的反斜槓。stripslashes函數使用不當,可能形成注入;
①注入點相似id=1這種整型的參數就會徹底無視GPC的過濾;
②注入點包含鍵值對的,那麼這裏只檢測了value,對key的過濾就沒有防禦;
③有時候全局的過濾只過濾掉GET、POST和COOKIE,可是沒過濾SERVER。
①FILES注入,全局只轉義掉GET、POST等傳來的參數,遺漏了FILES;
②變量覆蓋,危險函數:extract()、parse_str()、$$。
0X06 漏洞防禦
基本思路:輸入(解決數字型注入)-------轉義處理(解決字符型注入)-------輸出(解決數據庫報錯)
一、檢查輸入的數據是否具備所指望的數據格式。PHP 有不少能夠用於檢查輸入的函數,從簡單的變量函數和字符類型函數(好比 is_numeric(),ctype_digit())到複雜的 Perl 兼容正則表達式函數均可以完成這個工做。若是程序等待輸入一個數字,能夠考慮使用 is_numeric() 來檢查,或者直接使用 settype() 來轉換它的類型,也能夠用 sprintf() 把它格式化爲數字。
二、PHP內置轉義函數
Addslashes() http://php.net/manual/zh/function.addslashes.php
magic_quote_gpc http://php.net/manual/zh/info.configuration.php#ini.magic-quotes-gpc
mysql_real_escape_string() http://php.net/manual/zh/function.mysql-real-escape-string.php
mysql_escape_string() http://php.net/manual/zh/function.mysql-escape-string.php
三、數據庫報錯信息泄露防範:
把php.ini文件display_errors = Off
數據庫查詢函數前面加一個@字符
最有效可預防SQL注入攻擊的防護方式:預處理技術進行數據庫查詢:
代碼示例:
<?php $mysqli = new MySQLi("localhost","root","root","test"); if(!$mysqli){ die($mysqli->error); } $sql = "select id,username,password from users where id=?";////建立一個預約義的對象 ?佔位 $mysqli_stmt = $mysqli->prepare($sql); $id=$_REQUEST['id']; $mysqli_stmt->bind_param("i",$id);////綁定參數 $mysqli_stmt->bind_result($id,$username,$password);////綁定結果集 $mysqli_stmt->execute();//執行 while($mysqli_stmt->fetch()){ //取出綁定的結果集 echo $id." ".$username ." ". $password; } echo "<br/>"; echo $sql; $mysqli_stmt->free_result(); ////關閉結果集 $mysqli_stmt->close(); $mysqli->close(); ?>
關於我:一個網絡安全愛好者,致力於分享原創高質量乾貨,歡迎關注個人我的微信公衆號:Bypass--,瀏覽更多精彩文章。