DVWA靶场实战(五)

五、File Upload:

1.漏洞原理:

  File Upload中文名叫做文件上传,文件上传漏洞是指用户上传了一个可执行脚本文件(php、jsp、xml、cer等文件),而WEB系统没有进行检测或逻辑做的不够安全。但文件上传功能本身没有问题,问题在于上传后如何处理以及解析文件。一般情况下,WEB应用都会允许上传一些文件,如头像、附件、等信息,如果Web应用没有对用户上传的文件有效的检查过滤,那么恶意用户就会上传一句话木马等Webshell,从而达到控制Web网站的目的。
  存在文件上传功能的地方就有可能存在文件上传漏洞,比如上传相册、头像上传、视频、图片分享、论坛发帖和邮箱等可以上传附件的地方也是上传漏洞的高危地带,另外像文件管理器这样的功能也有可能被攻击者所利用。
一般文件上传的漏洞可以是木马、病毒、恶意脚本或Webshell等。

2.漏洞利用:

 一般利用方式分为以下几种:
(1)上传webshell文件,攻击者可以通过这些网页后门执行命令并控制服务器。
(2)上传文件是钓鱼图片或包含了脚本的图片时,图片中的脚本,在某些版本的浏览器上会被作为脚本执行,从而被利用于钓鱼或者欺诈。
(3)上传病毒、木马文件,用于诱骗用户和管理员下载执行权限或者自动运行。
(4)上传文件是其他恶意脚本时,攻击者可直接执行脚本进行攻击。

3.常见防御方式:

(1)检查文件上传路径(避免0x00截断、IIS6.0文件解析漏洞、目录遍历)。
(2)文件扩展名检测(避免服务器以非图片的文件格式解析文件),验证文件扩展名通常有“黑名单”和“白名单”两种方式。
(3)文件MIME验证(比如GIF图片MIME为image/gif,CSS文件的MIME为text/css等)。
(4)图片二次渲染(最变态的文件上传漏洞防御方式,基本上完全避免了文件上传漏洞)。
(5)文件重命名(比如随机字符串或时间戳等方式,防止攻击者得到webshell的路径)。
(6)隐藏上传路径。
(7)文件内容检测(避免图片中插入webshell)。

4.绕过方式:

(1)前端验证:

主要是通过JavaScript代码进行检测,是最简单的绕过。绕过方法以下两种:
·只需要在浏览器内禁用JS就可以了。
·使用Burpsuite代理后上传符合要求的文件类型,后抓包修改文件类型。

(2)后端——扩展名检测:

  针对文件的扩展名后缀进行检测,主要通过黑白名单进行检测,若不符合过滤规则,则不允许上传。

①黑名单检测:

  一般专门有blacklist文件或列举出黑名单,里面包含常见的危险脚本文件。常见的绕过方式有以下几种:
    A.大小写绕过:服务端没有将后缀名转换为统一格式进行比对,导致可以上传的后缀为pHp的文件,又因为Windows操作系统大小写不敏感,所以.pHp会被当做php文件解析。重点函数语句“$file_ext = strtolower($file_ext); //转换为小写”。
    B.修改后缀名绕过:黑名单不允许上传.asp,.php,.jsp,.exe后缀的文件,但可以上传诸如“asa/cer/aspx”“php/php3/php4/php5/phtml/pht”“jspx/jspf”“exee”.当然前提是Apache的httpd.conf中配置如下代码:

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

    当我们修改文件名后,可以发现上传成功。
    C.重写(双写)绕过:服务端将黑名单的后缀名替换为空,但只能进行一次。上传.pphphp后缀,替换一个php为空,则后缀名变为.php,成功绕过。
    D.利用Windows的命名机制:shell.php,shell.php【空格】,shell.php:1.jpg,shell. php::$DATA,shell.php:1.jpg在Windows中,后缀名后面的点和空格都会被删除掉。
    E.解析漏洞:
    ·.htaccess文件解析漏洞
    ·apache解析漏洞
    ·IIS7.0或IIS7.5或nginx的解析漏洞
    ·IIS6.0解析漏洞
    F.截断上传:
    ·截断类型:PHP%00截断
    ·截断原理:由于00代表结束符,所以会把00后面的字符都截断
    ·截断条件:PHP版本小于5.3.4,PHP的magic_quotes_gpc为OFF状态

②白名单检测:

  白名单检测的原理一般是有一个专门的whitelist文件,里面会包含的正常文件有“jpg”“png”“gif”。绕过方式有以下几种:
  A.解析漏洞:
  ·.htaccess文件解析漏洞
  ·apache解析漏洞
  ·IIS7.0/IIS7.5/Nginx的解析漏洞
  ·IIS6.0解析漏洞
  B.截断上传:
  ·截断类型:PHP%00截断
  ·截断原理:由于00代表结束符,所以会把00后面的字符都截断
  ·截断条件:PHP版本小于5.3.4,PHP的magic_quotes_gpc为OFF状态
  C.文件包含

(3)后端检测——00截断:

  原理:虽然web应用做了校验,但是由于文件上传后的路径用户可以控制,攻击者可以利用手段添加字符串标识符0x00的方式来将后面的拼接的内容进行截断,导致后面的内容无效,而且后面的内容又可以帮助我们绕过黑白名单的检测。
  思路:在C语言中,空字符有一个特殊的含义,代表字符拼接结束。这里我们使用的是PHP语言,属于高级语言,底层靠C语言来实现,也就是说空字符的字符串拼接结束功能在PHP中也能实现。但是我们在URL中不能直接使用空,这样会造成无法识别;我们通过查看ASCII对照表,也发现第一个就空字符,它对应的16进制是00,这里我们就可以用16进制的00来替代空字符,让他后面的内容。
  绕过:使用burpsuite进行抓包,因为这里是通过URL进行传递的文件上传后存储路径,所以需要对16进制的00进行URL编码,编码的结果就是%00,通过这种方式,就可以用%00截断后面的内容,让拼接的文件名不再进行生效。

(4)后端检测——MIME检测:

  MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定的应用程序来打开。
  常见的MIME类型:

MIME类型 代表文件类型
text/plain 纯文本
text/html HTML文档
text/javascript JS代码
application/xhtml+xml XHTML文档
image/gif GIF图像
image/jpge JPGE图像
image/png PNG图像
video/mpeg MPEG动画
Application/octet-stream 二进制数据
Application/pdf PDF文档

  检测方式:在文件上传过程中,服务端会针对我们的上传的文件生成一个数组,这个数组其中有一项就是这个文件的类型file_type;服务端对文件检测时,就是通过检测脚本中的黑白名单和这个数组中的file_type进行对比,如果符合要求就允许上传这个文件。
  MIMIE绕过原理:部分web应用系统判断文件类型是通过content-type字段,黑客可以通过抓包,将content-type字段改为常见的图片类型,如image/gif,从而绕过校验。

(5)后端检测——内容检测图片马绕过:

  原理:一般内容验证使用getimagesize函数检测,判断文件是否是一个有效的文件图片,如果是,则允许上传,否则的话不允许上传。
  图片马制作:直播一张内容,这里为a.png,和一个一句话木马,通过以下命令合成一个图片马3.php:a.php内容“<?php phpinfo(); ?>”,命令为“copy a.png /b + a.php /a 3/php”,这条命令的意思就是:通过copy命令,把a.png图片文件,以二进制文件形式添加到a.php文件中,以ASCII文本形式输出为3.php文件。

(6)后端检测——文件头检测漏洞:

  原理:在每一个文件(包括图片,视频或其他的非ASCII文件)的开头(十六进制表示)实际上都会一片区域来显示这个文件的实际用法,这就是文件头标志。我们可以通过16进制编辑器打开文件,添加服务器允许绕过检测的文件头。
  常见文件头:
  ·GIF:47 49 46 38 39 61
  ·PNG:89 50 4E 47 0D 0A 1A 0A
  ·JPG:FF D8 FF E0 00 10 4A 46 49 46
  在进行文件头绕过时,我们可以把上面的文件头添加到我们的一句话木马内容最前面,达到绕过文件头检测的目的。

(7)解析漏洞:

①.htaccess文件解析漏洞:

  漏洞利用前提:web具体应用没有禁止.htaccess文件的上传,同时web服务器提供商允许用户上传自定义的.htaccess文件。
  原理:.htaccess文件(或者“分布式配置文件”),全程Hyertext Access(超文本入口)。提供了针对目录改变配置的方法,即,在一个特定的文档目录放置一个包含或多个指令的文件,以作用于此目录及其所有的子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
  利用方式:上传覆盖.htaccess文件,重写解析规则,将上传的带有脚本的图片以脚本方式解析。
.htaccess文件内容:

<FilesMatch "evil.gif">
SetHandler application/x-httpd-php #在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行
AddHandler php5-script .gif #在当前目录下,如果匹配到evil.gif文件,则被解析成PHP代码执行
</FilesMatch>

②Apache解析漏洞:

  原理:Apache解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。比如test.php.a.b的“.a”和“.b”这两种后缀是apache不可识别解析,apache就会把test.php.a.b解析成test.php。

③IIS6.0解析漏洞:

  IIS6.0解析漏洞分两种:

  1)目录解析:

    以xx.asp命名的文件夹里的文件都将会被当成ASP文件执行。

  2)文件解析:

    xx.asp;.jpg像这种畸形文件名存在“;”后面的直接忽略,也就是说当成xx.asp文件执行
  注:IIS6.0默认的可执行文件除了asp还包含这三种.asa,.cer,.cdx。

④IIS7.0 / IIS7.5 / Nginx的解析漏洞:

  原理:Nginx拿到文件路径(更专业的说法URL)/test.jpg/test.php后,一看后缀是.php,便认为该文件是php文件,转交给php去处理。php一看/test.jpg/test.php不存在,便删去最后的/test.php,又看/test.jpg当成要执行的文件了,又因为后缀为.jpg,php认为这不是php文件,于是返回Access denied。这其中涉及到php的一个选项:cgi.fix_pathinfo,该默认值为1,表示开启。开启这一选项PHP可以对文件路径进行修理。
  举个例子,当php遇到文件路径/1.jpg/2.txt/3.php时,若/1.jpg/2.txt/3.php不存在,则会去掉最后的/3.php,然后判断/1.jpg/2.txt是否存在。若存在,则把/1.jpg/2.txt当做文件/1.jpg/2.txt/3.php,若/1.jpg/2.txt仍不存在,则继续去掉/2.txt,以此类推。
  漏洞形式:www.xxxxx.com/UploadFiles/image/1.jpg/1.php
  另外两种解析漏洞:
  www.xxxxx.com/UploadFiles/image/1.jpg%00.php
  www.xxxxx.com/UploadFiles/image/1.jpg/%20\0.php

(8)二次渲染漏洞:

  原理:在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站的对应的标签进行显示。
  绕过:

  ①配合文件包含漏洞:

  将一句话木马插入到网站二次处理后的图片中,也就是把一句话插入图片在二次渲染后会保留的那部分数据里,确保不会在二次处理时删除掉。这样二次渲染后的图片中存在了一句话,在配合文件包含漏洞获取webshell。

  ②配合条件竞争:

  这里二次渲染的逻辑存在漏洞,先将文件上传,之后再判断,符合就保存,不符合就删除,可可利用条件竞争来进行爆破上传。
注:如何判断图片是否被二次处理,主要是用16进制编辑器打开图片查看上传后保留了哪些数据,查看哪些数据被改变了。

5.实战:

(1)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>";
    }
}

?>

  从代码来看,这里没有什么防御措施,所以我们直接上传木马就可以getshell了。然后我们制作一个木马尝试上传,直接显示上传成功。

 

  上传成功以后,这里我们可以看见给我们爆了上传文件的存储路径。然后我们访问,在原本路径上加上“../../hackable/uploads/1.php succesfully uploaded!”就可以了。空白页面,证明上传成功。

  然后我们在哥斯拉中尝试链接,显示“Success!”说明添加成功。

(2)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>';
    }
}

?>

 

  对文件上传的类型做了大小限制,要求必须是image/jpeg或者image/png类型的。
  我们先打开burpsuite,接着开始上传一句话木马,这次我们修改为2.php避免冲突,然后进行抓包的操作,得到下面这个包进行修改就可以了。

  这里显示成功了,就说明上传成功,我们尝试访问这个链接,发现空白,就证明上传成功,也成功运行了一句话木马。

 

  我们进行链接,链接成功,攻击成功。

 

 

(3)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>';
    }
}

?>

  可以看见这里又添加一个getimagesize(string filename)函数,它会通过读取文件头,返回图片的长、宽等信息,如果没有相关图片文件头,函数会报错。这里读取的文件名中最后一个“.”后的字符串,期望通过文件名来限制文件类型,因此要求文件名形式必须是“.jpg”“.jpeg”“.png”之一。同时,getimagesize函数更像是限制了上传文件的文件头必须为图像类型。
  首先新建一个文件夹,将准备好的一句话木马和图片放入文件夹,在路径栏目输入“CMD”打开此文件夹的CMD命令窗口。

 

  输入如下指令“copy [一句话木马名称]/b+[图片名称]/a [新图片名称]”,显示如下即为成功。

  上传显示如下即为成功上传,我们得到路径“../../hackable/uploads/111.jpg”,访问路径,得到如下的图片。

  然后我们切换到Command Injection输入命令“127.0.0.1|move …/…/hackable/uploads/111.jpg …/…/hackable/uploads/111.php”将文件名修改。

  然后我们这里连接哥斯拉,显示“Success!”表示成功。

 

(4)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}</a> 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();

?>

  这个级别的文件上传对上传的文件进行了重命名(MD5的加密),还增加了token值的校验,对文件的内容也做了严格的检查。