6

文件上传漏洞靶场分析 UPLOAD_LABS - Web_Kio

 1 year ago
source link: https://www.cnblogs.com/Webkio/p/16394731.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

文件上传漏洞靶场(作者前言)

1653484309631-1a2c5ebb-06e4-4be8-a19d-8e94b655589c.png#clientId=uec928b06-22ba-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=430&id=ufefbd298&margin=%5Bobject%20Object%5D&name=image.png&originHeight=537&originWidth=1523&originalType=binary&ratio=1&rotation=0&showTitle=false&size=53508&status=done&style=none&taskId=u821c7b02-3c3d-4414-8949-93871bf1e3c&title=&width=1218.4

文件上传漏洞


PASS 1)

function checkFile() {
  var file = document.getElementsByName('upload_file')[0].value;
  if (file == null || file == "") {
    alert("请选择要上传的文件!");
    return false;
  }
  //定义允许上传的文件类型
  var allow_ext = ".jpg|.png|.gif";
  //提取上传文件的类型
  var ext_name = file.substring(file.lastIndexOf("."));
  //判断上传文件类型是否允许上传
  if (allow_ext.indexOf(ext_name + "|") == -1) {
    var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
    alert(errMsg);
    return false;
  }
}

这是一串JS前端语言编写的文件上传 这边限制上传的文件类型为JPG;PNG;GIF三种
通过file.substring(file.lastIndexof(".")); #获取文件后缀名 并判断文件的后缀名是否等于允许的文件类型
没有对文件名后缀做过滤限制

*JS是前端开发语言
1.更改php文件后缀名,上传图片文件时可以通过Burpsuite 进行拦截请求数据包并更改文件后缀名
2.F12查看前端代码 并复制 程序执行的过滤,按需求对其进行删改操作
3.修改form表单 定义action 进行直接上传的操作

  1. 验证文件真实性
  2. 过滤空格、点、大小写、双写、流文件

PASS 2)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']            
        if (move_uploaded_file($temp_file, $img_path)) {
          $is_upload = true;
        } else {
          $msg = '上传出错!';
        }
    } else {
      $msg = '文件类型不正确,请重新上传!';
    }
  } else {
    $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
  }
}

if
(($_FILES['upload_file']['type'] == 'image/jpeg') ||
($_FILES['upload_file']['type'] == 'image/png') ||
($_FILES['upload_file']['type'] == 'image/gif'))
这边仅对上传的文件类型做了过滤 没有对上传的文件后缀做过滤 且请求方式为POST

  1. 上传php文件时使用Burpsuite 更改请求头中的内容类型(content-type:image/jpeg:png:gif) 任意一种允许的文件类型。
  2. 上传转格式的包含php执行程序的图片文件,使用Burpsuite 更改后缀名

1.验证图片文件的真实性
2.过滤上传文件的后缀名


PASS 3)


$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array('.asp','.aspx','.php','.jsp');
    $file_name = trim($_FILES['upload_file']['name']);
    $file_name = deldot($file_name);//删除文件名末尾的点
    $file_ext = strrchr($file_name, '.');
    $file_ext = strtolower($file_ext); //转换为小写
    $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
    $file_ext = trim($file_ext); //收尾去空
    
    if(!in_array($file_ext, $deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;            
      if (move_uploaded_file($temp_file,$img_path)) {
        $is_upload = true;
      } else {
        $msg = '上传出错!';
      }
    } else {
      $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

$deny_ext = array('.asp','.aspx','.php','.jsp');
这边使用黑名单防御方法 不允许 后缀为.asp ; .aspx ; .php ; .jsp 的后端语言文件
if(!in_array($file_ext, $deny_ext))
取出用户后缀名,如果不存在限制的后缀名 则执行接下来的操作

  1. 使用php文件时 可以使用其他的php语言支持的后缀名 比如php3 phtml

通过查阅apache配置文件 httpd.conf中的 AddType application/x-httpd-php .php .php3 .phtml
这是phpstudy(PHP 5.2.17) apache配置文件中默认配置支持的文件.php .php3 .phtml

application/x-httpd-php :指定应用程序php的可用后缀名
原理:Addtype 添加类型 可以手动指定任意后缀名让apache服务解析至PHP文件 认定位php程序并执行
···比如: AddType application/x-httpd-php .abc 这里以.abc为后缀的文件将被认为是php执行文件

  1. 使用.htaccess 分布式配置文件 将上传目录的中的所有文件的执行策略更改为php执行程序
  1. 过滤完整的后端执行后缀名
  2. 过滤分布式配置文件.htaccess
  3. 使用白名单防御方法(只允许)

PASS 4)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_name = deldot($file_name);//删除文件名末尾的点
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //收尾去空

        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

$deny_ext = array
(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2","pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf");
以上源码 没有对.htaccess配置文件进行限制

$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
以上源码 trim函数使用两次

  1. 上传.htaccess 分布式配置文件 修改文件执行策略 针对目录改变配置的方法
    SetHandle application/x-httpd-php
    告诉apache 当前目录下的所有后缀名解析为php

<FilesMatch "file_name.png">
SetHandler application/x-httpd-php

告诉apache,只对当前目录下 file_name.png 文件当作php代码执行,其他文件照常不变

  1. 上传文件时使用BP拦截请求消息包 更改后缀名为.php. .绕过

程序执行时从上至下执行首先对文件名进行首尾清除空格
接着删除文件末尾的.
最后删除文件末尾的空格

即file.php. . 上传时
执行deldot($file_name)>
file.php.$nbsp 上传中执行trim($file_ext)>
留下file.php.

  1. 过滤htaccess 限制上传此文件
  2. 使用白名单防御方法 只允许想要上传的格式(白名单和黑名单防御方法根据需求定)

PASS 5)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
    $file_name = trim($_FILES['upload_file']['name']);
    $file_name = deldot($file_name);//删除文件名末尾的点
    $file_ext = strrchr($file_name, '.');
    $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
    $file_ext = trim($file_ext); //首尾去空
    
    if (!in_array($file_ext, $deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
      if (move_uploaded_file($temp_file, $img_path)) {
        $is_upload = true;
      } else {
        $msg = '上传出错!';
      }
    } else {
      $msg = '此文件类型不允许上传!';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
以上源码 trim函数使用两次,且没有对后缀名的大小写进行过滤 —— strtolower() 将字符转为小写

  1. 利用win系统的特性因为大小写不敏感 可以使用大小写绕过的方法绕过 即.PHp 这一特性不适用Linux系统
  2. 双写后缀名格式

上传文件时使用BP拦截请求消息包 更改后缀名为.php. .绕过
即file.php. . 上传时
执行deldot($file_name)>
file.php.$nbsp 上传中执行trim($file_ext)>
留下file.php.

  1. 白名单防御方法 只允许上传图片格式的文件(白名单和黑名单防御方法根据需求定)
  2. 限制大小写 转换为小写

PASS 6)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
    $file_name = $_FILES['upload_file']['name'];
    $file_name = deldot($file_name);//删除文件名末尾的点
    $file_ext = strrchr($file_name, '.');
    $file_ext = strtolower($file_ext); //转换为小写
    $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
    
    if (!in_array($file_ext, $deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
      if (move_uploaded_file($temp_file,$img_path)) {
        $is_upload = true;
      } else {
        $msg = '上传出错!';
      }
    } else {
      $msg = '此文件不允许上传';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

这里没有使用trim() 清除字符首尾空格函数 可以利用系统特性后缀名末尾为空格即为空这一文件

  1. 使用BP拦截请求消息 更改后缀名+空格 即.php+%20
  1. 白名单防御法 只允许想要上传进入的文件格式
  2. 使用trim函数

PASS 7)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
    if (file_exists(UPLOAD_PATH)) {
        $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
        $file_name = trim($_FILES['upload_file']['name']);
        $file_ext = strrchr($file_name, '.');
        $file_ext = strtolower($file_ext); //转换为小写
        $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
        $file_ext = trim($file_ext); //首尾去空
        
        if (!in_array($file_ext, $deny_ext)) {
            $temp_file = $_FILES['upload_file']['tmp_name'];
            $img_path = UPLOAD_PATH.'/'.$file_name;
            if (move_uploaded_file($temp_file, $img_path)) {
                $is_upload = true;
            } else {
                $msg = '上传出错!';
            }
        } else {
            $msg = '此文件类型不允许上传!';
        }
    } else {
        $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
    }
}

$file_name = trim($_FILES['upload_file']['name']);
$file_ext = trim($file_ext); //首尾去空
# trim() 函数移除字符串两侧的空白字符或其他预定义字符
没有使用deldot函数
# deldot($file_name) 删除文件名末尾的点

  1. 使用Burpsuite 拦截请求数据包 更改后缀名末尾处 添加“.” (这也是利用win系统的命名规则特性 “.”或空格即为空)
  2. 使用Burpsuite 拦截请求数据包 更改后缀名为.php. . (程序执行逻辑时 会执行两次移除字符串两侧的空白字符,但程序只执行一次,执行一次后留下的后缀名即为.php.)

以上两种方法仅适用于windows系统下 Linux系统不支持

  1. 只执行一次trim函数 只清除一次空格
  2. 添加deldot函数 删除文件末尾的点
  3. 白名单防御方法

PASS 8)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
    $file_name = trim($_FILES['upload_file']['name']);
    $file_name = deldot($file_name);//删除文件名末尾的点
    $file_ext = strrchr($file_name, '.');
    $file_ext = strtolower($file_ext); //转换为小写
    $file_ext = trim($file_ext); //首尾去空
    
    if (!in_array($file_ext, $deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
      if (move_uploaded_file($temp_file, $img_path)) {
        $is_upload = true;
      } else {
        $msg = '上传出错!';
      }
    } else {
      $msg = '此文件类型不允许上传!';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

$file_name = trim($_FILES['upload_file']['name']);
$file_ext = trim($file_ext); //首尾去空
执行了两次首尾去空
没有对流文件::$DATA进行过滤

  1. 为文件后缀名添加::$DATA
  2. 改变后缀名格式 让程序执行逻辑时 对.和空格做一次移除操作 保留下来的即为可用执行文件
  1. 只使用一次trim移除函数
  2. 限制过滤流文件::$DATA
  3. 限制上传文件格式(白名单防御方法)

PASS 9)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
    $file_name = trim($_FILES['upload_file']['name']);
    $file_name = deldot($file_name);//删除文件名末尾的点
    $file_ext = strrchr($file_name, '.');
    $file_ext = strtolower($file_ext); //转换为小写
    $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
    $file_ext = trim($file_ext); //首尾去空
    
    if (!in_array($file_ext, $deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH.'/'.$file_name;
      if (move_uploaded_file($temp_file, $img_path)) {
        $is_upload = true;
      } else {
        $msg = '上传出错!';
      }
    } else {
      $msg = '此文件类型不允许上传!';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

$file_name = trim($_FILES['upload_file']['name']);
$file_ext = trim($file_ext); //首尾去空
执行了两次首尾去空

  1. 改变后缀名格式 让程序执行逻辑时 对.和空格做一次移除操作 保留下来的即为可用执行文件
    1. 即xx.php. . ---> xx.php.
  1. 只使用一次trim移除函数
  2. 限制上传文件格式(白名单防御方法)

PASS 10)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
    
    $file_name = trim($_FILES['upload_file']['name']);
    $file_name = str_ireplace($deny_ext,"", $file_name);
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $img_path = UPLOAD_PATH.'/'.$file_name;        
    if (move_uploaded_file($temp_file, $img_path)) {
      $is_upload = true;
    } else {
      $msg = '上传出错!';
    }
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

$file_name = str_ireplace($deny_ext,"", $file_name);
这里将上传的文件名中包含不允许的后缀名替换为空 得到$file_name
如果上传的文件名中一旦包含php asp jsp 等字符都会被替换为空达到不能正常执行shell程序的作用

  1. 双写绕过 BP拦截更改后缀名为.pphphp
    1. 即xx.pphpphp --识别到php 替换为空-> xx.php
  1. 使用完整的黑名单过滤方法 过滤点、空格、大小写、流文件、htaccess分布式配置文件
  2. 使用白名单防御方法 只允许 想要进入的文件格式

⭐PASS1-10 总结过滤后缀名

if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
    $file_name = $_FILES['upload_file']['name'];//获取文件名
    $file_name = deldot($file_name);//删除文件名末尾的点 【没有利用在后缀名添加.】
    $file_ext = strrchr($file_name, '.');//获取后缀名	
    $file_ext = strtolower($file_ext); //转换为小写	【没有利用 大小写绕过】
    $file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA 【没有利用在后缀名添加::4DATA】
     $file_ext = trim($file_ext); //首尾去空【没有利用在后缀名添加空格】

PASS 11)

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
  $ext_arr = array('jpg','png','gif');
  $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
  if(in_array($file_ext,$ext_arr)){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
    
    if(move_uploaded_file($temp_file,$img_path)){
      $is_upload = true;
    } else {
      $msg = '上传出错!';
    }
  } else{
    $msg = "只允许上传.jpg|.png|.gif类型文件!";
  }
}

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
通过get传入的参数 拼接至url中

  1. GET %00截断(通过burpsuite 实现)

1653545914940-f4131588-bfa8-4de0-b281-15976fa02186.png#clientId=ue719c044-3743-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=74&id=u2d6a0e45&margin=%5Bobject%20Object%5D&name=image.png&originHeight=92&originWidth=361&originalType=binary&ratio=1&rotation=0&showTitle=false&size=30022&status=done&style=none&taskId=ua323e152-6357-494c-9bb9-777c5e58c47&title=&width=288.8

1.验证图片文件真实性
2.不使用请求方式接收参数方式 将其拼接至url


PASS 12)

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
  $ext_arr = array('jpg','png','gif');
  $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
  if(in_array($file_ext,$ext_arr)){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
    
    if(move_uploaded_file($temp_file,$img_path)){
      $is_upload = true;
    } else {
      $msg = "上传失败";
    }
  } else {
    $msg = "只允许上传.jpg|.png|.gif类型文件!";
  }
}

$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
通过POST传入的参数 拼接至url中

  1. POST 00截断(通过burpsuite 实现)

1653545903438-3ec45448-c838-467d-8792-6aba2527dad2.png#clientId=ue719c044-3743-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=74&id=ub25107e0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=92&originWidth=361&originalType=binary&ratio=1&rotation=0&showTitle=false&size=30022&status=done&style=none&taskId=ua11f8002-3428-455d-bd2e-f3bc47659a9&title=&width=288.8

1.验证图片文件真实性
2.不使用请求方式接收参数方式 将其拼接至url


PASS 13)

function getReailFileType($filename){
  $file = fopen($filename, "rb");
  $bin = fread($file, 2); //只读2字节
  fclose($file);
  $strInfo = @unpack("C2chars", $bin);    
  $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);    
  $fileType = '';    
  switch($typeCode){      
    case 255216:            
      $fileType = 'jpg';
      break;
    case 13780:            
      $fileType = 'png';
      break;        
    case 7173:            
      $fileType = 'gif';
      break;
    default:            
      $fileType = 'unknown';
  }    
  return $fileType;
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
  $temp_file = $_FILES['upload_file']['tmp_name'];
  $file_type = getReailFileType($temp_file);
  
  if($file_type == 'unknown'){
    $msg = "文件未知,上传失败!";
  }else{
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
    if(move_uploaded_file($temp_file,$img_path)){
      $is_upload = true;
    } else {
      $msg = "上传出错!";
    }
  }
}

通过观察源码

  1. 可知文件被打开时被验证了文件头2个字节(16位)
  2. 使用 switch……case……语句判断文件头16位编号 以验证图片文件的真实性
    1. 常用文件的文件头如下(16进制):

jpg文件头:FFD8FFE0或FFD8FFE1或FFD8FFE8
gif文件头:47494638PNG
png文件头:89504E47

使用文件包含漏洞解析制作的图片马中包含的php执行的webshell语句
图片马制作方法:
copy 1.png/b + 2.php/a 3.png
以二进制bin打开1.png(真实图片) 用ascii编码打开2.php(webshell.php)
把两者相加,获得3.png

防御其他漏洞


PASS 14)

function isImage($filename){
    $types = '.jpeg|.png|.gif';
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)>=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $temp_file = $_FILES['upload_file']['tmp_name'];
    $res = isImage($temp_file);
    if(!$res){
        $msg = "文件未知,上传失败!";
    }else{
        $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = "上传出错!";
        }
    }
}

$info = getimagesize($filename); //获取图片的真实大小和信息
image_type_to_extension($info[2]) //获取文件的文件头

验证文件的真实类型
如果传入的不是图片就会报错

  1. 使用图片马+文件包含漏洞

PASS 15)

function isImage($filename){
  //需要开启php_exif模块
  $image_type = exif_imagetype($filename);
  switch ($image_type) {
    case IMAGETYPE_GIF:
      return "gif";
      break;
    case IMAGETYPE_JPEG:
      return "jpg";
      break;
    case IMAGETYPE_PNG:
      return "png";
      break;    
    default:
      return false;
      break;
  }
}

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
  $temp_file = $_FILES['upload_file']['tmp_name'];
  $res = isImage($temp_file);
  if(!$res){
    $msg = "文件未知,上传失败!";
  }else{
    $img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
    if(move_uploaded_file($temp_file,$img_path)){
      $is_upload = true;
    } else {
      $msg = "上传出错!";
    }
  }
}

exif_imagetype() //获取图像信息和图像类型
验证真实文件类型
如果传入的不是真实图片就会报错

  1. 图片马+文件包含漏洞组合拳

PASS 16)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
    // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
    $filename = $_FILES['upload_file']['name'];
    $filetype = $_FILES['upload_file']['type'];
    $tmpname = $_FILES['upload_file']['tmp_name'];

    $target_path=UPLOAD_PATH.'/'.basename($filename);

    // 获得上传文件的扩展名
    $fileext= substr(strrchr($filename,"."),1);

    //判断文件后缀与类型,合法才进行上传操作
    if(($fileext == "jpg") && ($filetype=="image/jpeg")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromjpeg($target_path);

            if($im == false){
                $msg = "该文件不是jpg格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".jpg";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagejpeg($im,$img_path);
                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "png") && ($filetype=="image/png")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefrompng($target_path);

            if($im == false){
                $msg = "该文件不是png格式的图片!";
                @unlink($target_path);
            }else{
                 //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".png";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagepng($im,$img_path);

                @unlink($target_path);
                $is_upload = true;               
            }
        } else {
            $msg = "上传出错!";
        }

    }else if(($fileext == "gif") && ($filetype=="image/gif")){
        if(move_uploaded_file($tmpname,$target_path)){
            //使用上传的图片生成新的图片
            $im = imagecreatefromgif($target_path);
            if($im == false){
                $msg = "该文件不是gif格式的图片!";
                @unlink($target_path);
            }else{
                //给新图片指定文件名
                srand(time());
                $newfilename = strval(rand()).".gif";
                //显示二次渲染后的图片(使用用户上传图片生成的新图片)
                $img_path = UPLOAD_PATH.'/'.$newfilename;
                imagegif($im,$img_path);

                @unlink($target_path);
                $is_upload = true;
            }
        } else {
            $msg = "上传出错!";
        }
    }else{
        $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
    }
}

imagecreatefromjpeg() 二次渲染图片 将上传的图片渲染生成新的图片 压缩处理渲染处理,导致用户上传的图片马被改变,无法使用
(类似微信朋友圈会对图片进行压缩)
在压缩的时候执行半压缩 只执行了压缩清晰图
微信QQ微博是全压缩

--》low imagecreatefromjpeg
--》high 谷歌的图像压缩算法

  1. 上传图片马
  2. 下载图片马链接

https://wwe.lanzoui.com/iFSwwn53jaf
使用特殊的gif,找到在渲染前后不会改变的文件位置,使用webshell链接


PASS 17)

$is_upload = false;
$msg = null;

if(isset($_POST['submit'])){
  $ext_arr = array('jpg','png','gif');
  $file_name = $_FILES['upload_file']['name'];
  $temp_file = $_FILES['upload_file']['tmp_name'];
  $file_ext = substr($file_name,strrpos($file_name,".")+1);
  $upload_file = UPLOAD_PATH . '/' . $file_name;
  
  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);
    }
  }else{
    $msg = '上传出错!';
  }
}

strrpos()获取字符串最后一个位置
_substr_函数在oracle中使用表示被截取的字符串或字符串表达式

  1. 取出文件得后缀名1.png

$file_ext = substr($file_name,strrpos($file_name,".")+1);

  1. 上传文件 1.png 传到 upload/1.png

$upload_file = UPLOAD_PATH . '/' . $file_name;

  1. if(in_array($file_ext,$ext_arr))

判断后缀名
4.$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
如果是'jpg','png','gif' 文件则上传成功 反之则unlink删除文件

总结上传原理:
条件竞争漏洞
因为PASS17 是先上传再判断,所以加快上传速度和访问速度,进行竞争访问,如果竞争访问速度快过php删除的速度,即可完成漏洞利用

  1. BP爆破 空爆破

1653532874230-a89a228a-628f-405b-834a-6f3b04d090c5.png#clientId=ue719c044-3743-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=u7393fbed&margin=%5Bobject%20Object%5D&name=image.png&originHeight=325&originWidth=302&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70655&status=done&style=none&taskId=u34209464-dddd-4eea-939d-078f0e27892&title=&width=241.6

使用burpsuite爆破模块 高速 上传 webshell.php
使用burpsuite爆破模块 快速 访问 webshell.php

webshell玩法

  1. 执行单一命令 利用漏洞在路径中生成新的webshell
<?php
    file_put_contents('a.php','eval($_REQUEST['kio'])',LOCK_EX);
  ?>
  1. 执行单一命令 利用漏洞在路径中执行命令
<?php system('whoami'); ?>

PASS 18)

//index.php
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
  require_once("./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);
  switch ($status_code) {
    case 1:
      $is_upload = true;
      $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
      break;
    case 2:
      $msg = '文件已经被上传,但没有重命名。';
      break; 
    case -1:
      $msg = '这个文件不能上传到服务器的临时文件存储目录。';
      break; 
    case -2:
      $msg = '上传失败,上传目录不可写。';
      break; 
    case -3:
      $msg = '上传失败,无法上传该类型文件。';
      break; 
    case -4:
      $msg = '上传失败,上传的文件过大。';
      break; 
    case -5:
      $msg = '上传失败,服务器已经存在相同名称文件。';
      break; 
    case -6:
      $msg = '文件无法上传,文件不能复制到目标目录。';
      break;      
    default:
      $msg = '未知错误!';
      break;
  }
}

//myupload.php
class MyUpload{
  ......
    ......
    ...... 
    var $cls_arr_ext_accepted = array(
    ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
    ".html", ".xml", ".tiff", ".jpeg", ".png" );
  
  ......
    ......
    ......  
    /** upload()
    **
    ** Method to upload the file.
    ** This is the only method to call outside the class.
    ** @para String name of directory we upload to
    ** @returns void
    **/
    function upload( $dir ){
    
    $ret = $this->isUploadedFile();
    
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    
    $ret = $this->setDir( $dir );
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    
    $ret = $this->checkExtension();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );
    }
    
    $ret = $this->checkSize();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }
    
    // if flag to check if the file exists is set to 1
    
    if( $this->cls_file_exists == 1 ){
      
      $ret = $this->checkFileExists();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }
    
    // if we are here, we are ready to move the file to destination
    
    $ret = $this->move();
    if( $ret != 1 ){
      return $this->resultUpload( $ret );    
    }
    
    // check if we need to rename the file
    
    if( $this->cls_rename_file == 1 ){
      $ret = $this->renameFile();
      if( $ret != 1 ){
        return $this->resultUpload( $ret );    
      }
    }
    
    // if we are here, everything worked as planned :)
    
    return $this->resultUpload( "SUCCESS" );
    
  }
  ......
    ......
    ...... 
};

对文件先上传再重命名
如果上传速度过快 php可能反应不过来

利用条件竞争漏洞批量上传文件


PASS 19)

$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
  if (file_exists(UPLOAD_PATH)) {
    $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
    
    $file_name = $_POST['save_name'];
    $file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
    
    if(!in_array($file_ext,$deny_ext)) {
      $temp_file = $_FILES['upload_file']['tmp_name'];
      $img_path = UPLOAD_PATH . '/' .$file_name;
      if (move_uploaded_file($temp_file, $img_path)) { 
        $is_upload = true;
      }else{
        $msg = '上传出错!';
      }
    }else{
      $msg = '禁止保存为该类型文件!';
    }
    
  } else {
    $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
  }
}

CVE-2015-2348 00截断

1653546392577-528f1123-a90a-48bb-a3fe-5abe7b8e4262.png#clientId=ue719c044-3743-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=306&id=uac4e41d1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=382&originWidth=646&originalType=binary&ratio=1&rotation=0&showTitle=false&size=104332&status=done&style=none&taskId=uc9361945-9cba-4914-81af-3956fd394c9&title=&width=516.8

使用00截断方法


PASS 20)

$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
  //检查MIME
  $allow_type = array('image/jpeg','image/png','image/gif');
  if(!in_array($_FILES['upload_file']['type'],$allow_type)){
    $msg = "禁止上传该类型文件!";
  }else{
    //检查文件名
    $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 = "文件上传失败!";
      }
    }
  }
}else{
  $msg = "请选择要上传的文件!";
}

总结防御方法(不考虑文件包含的存在):

  1. 对象存储服务器

(将用户上传的文件,放置专用的服务器,和网站本身服务器没有任何关系 大厂常用方法)

  1. 检测content-type (如果是上传图片 检测是否为image/png;image/jpg;image/png)
  2. 检测后缀名 (合规的后缀名检测是无法绕过的)
  3. 二次渲染 (对上传的图片进行压缩或处理 合规的渲染可以防御)
  4. 内容检测 (获取文件信息,判断目标不是允许的文件时会直接报错)

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK