文件上传

    • 文件上传验证

      文件头验证

      jpg格式图片,文件头为JFIF

      gif格式的图片,文件头为GIF89a

      png格式的图片,文件头为PNG

      绕过:修改文件头,伪造文件属性

      文件类型验证

      抓包中的发送包里的

      捕获

      Content-Type

      图片格式:image/jpeg

      脚本格式:application/octet-stream

      含有上传文件属性

      绕过:修改文件类型,伪造文件类型,MIME绕过

      文件后缀验证

      绕过:增加多重后缀,伪造文件后缀,命名绕过

      常用绕过后缀名

      php,php3,php4,php5,phtml.pht
      

      大小写绕过

      windows 系统会忽略大小写。 而Linux系统默认对大小写敏感,如忽略则需要特殊配置

      双写绕过

      waf 常用手段

      服务端逻辑对不合法的后缀名进行了替换为空

      那我们可以扩充下思路

      例如后端限制了php,如果发现我们上传的文件名后缀为php 就替换为空

      a.php -> .php -> a a.pphphp ->php -> a.php

      ::$DATA

      在后缀加 ::$DATA Windows特性,不会解析

      点绕过

      Windows下xx.jpg[空格] 或者xx.jpg.这两类文件都是不允许存在的, 若这样命名,windows会默认去除空格或点

      空格绕过

      Windows下xx.jpg[空格] 或者xx.jpg.这两类文件都是不允许存在的, 若这样命名,windows会默认去除空格或点

      点空格点绕过

      Windows下xx.jpg[空格] 或者xx.jpg.这两类文件都是不允许存在的, 若这样命名,windows会默认去除空格或点

      服务器上传漏洞

      服务端验证

      1:MIME类型检测绕过

      检测原理 当用户上传文件到服务器端的时候,服务器端的程序会获取上传文件的MIME类型,然后用这个获取到的类型来和期望的MIME类型进行匹配,如果匹配不上则说明上传的文件不合法。服务端检测MIME类型的代码如下:

      if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')){
      
       ...//判断过后对文件处理的进一步操作
      }
      

      绕过方法

      因为服务端检测的是文件的MIME类型,而对这个MIME类型的的值的获取是通过HTTP请求字段里的Content-Type字段 ,所以绕过的方法就是通过修改Content-Type的值,比如修改为image/jpeg;image/png;image/gif等等允许上传类型对应的MIME值

      2:黑名单绕过

      检测原理

      文件类型根据黑名单来检测的原理就是:服务器程序根据一份文件后缀名的名单来判断是否允许当前文件上传到服务器,只要上传的文件的类型能够和这个黑名单里面的类型匹配,那么就禁止该文件上传

      绕过方法

      \1. 文件名大小写绕过 用像AsP, pHp之类的文件名绕过黑名单检测

      \2.名单列表绕过

      用黑名单里没有的名单进行攻击,比如黑名单里没有asa或cer之类 \3. 特殊文件名绕过 比如发送的 http包里把文件名改成 test.asp. 或 test.asp_(下划线为空格),这种命名方式 在windows系统里是不被允许的,所以需要在 burp之类里进行修改,然后绕过验证后,会 被windows系统自动去掉后面的点和空格,但要注意Unix/Linux系统没有这个特性。

      3.%00截断上传

      前提:需要magic_quotes_gpc=offphp版本要小于5.3.4,5.3.4及以上已经修复该问题

      %00为截断符

      绕过方法:

      GET方法:抓包时先上传xx.jpg,再使用bp截断

      img

      由于root.php被截断,所以直接访问root.php就可以了

      img

      POST方法:%00进行url decode,转换为空字符,进行截断(GET可以url自动转码的,但POST不会)

      img

      也可以在hex处进行进行修改

      在root.php后加一个字符 +

      +对应的hex为2b

      将2b改成00就进行截断

      img

      JS本地验证

      判断JS本地验证方法
      源代码
      <?php
      02  /**
      03   * Created by 独自等待
      04   * Date: 14-1-22
      05   * Time: 下午7:19
      06   * Name: upload1.php
      07   * 独自等待博客:http://www.waitalone.cn/
      08   */
      09  //文件上传漏洞演示脚本之js验证
      10  $uploaddir = 'uploads/';
      11  if (isset($_POST['submit'])) {
      12      if (file_exists($uploaddir)) {
      13          if (move_uploaded_file($_FILES['upfile']['tmp_name'], $uploaddir . '/' . $_FILES['upfile']['name'])) {
      14              echo '文件上传成功,保存于:' . $uploaddir . $_FILES['upfile']['name'] . "n";
      15          }
      16      } else {
      17          exit($uploaddir . '文件夹不存在,请手工创建!');
      18      }
      19      //print_r($_FILES);
      20  }
      21  ?>
      22  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      23      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      24  <html xmlns="http://www.w3.org/1999/xhtml">
      25  <head>
      26      <meta http-equiv="Content-Type" content="text/html;charset=gbk"/>
      27      <meta http-equiv="content-language" content="zh-CN"/>
      28      <title>文件上传漏洞演示脚本--JS验证实例</title>
      29      <script type="text/javascript">
      30          function checkFile() {
      31              var file = document.getElementsByName('upfile')[0].value;
      32              if (file == null || file == "") {
      33                  alert("你还没有选择任何文件,不能上传!");
      34                  return false;
      35              }
      36              //定义允许上传的文件类型
      37              var allow_ext = ".jpg|.jpeg|.png|.gif|.bmp|";
      38              //提取上传文件的类型
      39              var ext_name = file.substring(file.lastIndexOf("."));
      40              //alert(ext_name);
      41              //alert(ext_name + "|");
      42              //判断上传文件类型是否允许上传
      43              if (allow_ext.indexOf(ext_name + "|") == -1) {
      44                  var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
      45                  alert(errMsg);
      46                  return false;
      47              }
      48          }
      49      </script>
      50  <body>
      51  <h3>文件上传漏洞演示脚本--JS验证实例</h3>
      52  
      53  <form action="" method="post" enctype="multipart/form-data" name="upload" onsubmit="return checkFile()">
      54      <input type="hidden" name="MAX_FILE_SIZE" value="204800"/>
      55      请选择要上传的文件:<input type="file" name="upfile"/>
      56      <input type="submit" name="submit" value="上传"/>
      57  </form>
      58  </body>
      59  </html>
      
      
      观察反馈速度

      一点击上传,若立即弹窗,基本为本地验证

      若需要一定时间,可能为服务端验证

      因为本地到本地速度比本地到服务端要快

      绕过方法
      1.删除对js验证脚本的调用

      复制网页源代码

      创建html文件

      复制源代码到html文件

      删除这一段代码

      29      <script type="text/javascript">
      30          function checkFile() {
      31              var file = document.getElementsByName('upfile')[0].value;
      32              if (file == null || file == "") {
      33                  alert("你还没有选择任何文件,不能上传!");
      34                  return false;
      35              }
      36              //定义允许上传的文件类型
      37              var allow_ext = ".jpg|.jpeg|.png|.gif|.bmp|";
      38              //提取上传文件的类型
      39              var ext_name = file.substring(file.lastIndexOf("."));
      40              //alert(ext_name);
      41              //alert(ext_name + "|");
      42              //判断上传文件类型是否允许上传
      43              if (allow_ext.indexOf(ext_name + "|") == -1) {
      44                  var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
      45                  alert(errMsg);
      46                  return false;
      47              }
      48          }
      49      </script>
      

      再到末尾的

      form action=""
      

      填入URL指向地址

      再打开文件进行上传

      2.抓包后改类型

      利用burpsuite抓包,修改文件类型进行绕过-首先将我们想要上传的恶意脚本的后缀更改为符合要求的文件类型后缀如:webshell.php -> webshell.jpg-当点击上传的时候使用burp进行抓包,将名字的后缀改回.php,以便上传至服务器能够正确解析

      web应用程序解析绕过

      1. Apache解析漏洞

      解析:test.php.(任意不属于黑名单且也不属于Apache解析白名单的名称),比如test.php.lala(加空格也可以) 描述:一个文件名为test.x1.x2.x3的文件,apache会从x3的位置开始尝试解析,如果x3不属于apache能够解析的扩展名,那么apache会尝试去解析x2,直到能够解析到能够解析的为止,否则就会报错

      2. IIS解析漏洞

      解析 :test.asp/(任意文件名)|test.asp;(任意文件名) | (任意文件名)/(任意文件名).php

      描述:IIS6.0在解析asp格式的时候有两个解析漏洞,一个是如果目录名包含".asp"字符串, 那么这个目录下所有的文件都会按照asp去解析,另一个是只要文件名中含有".asp;"会优先按asp来解析

      IIS7.0/7.5是对php解析时有一个类似于Nginx的解析漏洞,对任意文件名只要在URL后面追加上字符串"/任意文件名.php"就会按照php的方式去解析;

      .htaccess文件攻击

      .htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置.通过htaccess文件,可以实现:网页301重定向、自定义404页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

      配合名单列表绕过,上传一个自定义的.htaccess,就可以轻松绕过各种检测

      建一个.htaccess 文件,里面的内容如下:

      <FilesMatch "pino">
      SetHandler application/x-httpd-php
      </FilesMatch>
      

      它将该目录下匹配的pino文件解析为php执行

      这个时候就上传一个文件名字是pino,这个时候我们上传一个文件名字叫做pino的文件,不要后缀名,然后里面是一句话木马,用菜刀连接,可以成功

      .user.ini文件攻击

      自 PHP 5.3.0 起,PHP 支持基于每个目录的 INI 文件配置。此类文件 被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。

      除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。

      这里就很清楚了,.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。

      指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:

      auto_prepend_file=1.png
      

      1.png是要包含的文件。

      所以,我们可以借助.user.ini轻松让所有php文件都“自动”包含某个文件,而这个文件可以是一个正常php文件,也可以是一个包含一句话的webshell。

      上传成功后,过段时间(大概5分钟),访问上传目录下的php文件,像index.php,就会包含的文件当php文件执行,如果上传cat flag的函数,则访问index.php就出现flag;如果上传一句话木马,则将index.php所在位置当地址填入菜刀或蚁剑

      ZIP上传实现目录穿越

      在文件上传中,有些上传支持ZIP压缩包文件,或将不是压缩包格式的文件进行压缩再上传,后端再进行解压。

      很多程序会在解压完后检测目录下是否存在脚本文件,有则进行删除(readdir函数获取目录下所有文件)

      foreach($archive->listContent() as $value){
                      $filename = $value["filename"];
                      if(preg_match('/\.php$/', $filename)){
                           exit("压缩包内不允许含有php文件!");
                       }
                  }
      

      例如这段代码用正则匹配过滤php后缀的文件

      实现绕过

      1、解压产生异常实现绕过

      有些程序会在解压失败后立即退出程序

      这时可以构造解压到一半失败的ZIP包,用010Editor修改ZIP包,在xxx.php后内容修改为0xff,保存上传

      2、解压特殊文件实现绕过

      有些代码在解压失败后会删除文件

      则上述方法不可行

      如果在解压文件时解压出文件名含有“../”字符实现目录穿越跳出上传目录,则不会被check_dir删除

      用010Editor打开含有php文件的压缩包

      找到含有文件名的那行进行修改

      image-20210327010924363

      image-20210327011143744

      ../实现跳跃上级,.php后的后缀名利用Apache解析漏洞,匹配到不存在的后缀名继续向左匹配,直到可以解析为止,最终跳跃到根目录下,直接访问可以得到flag

      3、制作软连接

      当apache不能解析php时,可以制作软连接进行链接到flag或读取根目录

      题目提示在磁盘根目录放了名为flag的文件

      因此将软连接链接到根目录的flag,名为a

      ln -s /flag a
      

      如果不清楚flag在哪,可以先读取根目录

      ln -s / a
      

      然后将软连接a打包成b.zip

      zip --symlinks b.zip a
      

      然后上传到服务器自动解压,点击a就可下载flag

posted @   Hermitaria  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示