搭建漏洞服務
還是先搭一個漏洞的服務吧:新建一個~/Desktop/php/upload文件夾,文件夾下存在如下結構的文件和文件夾:
$ tree
.
├── files
└── upload.php
1 directory, 1 file
upload.php:
<html>
<body>
<form action="upload.php" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
<?php
if ($_FILES["file"]["error"] > 0) {
echo "Error: " . $_FILES["file"]["error"] . "<br />";
}
else {
// 判斷當期目錄下的 upload 目錄是否存在該文件
// 如果沒有 upload 目錄,你需要創(chuàng)建它,upload 目錄權限為 777
if (file_exists("files/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " 文件已經(jīng)存在。 ";
}
else
{
echo "PWD: " . `pwd` . "<br />";
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
// 如果 upload 目錄不存在該文件則將文件上傳到 upload 目錄下
$file_path = "files/" . $_FILES["file"]["name"];
$success = move_uploaded_file($_FILES["file"]["tmp_name"], $file_path);
echo "Stored in: " .$file_path."<br/>";
if(file_exists($file_path)){
$str = file_get_contents($file_path);//將整個文件內容讀入到一個字符串中
$str = str_replace("\r\n","<br />",$str);
$str = htmlspecialchars($str);
echo "File Contents: $str";
}
}
}
?>
配置nginx服務:
server {
listen 80;
listen [::]:80;
server_name test.com;
root /home/repersp/Desktop/php;
index test.php;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php7.0-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# With php7.0-fpm:
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
}
改完host之后網(wǎng)站正常訪問,接著我們上傳一句話,這里因為之前裝了安全狗,我們用%00截斷過一下狗:

1544361917866.png
文件上傳好了,用一下看看:

1544360944473.png
可以看到,即使用了通用防護措施,但是若網(wǎng)站本身就是一個脆弱的,那么依舊有風險。
防護
我們知道,通過php判斷后綴名,判斷文件開頭的幾個字符是否為圖片,判斷Content-Type都是可以通過偽造繞過的,唯一的辦法就是配置nginx,確保上傳目錄不被執(zhí)行:
server {
listen 80;
listen [::]:80;
server_name test.com;
root /home/repersp/Desktop/php;
index test.php;
location ~ \/upload\/files\/.* {
root /home/repersp/Desktop/php;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php7.0-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# With php7.0-fpm:
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
}
可以看到這樣做代碼只是展現(xiàn)在前臺而不會被執(zhí)行

1544363449099.png
任意文件讀取防御
如果網(wǎng)站需要用戶上傳zip壓縮文件,并且展現(xiàn)了zip壓縮的內容,那么用戶可以上傳一個壓縮后的符號鏈接文件,這樣php會打印被鏈接的文件內容,即任意文件讀取,這時代碼里面還需要判斷zip壓縮里的文件類型。
目錄穿越防御
如果網(wǎng)站設計缺陷可以使用../將文件上傳到上層目錄(當然這里不存在,因為表單默認過濾了../及其前面的字符),并且用戶可以控制目錄的話,還需要在代碼中檢查文件名。