• 跟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类函数的参数不允许用户控制,或者在白名单内做限制。
    
  • 相关阅读:
    九度OJ 1035:找出直系亲属(二叉树)
    while 循环的理解
    while 循环的理解
    count 变量的使用
    count 变量的使用
    数据结构与算法的实现 —— 结点定义与数据结构的选择
    数据结构与算法的实现 —— 结点定义与数据结构的选择
    最小生成树(MST,minimum spanning tree)
    最小生成树(MST,minimum spanning tree)
    Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
  • 原文地址:https://www.cnblogs.com/KevinGeorge/p/10362324.html
Copyright © 2020-2023  润新知