跟bWAPP學WEB安全(PHP代碼)--終結篇:文件目錄遍歷、文件上傳、SSRF、CSRF、XXE、文件包含


前言



過年過的很不順,家里領導和我本人接連生病,年前臘月29才都治好出院,大年初六家里的拉布拉多愛犬又因為細小醫治無效離開了,沒能過年回家,花了好多錢,狗狗還離世了。所以也就沒什么心思更新博客。今天初七,正式上班了,更新一篇吧,把bWAPP中常見的web漏洞也就一次性更新完畢,完成這個系列,雖然有點虎頭蛇尾,但是也頗感無奈了。去年立的flag也還有兩個大系列沒有完成,一個是互聯網公司常見漏洞分析,一個是C語言基礎自學筆記,要看的東西太多了,也就是借着努力學習工作來忘卻生活的打擊和痛苦吧。

文件和目錄遍歷



從本質上說,二者的漏洞原因沒啥不一樣,尤其是linux系統中,一般都出現在文件閱讀、下載、展示、或者可以列出目錄的地方。

PHP代碼


function directory_traversal_check_1($data)
{

    // Not bulletproof
    
    $directory_traversal_error = "";  
    
    // Searches for special characters in the GET parameter
    if(strpos($data, "../") !== false ||
       strpos($data, "..\\") !== false ||
       strpos($data, "/..") !== false ||
       strpos($data, "\..") !== false)
            
    {

        $directory_traversal_error = "Directory Traversal detected!";
    
    }
    
    /*
    else
    {
    
        echo "Good path!";
    
    }     
     */
    
    return $directory_traversal_error;

}

function directory_traversal_check_2($data)
{

    // Not bulletproof
    
    $directory_traversal_error = "";  
    
    // Searches for special characters in the GET parameter
    if(strpos($data, "../") !== false ||
       strpos($data, "..\\") !== false ||
       strpos($data, "/..") !== false ||
       strpos($data, "\..") !== false ||
       strpos($data, ".") !== false)
            
    {

        $directory_traversal_error = "Directory Traversal detected!";
    
    }
    
    /*
    else
    {
    
        echo "Good path!";
    
    }     
     */
    
    return $directory_traversal_error;

}

function directory_traversal_check_3($user_path,$base_path = "")
{
    
    $directory_traversal_error = "";
    
    $real_base_path = realpath($base_path);

    // echo "base path: " . $base_path . " real base path: " . $real_base_path . "<br />";

    $real_user_path = realpath($user_path);

    // echo "user path: " . $user_path . " real user path: " . $real_user_path . "<br />";

    // int strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )
    // URL: http://php.net/manual/en/function.strpos.php
    if(strpos($real_user_path, $real_base_path) === false)
    {
    
        $directory_traversal_error = "<font color=\"red\">An error occurred, please try again.</font>";
    
    }

    /*
    else
    {
    
        echo "Good path!";
    
    }     
     */
    
    return $directory_traversal_error;

}

代碼分析


函數一過濾了../ ..\ /.. .. , 函數二還過濾了. 但是繞過都很簡單,直接使用絕對路徑,不使用相對路徑即可,例如/etc/passwd。函數三則對相對路徑轉為絕對路徑后,和允許訪問的絕對路徑比對,如果不一致則禁止訪問,真正做到了約束和防御。

文件上傳



PHP代碼

function file_upload_check_1($file, $file_extensions  = array("asp", "aspx", "dll", "exe", "jsp", "php"), $directory = "images")
{
    
    $file_error = "";
    
    // Checks if the input field is empty
    if($file["name"] == "")
    {
        
        $file_error = "Please select a file...";
        
        return $file_error;
        
    }
    
    // Checks if there is an error with the file
    switch($file["error"])
    
    // URL: http://php.net/manual/en/features.file-upload.errors.php
    
    {
        
        case 1 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 2 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 3 : $file_error = "Sorry, the file was only partially uploaded. Please try again...";
                 break;
             
        case 6 : $file_error = "Sorry, a temporary folder is missing. Please try again...";
                 break;
             
        case 7 : $file_error = "Sorry, the file could not be written. Please try again...";
                 break;
             
        case 8 : $file_error = "Sorry, a PHP extension stopped the file upload. Please try again...";
                 break;
             
    }
    
    if($file_error)
    {
        
        return $file_error;
        
    }
    
    // Breaks the file in pieces (.) All pieces are put in an array
    $file_array = explode(".", $file["name"]);
    
    // Puts the last part of the array (= the file extension) in a new variabele
    // Converts the characters to lower case
    $file_extension = strtolower($file_array[count($file_array) - 1]);
    
    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(in_array($file_extension, $file_extensions))
    {
        
       $file_error = "Sorry, the file extension is not allowed. The following extensions are blocked: <b>" . join(", ", $file_extensions) . "</b>";
       
       return $file_error;
       
    }
    
    // Checks if the file already exists in the directory
    if(is_file("$directory/" . $file["name"]))
    {
        
        $file_error = "Sorry, the file already exists. Please rename the file...";      
        
    }
    
    return $file_error;
    
}

function file_upload_check_2($file, $file_extensions  = array("jpeg", "jpg", "png", "gif"), $directory = "images")
{
    
    $file_error = "";
    
    // Checks if the input field is empty
    if($file["name"] == "")
    {
        
        $file_error = "Please select a file...";
        
        return $file_error;
        
    }
    
    // Checks if there is an error with the file
    switch($file["error"])
    
    // URL: http://php.net/manual/en/features.file-upload.errors.php
    
    {
        
        case 1 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 2 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 3 : $file_error = "Sorry, the file was only partially uploaded. Please try again...";
                 break;
             
        case 6 : $file_error = "Sorry, a temporary folder is missing. Please try again...";
                 break;
             
        case 7 : $file_error = "Sorry, the file could not be written. Please try again...";
                 break;
             
        case 8 : $file_error = "Sorry, a PHP extension stopped the file upload. Please try again...";
                 break;
             
    }
    
    if($file_error)
    {
        
        return $file_error;
        
    }
    
    // Breaks the file in pieces (.) All pieces are put in an array
    $file_array = explode(".", $file["name"]);
    
    // Puts the last part of the array (= the file extension) in a new variabele
    // Converts the characters to lower case
    $file_extension = strtolower($file_array[count($file_array) - 1]);
    
    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(!in_array($file_extension, $file_extensions))
    {
        
       $file_error = "Sorry, the file extension is not allowed. Only the following extensions are allowed: <b>" . join(", ", $file_extensions) . "</b>";
       
       return $file_error;
       
    }
    
    // Checks if the file already exists in the directory
    if(is_file("$directory/" . $file["name"]))
    {
        
        $file_error = "Sorry, the file already exists. Please rename the file...";      
        
    }
    
    return $file_error;
    
}

代碼分析


上面的代碼對於防文件上傳不是特別典型,就不分析了,我們看一下正確的PHP文件上傳的代碼

  • 1、擴展名檢查(要白名單不能黑名單,一般就是允許jpg、jpeg、png等等)。
  • 2、重命名隨機新文件名,且該新文件名不返回到前端,這個時候一定要注意擴展名。
  • 3、對圖片進行二次渲染,如果不是上傳圖片文件就忽略這一點。
  • 4、文件轉存到一個路徑,該路徑不要暴露。
  • 5、保證服務器版本,避免解析漏洞。
<?php 
  //配置屬性
  $upload_path = '/var/www/images/';
  $target_file   =  md5(uniqid());
  $allow_extend_filenames = array('jpg', 'jpeg', 'png');//限定可以上傳的文件擴展名
  //文件獲取
  if(isset($_POST['file'])){
    checkToken($_REQUEST['csrfToken'], $_SESSION['csrfToken'], 'upload.php');
  }
  $file = $_FILES['file'];
  
  //獲取文件屬性
  $name = $file['name'];
  $type = $file['type'];
  $size = $file['size'];
  $tmp_name = $file['tmp_name']
  $extend_name = strtolower(substr($name, strrops($name, '.') + 1));

  //文件屬性更新
  $target_file = $upload_path . $target_file . '.';
  
  //白名單檢查
  $dot_count = substr_count('.');
  if ($dot_count > 1){
    return 'filename error!';
  }

  if(!in_array($extend_name, $allow_extend_filenames)){
    return 'filetype error!';
  }
  
  if ($type != 'image/jpeg' && $type != 'image/png'){
    return 'filetype error!';
  }

  //圖片二次渲染
  if($type == 'image/jpeg'){
    $img = imagecreatefromjpeg($tmp_name);
    imagejpeg($img, $target_file . 'jpg', 100);
  }
  else{
    $img = imagecreatefrompng($tmp_name);
    imagepng($img, $target_file . 'png', 9);
  }
  imagedestory($img);

  return 'upload success!';

  generateSessionToken();
 ?>

SSRF



關鍵問題是對內部可能發起訪問的地址,做白名單限制,或者按照自定義規則驗證合法性,而不是任意發起,沒有php級別的防御措施,是代碼邏輯防御級別的。

CSRF



防御方式


表單隱藏隨機token驗證的方式防御,或者彈出驗證碼防御,后者是功能邏輯防御,影響用戶體驗但更安全,代碼層面來介紹下前者。

防御代碼

<input type="hideen" value="token_value" name="token"></input>
if($_SESSION['id'] &&  $_POST['csrfToken']  == $_SESSION['csrfToken']){
    return true;
}
else{
    return false;
}

XXE的PHP級別防御



防御原理


由於simplexml_load_string函數的XML解析問題出在libxml庫上,所以加載實體前可以調用一個函數來禁用。

防御代碼


<?php libxml_disable_entity_loader(true); ?>

文件包含的防御


防御遠程文件包含

//PHP配置文件
allow_url_include = OFF

防御本地文件包含

//include類函數的參數不允許用戶控制,或者在白名單內做限制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM