blubiu

My Love

上网不网恋,简直浪费电.


DVWA代码审计--文件上传


文件上传

怎么感觉这个系列的文章可以共用一个模板了。。(逃


原理:

文件上传漏洞是指用户上传了一个可执行的脚本文件,

并通过此脚本文件获得了执行服务器端命令的能力。


目录:


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

?> 

先学习学习函数!!!


basename() 函数返回路径中的文件名部分。

语法:basename(path,suffix)

参数:

  1. path 必需。规定要检查的路径。

  2. suffix 可选。规定文件扩展名。如果文件有名有文件扩展名,将不会显示这个扩展名。


move_uploaded_file() 函数将上传的文件移动到新位置。

语法: move_uploaded_file(file,newloc)

:本函数仅用于通过 HTTP POST 上传的文件。

参数:

  1. file 必需。规定要移动的文件。

  2. newloc 必需。规定文件的新位置。


$_FILES函数

$_FILES数组内容如下:

$_FILES['myFile']['name']   客户端文件的原名称。

$_FILES['myFile']['type']   文件的 MIME 类型,需要浏览器提供该信息的支持,例如”image/gif”。

$_FILES['myFile']['size']   已上传文件的大小,单位为字节(Byte)。

$_FILES['myFile']['tmp_name']   文件被上传后在服务端储存的临时文件名

$_FILES['myFile']['error']   和该文件上传相关的错误代码。

[‘error’] 有六种值,分别是:

UPLOAD_ERR_OK
值:0; 没有错误发生,文件上传成功。


UPLOAD_ERR_INI_SIZE
值:1; 上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。


UPLOAD_ERR_FORM_SIZE
值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。


UPLOAD_ERR_PARTIAL
值:3; 文件只有部分被上传。


UPLOAD_ERR_NO_FILE
值:4; 没有文件被上传。
值:5; 上传文件大小为0.


注:
文件被上传结束后,默认地被存储在了临时目录中,

这时必须将它从临时目录中删除或移动到其它地方,如果没有,则会被删除。


来解读一下代码,

if判断有没有文件上传,

如果有的话设置一个$target_path变量来接收文件上传的路径(hackable/uploads/),

然后使用basename() 函数获取上传的文件名,然后再拼接上传的路径,

为了方便看可以换成这样,

$target_path = $target_path.basename( $_FILES[ 'uploaded' ][ 'name' ] );

假如上传一个名为shell.php的文件,

$target_path 依然是不变的,为hackable/uploads/

然后basename() 函数获取文件名就为shell.php

拼接起来就是hackable/uploads/shell.php


这段代码没有对上传的文件后缀做任何的判断,

导致任意文件上传,

这里直接上传php后缀的一句话即可

上传成功后返回的路径

images


NO.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 ) ) {  // 100kb

        // 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>'; 
    } 
} 

?> 

这段代码也是挺简单的,

加了三个判断,判断类型是不是jpgpng,还有文件不能大于100kb

这里依旧没有判断文件后缀,只是检测了MIME 类型,也就是这个东西,

images


这里直接抓包将Content-Type 值改为image/jpeg 即可绕过

images


这种方法在渗透测试中也经常使用,

另外,如果不修改mime类型的话,此题没有其他绕过方法了。


NO.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 
            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>'; 
    } 
} 

?>

substr() 函数返回字符串的一部分。

语法: substr(string,start,length)

参数: string 必需。规定要返回其中一部分的字符串。

start 必需。规定在字符串的何处开始。
  正数 - 在字符串的指定位置开始
  负数 - 在从字符串结尾的指定位置开始
  0 - 在字符串中的第一个字符处开始

length 可选。规定要返回的字符串长度。默认是直到字符串的结尾。
  正数 - 从 start 参数所在的位置返回
  负数 - 从字符串末端返回


strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。

语法: strrpos(string,find,start)

参数:

  1. string 必需。规定被搜索的字符串。
  2. find 必需。规定要查找的字符。
  3. start 可选。规定开始搜索的位置。

例: 查找 “php” 在字符串中最后一次出现的位置 输出19

<?php
echo strrpos("I love php, I love php too!","php");
?>

相关函数:

  1. strpos() - 查找字符串在另一字符串中第一次出现的位置(区分大小写)
  2. stripos() - 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
  3. strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)

strtolower() 函数把字符串转换为小写。

语法: strtolower(string)

:该函数是二进制安全的。

  • 相关函数:
    • lcfirst() - 把字符串中的首字符转换为小写
    • strtoupper() - 把字符串转换为大写
    • ucfirst() - 把字符串中的首字符转换为大写
    • ucwords() - 把字符串中每个单词的首字符转换为大写

getimagesize() 函数用于获取图像大小及相关信息。

语法: array getimagesize ( string $filename [, array &$imageinfo ] )

这里的话是判断文件头是否为图片。


先来详解一下这段代码:

$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);

变量$uploaded_name接收上传的文件名,假如上传一个shell.php文件

先看看strrpos 函数

上传文件后就会变成 strrpos( 'shell.php', '.' )

意思是查找.出现的文章,然后会输出5,点的前一位,


再来看看substr 函数

然后就会变成 substr( "shell.php", 5 + 1) ,为什么要加一呢?

如果是5的话,则会截取.php,不包括5,会从第六位开始截取,

6的话则会截取到php,当然整段代码意思也很明确,就是获取文件后缀。


这个级别除了判断文件后缀之外,还判断了文件头,

普通一句话改成jpg都过不了了,

这里我们可以使用一句话图片马,

合成语法为: copy x.jpg /b + b.txt /a c.jpg

注意: x.jpg为图片(png,gif都行) ,b.txt为一句话 ,c.gif为最后欲生成的文件名

images


然后打开编辑器可以看到一句话

images


然后这里直接以jpg后缀上传,然后采用文件包含的方式访问,

http://localhost/dvwa/vulnerabilities/fi/?page=file:///D:\phpStudy\PHPTutorial\WWW\dvwa\hackable\uploads\c.jpg

发现可以解析

images


然后上菜刀就行了,注意: 这里的话可能需要菜刀上的浏览器登录一下dvwa才能连接shell,

浏览器左上角菜单栏可以打开,

本菜这里环境有点问题,就不演示了,感兴趣的朋友可以去试试。。


附1:文件上传可控点

  1. Content-Length,即上传内容大小
  2. MAX_FILE_SIZE,即上传内容的最大长度
  3. filename,即上传的文件名
  4. 上传的文件内容
  5. 请求包可控点可能存在上传路径

附2:上传绕过–客户端

  1. 用firebug将form表单中的onsubmit事件删除
  2. 上传文件,抓包拦截,修改数据

这些例子只是个参考,更多方法还要靠自己去研究。


附3:上传绕过–服务端

  1. 黑白名单过滤
  2. 修改MIME类型
  3. 截断上传
  4. .htaccess文件攻击
  5. 目录验证

总结

这些题目还是很局限性的,

有些是因为自己环境问题完成不了一些操作,

同时,题目的设计导致很多方法都不能够测试,

不过上传还是有很多绕过方法的,

比如配合解析漏洞等等


参考文献

https://www.freebuf.com/articles/web/119467.html
https://www.freebuf.com/vuls/128846.html