DVWA靶机之文件上传漏洞通关笔记

简介

文件上传漏洞通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限,因此文件上传漏洞带来的危害常常是毁灭性的。

环境搭建

关于docker的安装:

https://www.docker.com/

通过Docker一键搭建 docker run --rm -it -p 80:80 vulnerables/web-dvwa 搭建完成后,访问http://localhost即可进入

测试工具

  • Burp Suite Community Edition
  • Firefox Browser

文件上传操作流程(本段内容来自参考资料2)

前端提交

在前端上传文件的form表单中,<form>标签的属性值enctype属性会规定发送到服务器之前对表单的数据进行何种编码 它的常见值如下:

application/x-www-form-urlencoded: 在发送前编码所有字符(默认)

multipart/form-data: 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。

text/plain: 空格转换为 "+" 加号,但不对特殊字符编码。

后端处理

PHP会用$_FILES数组接收参数 $_FILES的内容为:

[name] => feng.jpeg     文件的名称
[type] => image/jpeg     文件的MIME类型
[tmp_name] => C:\Users\Administrator\AppData\Local\Temp\php2007.tmp  文件的临时位置
[error] => 0       文件的错误信息  0 ok      1234 error
[size] => 2859    文件的大小,单位:byte 1M=1024KB 1KB=128Byte

文件上传漏洞的利用条件

  1. 能够成功上传木马文件
  2. 上传文件必须能够被执行
  3. 上传文件的路径必须可知

Security Level: low

源代码:

<?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
        $html .= '<pre>Your image was not uploaded.</pre>';
    }
    else {
        // Yes!
        $html .= "<pre>{$target_path} succesfully uploaded!</pre>";
    }
}

?>

源代码分析:

代码流程

文件上传文件后,文件直接保存,保存路径为hackable/uploads/,文件上传成功后,返回succesfully。 函数basename():返回路径中的文件名部分 函数

move_uploaded_file ( string $filename , string $destination ):将文件移动到指定地址

防御措施:无

测试方法:

本关没有任何保护措施,上传成功后会返回文件路径地址,可直接上传恶意脚本文件进行攻击


Security Level: Medium

源代码:

<?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
            $html .= '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            $html .= "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        $html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

源代码分析:

代码流程

当有提交上传文件时,后端先用变量从$_POST['uploaded']取出文件路径、文件名、文件类型、文件大小;然后对文件MIME类型进行判断,如果文件类型为image/jpeg或者image/png并且文件大小小于100000byte,则将临时文件移动到指定目录,上传成功,则回显文件路径

防御措施

  • MIME类型白名单过滤检测
  • 上传文件大小限制

测试方法:

上传我们准备好的恶意脚本文件,通过Burp Suite抓包拦截

Content-Type的值修改为image/png,即可绕过检测,成功上传,访问该文件即可执行恶意脚本


Security Level: High

源代码:

<?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
            $html .= '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            $html .= "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        $html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

源代码分析:

代码流程

当有提交上传文件时,后端先用变量从$_POST['uploaded']取出文件路径、文件名、文件扩展名、文件大小,临时文件名称;然后进行判断,如果文件扩展名为jpgpngjpeg,且文件大小小于100000byte,并且能获取到图像信息,则将临时文件移动到指定目录,上传成功,则回显文件路径 函数strtolower():将所有字母转换为小写 函数getimagesize():获取图像信息

防御措施

  • 大小写统一
  • 基于白名单的扩展名过滤
  • 上传的文件大小限制
  • 图像信息检测

文件头知识补充(本段内容来自参考资料2)

常见的图片格式的文件头标识如下:

JPEG/JPG - 文件头标识 (2 bytes): FF D8 (SOI) (JPEG 文件标识) - 文件结束标识 (2 bytes): FF D9 (EOI) 

PNG - 文件头标识 (8 bytes)   89 50 4E 47 0D 0A 1A 0A

GIF - 文件头标识 (6 bytes)   47 49 46 38 39(37) 61 |GIF89(7)a

文件头欺骗:伪造文件头,使文件头标识一样,其它部分我们修改为一句话木马,也就成了我们常说的图片一句话。

测试方法:

思路

绕过扩展名检测和getimagesize()图像信息检测,通过制作图片马可以绕过图像信息检测,通过00截断可以绕过扩展名检测(适用于 php 小于 5.3.4 版本),通常图片马需要配合文件包含才能发挥作用

制作图片马

(windows的copy命令) 准备好恶意脚本文件(x.php)和图片文件(y.png),在cmd下执行如下命令

copy y.png/b+x.php/a z.png

即可生成图片马

%00截断上传

由于我搭建的环境是PHP7.2,%00截断在此不适用

文件上传+文件包含组合利用: 通过我们刚刚上传的图片一句话木马,借助High Security Level的文件包含漏洞来进行组合利用:

http://your-dvwa-ip/vulnerabilities/fi/?page=file:///var/www/dvwa/hackable/uploads/c.jpg

访问如上地址即可成功执行我们的恶意脚本(这里用phpinfo()代替)


Security Level: impossible

源代码:

<?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!
            $html .= "<pre><a href='${target_path}${target_file}'>${target_file}</ succesfully uploaded!</pre>";
        }
        else {
            // No
            $html .= '<pre>Your image was not uploaded.</pre>';
        }

        // Delete any temp files
        if( file_exists( $temp_file ) )
            unlink( $temp_file );
    }
    else {
        // Invalid file
        $html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

源代码分析:

代码流程

当有提交上传文件时,后端先用变量从$_POST['uploaded']取出文件路径、文件名、文件扩展名、文件大小,临时文件名称;通过uniqid()生成唯一的ID拼接文件名进行md5加密再拼接上扩展名形成新的文件名,对临时文件也进行此操作,如果扩展名为jpg,jpeg,png且文件大小小于100000byte,且MIME类型为image/png或者image/jpeg并且能取得图像信息,则重新生成图像进行上传

函数uniqid():基于以微秒计的当前时间,生成一个唯一的 ID。
函数ini_get():函数返回相应选项的值
函数sys_get_temp_dir(): 返回用于临时文件的目录
函数imagecreatefromjpeg(filename):函数返回图片文件的图像标识,失败返回false
函数imagejpeg(image,filename,quality):从image图像以filename为文件名创建一个JPEG图像,可选参数quality,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。
函数imagedestroy():函数销毁图像资源
函数rename():重命名文件或目录
函数getcwd():获取当前工作目录

防御措施

  • 重命名文件名并进行md5加密,%00截断失效
  • 基于白名单的扩展名,MIME类型检测
  • 服务器文件内容检测
  • 文件大小限制
  • 加入Anti-CSRF token 防护 CSRF攻击

测试方法:

无解

##  参考资料:
1. DVWA_File Upload 文件上传:https://www.cnblogs.com/huangming-zzz/p/9900435.html
2. DVWA File Upload 通关教程:http://www.storysec.com/dvwa-file-upload.html
3. 《DVWA漏洞测试平台分析》
4. 通过文件头标识判断图片格式:http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html
5. windows中copy命令详解:https://www.cnblogs.com/andr01la/p/5146422.html

原文发布于微信公众号 - 小白帽学习之路(bat7089)

原文发表时间:2019-05-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券