1. 程式人生 > 其它 >php程式碼審計-sql注入進階篇

php程式碼審計-sql注入進階篇

關鍵字過濾

部分waf會對關鍵字進行過濾,我們可以用大小寫或者雙寫關鍵字來繞過。

原始碼分析

<?php
require 'db.php';
header('Content-type:text/html;charset=utf8');
$username=dl($_POST['username']);
$password=dl($_POST['password']);
$dl="SELECT * FROM xs WHERE username='$username' and password='$password'"; //登入介面後臺處理
$ck=mysqli_query($db,$dl);
$row = mysqli_fetch_array($ck);
if($_POST['login']){
if($row) {
echo"你的密碼".$row['username'];
}else{
echo"登入失敗";
}
}
function dl($gl){
$gl=str_replace(array("union","UNION"),"","$gl");
$gl=str_replace(array("select","SELECT"),"","$gl");
$gl=str_replace(array("database","DATABASE"),"","$gl");
$gl=str_replace(array("sleep","SLEEP"),"","$gl");
$gl=str_replace(array("if","IF"),"","$gl");
$gl=str_replace("--","","$gl");
$gl=str_replace("order","","$gl");
return $gl;
}

分析一下程式碼,首先獲取了資料,載入dl函式以後帶入了資料庫中執行,然後if判定是否有提交,是否登入成功,登入成功後回顯使用者的賬號,這是一個非常簡單的後臺登入程式碼。往下看有一個自定義函式dl,函式內使用了str_replace(),str_replace()的作用是替換字串,這裡union,select,database ,if這些常用的注入字元大小寫都被替換成空。做了一個簡單的危險字元過濾自定義函式。

關鍵字過濾注入方法

用大小寫和雙寫關鍵字來嘗試繞過,返回程式碼裡有回顯位所以可以union注入,dl函式把union,select這些字元替換成空但是mysql中是不不區分大小寫的,所以可以大小寫混寫來繞過dl函式的過濾。比如Select Union DAtabase()這樣的字元是可以執行的。也可以用雙寫的手法,比如seselectlect這樣的語句, dl函式會把裡面的select替換為空這樣兩邊的字元湊在一起剛好又是一個select這樣就起到了繞過的作用。

大小寫繞過語句為 -1’ unioN Select dataBASE(),2 #

雙寫關鍵字繞過語句為 -1' ununionion selecselectt databasdatabasee(),2 #

都執行成功

or and xor not過濾

or and xor not 像這樣的邏輯運算也會被過濾袋掉那我們怎麼繞過呢?

原始碼分析

<?php
require 'db.php';
header('Content-type:text/html;charset=utf8');
$username=dl($_POST['username']);
$password=dl($_POST['password']);
$zifu='/(and|or|xor|not)/i';
if(preg_match("$zifu","$username&&$password")){
echo "<script>alert('存在危險字元')</script>";
}
$dl="SELECT * FROM xs WHERE username='$username' and password='$password'"; //登入介面後臺處理
$ck=mysqli_query($db,$dl);
$row = mysqli_fetch_array($ck);
if($_POST['login']){
if($row) {
echo"登入成功";
}else{
echo"登入失敗";
}
}
function dl($gl){
$gl=str_replace(array("union","UNION"),"","$gl");
$gl=str_replace(array("select","SELECT"),"","$gl");
$gl=str_replace(array("database","DATABASE"),"","$gl");
$gl=str_replace(array("sleep","SLEEP"),"","$gl");
$gl=str_replace(array("if","IF"),"","$gl");
$gl=str_replace("--","","$gl");
$gl=str_replace("order","","$gl");
return $gl;
}
?>

閱讀一遍程式碼發現在上一段的基礎上面添加了一個preg_match函式,這個函式過濾了or and xor not關鍵字,需要注意的是preg_match會大小寫都過濾,繼續往下讀回顯位改成了成功或者失敗所以我們只能採用盲注或者延時注入。

邏輯運算子繞過

先嚐試大小寫繞過,果然是失敗的。

使用邏輯運算子嘗試

and = &&

or = ||

xor = | # 異或

not = !

使用&&代替and構造盲注語句1’ && length(DATAbase())=3 # 因為關鍵字過濾函式還在所以還同時需要大小寫繞過。

注入成功

url編碼繞過

在平常使用url提交資料時,web容器在接到url後會自動進行一次url編碼解析,但是由於業務問題有些網站在web容器自動解析之後,通過編寫程式對解析的引數進行再次url編碼解析,就會出大問題。

原始碼分析

<?php
require 'db.php';
header('Content-type:text/html;charset=utf8');
$id1=$_GET['id'];
$gl="/and|or|not|xor|length|union|select|database|if|sleep|substr/i";
if(preg_match($gl,$id1)){
echo"<script>alert('存在危險字元')</script>";
}else{
$id=urldecode($id1);
$dl="SELECT * FROM xs WHERE id='$id'";
$result=mysqli_query($db,$dl);
$row = mysqli_fetch_assoc($result);
if($_GET['id']) {
if ($row) {
echo "登入成功:" . $row['username'];
}
}}
?>

上來還是先看看程式碼,把客戶端傳入的get引數賦值進了$id1,用if加preg_match對變數$id1裡的值進行檢索。如果客戶端傳入的引數有$gl裡的值那麼就會返回前端程式碼進行警告。沒有危險字元才會執行下面的程式碼,接著把$id1裡的引數進行一次url解編碼並賦值給$id。此時客戶端傳入的引數已經經過了兩次url編碼解析。最後過濾完成把id變數帶入資料庫中進行查詢並返回使用者的賬號。

注入語句

分析程式碼時說到客戶端傳入的引數會進行兩次url編碼解析之後帶入資料庫,但危險過濾是在第一次解析之後第二次解析之前執行的。也就是說我們可以寫入兩次url編碼過的語句繞過preg_match,比如and在過濾範圍之中,對and一次url全編碼後變為%61%6e%64%0,再進行一次編碼為%25%36%31%25%36%65%25%36%34

。把經過兩次編碼後的and提交資料會經過web容器解碼後變為%61%6e%64,preg_match判定就不會觸發。

構造嘗試語句

把-1’ union select database(),2,3 --+編碼為-1'

%25%37%35%25%36%65%25%36%39%25%36%66%25%36%65 %25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34 %25%36%34%25%36%31%25%37%34%25%36%31%25%36%32%25%36%31%25%37%33%25%36%35(),2,3 --+

成功繞過,程式碼執行帶出了當前資料庫。

轉發來自freebuf