Abstract: upload-labs 中的 Pass-03 - Pass12 文件后缀解析漏洞。

Table of Contents

  1. 文件上传漏洞总结
    1. 文件上传分类
    2. 如何判断上传漏洞类型
    3. 根据不同类型选择不同方式
    4. Pass-03
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检查
      3. Pass-03 代码分析
    5. Pass-04
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检查
        1. 使用 .htaccess 绕过
        2. 利用系统特性覆盖空的php文件
      3. Pass-04 代码分析
    6. Pass-05
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检查
      3. Pass-05 代码分析
    7. Pass-06
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检测
      3. Pass-06 代码分析
    8. Pass-07
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检测
      3. Pass-07 代码分析
    9. Pass-08
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检测
      3. Pass-08 代码分析
    10. Pass-09
      1. 先判断上传漏洞的类型
      2. 根据已知信息构造后缀绕过检测
        1. 使用点空格点绕过
        2. 利用系统特性覆盖空的php文件
        3. Pass-09 代码分析
    11. Pass-10
      1. 先判断上传漏洞类型
      2. Pass-10 代码分析
    12. Pass-11
      1. 先判断上传漏洞类型
      2. 根据已知信息构造后缀绕过检测
      3. Pass-11 代码分析
    13. Pass-12
      1. 先判断上传漏洞类型
      2. 根据已知信息构造后缀绕过检测
      3. Pass-12 代码分析

文件上传漏洞总结

文件上传分类

如何判断上传漏洞类型

根据不同类型选择不同方式

Pass-03

先判断上传漏洞的类型

打开第三关首页,寻找上传点,我们可以很容易找到上传点。

先选择一个 404.txt 文件上传,

发现可以上传成功,右击复制图片地址,可以查看文件内容。

返回首页,选择 shell.php 文件上传,发现上传失败。

根据以上操作,可以发现该判断机制为后缀的黑名单判断。除此之外,通过 Wappalyzer 插件可以搜集到很多信息。

根据已知信息构造后缀绕过检查

根据提示可知 .php 文件是不允许的,那么可以通过修改后缀名来绕过后缀名检查。可以新建一个 2.phtml

然后直接上传 2.phtml 文件绕过后缀名检查。

也可以打开 burpsuite 软件,在上传时,修改 2.php 的后缀为 2.phtml 。来绕过后缀名检查。

上传成功后,右击选择复制图片地址,在新标签页打开,可以发现

Pass-03 代码分析

核心代码:

1
2
3
4
5
6
7
8
9
10
11
<?php
...
$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); //收尾去空
...
?>

可以发现,黑名单中含的名单过少,所以可以通过 .phtml 绕过。由于 deldot()strtolower()trim() 函数的存在,所以无法用后缀那个加 . 或者 空格 等方法绕过。 当我们输入 2.php.空格. 时,经过 deldot() 函数后,变为 2.php.空格 ,再经过 strrchr() 函数后变为 .空格,经过 strtolower()str_ireplace() 后,没有变化,最后经过 trim() 函数后,变为 .2.php.空格. 虽然可以绕过上传成功,但是文件名为随机数加 .,无法解析。

Pass-04

先判断上传漏洞的类型

选择 1.php 点击上传,发现无法上传。

选择 webshell.txt 点击上传,发现可以上传成功,

右击选择复制图片地址,在新标签页中打开,可以看到上传的文件名和文件内容。

发现服务器并没有对文件名进行重命名。

此上传类型为不重命名的黑名单上传。

根据已知信息构造后缀绕过检查

使用 .htaccess 绕过

新建 .htaccess文件,写入

1
SetHandler application/x-httpd-php

新建 404.jpg 用记事本打开,写入一句话木马,

1
<?php phpinfo();?>

另存为 404.jpg 文件。

先上传 .htaccess 文件,使得上传的文件按 php 解析。

再上传 404.jpg 文件

查看图片地址,

虽然是 .jpg 格式,但按照的是 php 文件的解析,所以可以成功。

利用系统特性覆盖空的php文件

windows 系统下,< 等于 *,即代表任意字符。

参考文章

当php邂逅windows通用上传缺陷

思路:利用特殊方法生成一个 php 文件,然后再利用改特性将文件覆盖。

打开 burpsuit 软件,选择 2.php 点击上传,抓取数据包,然后发送到 Repeater 模块,这这里进行尝试,知道发现可以上传的文件后缀名为止。

依次尝试文件上传思维导图中黑名单中的方法,发现当文件名为 2.php:.jpg 可以上传成功。

返回到 Proxy 模块,修改文件后缀为 3.php:.jpg ,点击 Forward

右击选择复制图片地址,在新标签页中打开,由于 windows 系统会把后缀名解析为 3.php

修改文件后缀名,继续查看。

发现没有内容,打开服务器,查看 3.php 发现没有内容。

重新上传,用 burpsuite 修改后缀名为 3.<<<,上传成功后,重新查看文件。

发现本次可以显示内容,

在服务器查看 3.php 文件。

Pass-04 代码分析

核心代码

1
2
3
4
5
6
7
8
9
10
11
<?php
...
$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); //收尾去空
...
?>

黑名单中名单虽多,但是并没有 .htaccess 所以可以先上传 .htaccess,之后把 404.php 另存为 404.jpg,上传 404.jpg,会以 php文件解析。 当输入为 2.php:.jpg时,经过处理后输出为 .jpg,可以绕过检测,上传至服务器上,由于 windows 特性,上传的文件名为 2.php。之后再修改文件名为 2.<<<,系统检测到后匹配

2.php 文件,之后把内容覆盖掉。

Pass-05

先判断上传漏洞的类型

选择 2.php 文件,无法上传。

选择 webshell.txt ,上传成功,

查看文件,

该类型为黑名单的文件重命名上传。

根据已知信息构造后缀绕过检查

打开 burpsuite 软件,选择一个文件点击上传,用 burpsuite 抓取数据包,右击发送到 Repeater 模块。

修改 2.php2.pHP 发现可以上传成功,

返回 Proxy 模块,修改后缀 .php.pHP

查看文件

Pass-05 代码分析

1
2
3
4
5
6
7
8
9
10
<?php
...
$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); //首尾去空
...
?>

Pass-04 代码相比,可以发现,黑名单中增加了 .htaccess 后缀名。但是,缺少了 strtolower() 函数,所以可以通过大写绕过。

Pass-06

先判断上传漏洞的类型

上传 1.php ,提示此文件类型不能上传。

上传 webshell.txt ,发现可以上传,

查看文件,

可以发现,文件名已经改变,故该类型为黑名单重命名上传类型。

根据已知信息构造后缀绕过检测

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。

多次尝试后,发现在后缀名后添加空格就可以绕过检测。

返回 Proxy 模块,修改后缀名,可以上传成功,查看文件

Pass-06 代码分析

1
2
3
4
5
6
7
8
9
10
<?php
...
$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() ,所以可以通过后缀名加空格来绕过。

Pass-07

先判断上传漏洞的类型

上传 1.php 提示:此文件类型不允许上传!

上传 webshell.txt ,可以上传,查看文件,

可以判断该类型为黑名单不重命名的上传类型。

根据已知信息构造后缀绕过检测

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。多次尝试后,发现把 2.php 改成 2.php. 可以绕过检测。

返回 Proxy 模块,进行上传,

发现可以上传成功,点击查看文件,

Pass-07 代码分析

1
2
3
4
5
6
7
8
9
10
<?php
...
$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); //首尾去空
...
?>

程序通过对末尾 . 的处理,来取出后缀,但输入为 2.php. 的话,最后输出为 . 可以绕过检测。上传至服务器的后缀名为 .php. 由于服务器系统特性,也可以解析为 .php

Pass-08

先判断上传漏洞的类型

上传 1.php ,无法上传,提示:此文件类型不允许上传。

上传 webshell.txt ,上传成功,查看文件,

可以看出,该类型为黑名单重命名的上传类型。

根据已知信息构造后缀绕过检测

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。多次尝试,发现把 2.php 修改为 2.php::$DATA 可以绕过检测。

返回 Proxy 模块进行上传。

上传成功后,点击查看文件,

把后面的 ::$data 重新上传

Pass-08 代码分析

1
2
3
4
5
6
7
8
9
10
<?php
...
$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); //首尾去空
...
?>

与前几篇的对比可以发现,通过 ::$DATA 绕过。

Pass-09

先判断上传漏洞的类型

上传 1.php ,无法上传,提示:此文件类型不允许上传。

上传 webshell.txt ,可以上传成功,查看文件,

可以看出,该类型为黑名单不重命名的上传类型。

根据已知信息构造后缀绕过检测

使用点空格点绕过

上传 2.php 文件,同时使用 burpsuite 改包,把 2.php 修改为 2.php. .,点击 Forward ,上传成功。

查看文件

利用系统特性覆盖空的php文件

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。多次尝试,将 2.php 改成 2.php:jpg,可以长传成功。

返回 Proxy 模块,上传文件成功,查看文件,

:jpg 删掉,

刷新网页,

打开服务器,查看 2.php,发现确实没有数据,

重新上传,把 2.php 修改为 2.<<<

查看文件,

<<< 改为 .php

查看服务器端的 2.php 文件

Pass-09 代码分析
1
2
3
4
5
6
7
8
9
10
11
<?php
...
$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); //首尾去空
...
?>

文件名的处理过程如图所示,

当输入文件名为 1.pHP. 时,1.pHP.1.pHP.pHP.php.php → 输出。

当输入文件名为 1.php.空格时,1.php.空格1.php.1.php.php → 输出。

当输入文件名为 1.php.空格. 时,1.php.空格.1.php.空格.1.php.空格.空格. →输出。

Pass-10

先判断上传漏洞类型

上传 1.php 文件,发现可以上传成功,

点击查看文件,

发现文件名字变为 1. ,推测有可能检测到后缀名中有 php 后就把后缀的 php 给去掉了,尝试双拼绕过。

点击上传,可以上传成功,查看文件,

Pass-10 代码分析

1
2
3
4
5
6
7
8
9
<?php
...
$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'];
...
?>

把在黑名单中出现的后缀名替换为空,即可以通过双拼绕过。

Pass-11

先判断上传漏洞类型

上传 1.php 文件,没有成功,提示:只允许上传 .jpg|.png|.gif 类型文件。首先确定为白名单策略。

选择图片上传,

查看图片,

可以判断出该类型是白名单重命名上传类型。

根据已知信息构造后缀绕过检测

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。多次尝试,把 2.php 改成 2.php%00.jpg

打开服务端,里面没有 2.php 文件。

burpsuite 改包,

上传成功后,查看

查看服务端,

文件名只保留 2.php,刷新页面

Pass-11 代码分析

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

设置白名单,通过寻找 . 的位置,截取文件名,取出文件后缀,之后与白名单中的后缀做对比。关键问题出在可以设置保存路径上。把 save_name 设置为 /uploads/404.php%00,上传文件名为 404.jpg 后,传至服务器的文件名为 /uploads/404.php%00404.jpg由于windows特性实际上传至服务器的名字为 /uploads/404.php

Pass-12

先判断上传漏洞类型

上传 1.php 文件,观察页面的报错提示。

上传一个头像,

此类型为白名单重命名的上传类型。

根据已知信息构造后缀绕过检测

burpsuite 抓取数据包,在 Repeater 判断绕过的方法。在 burpsuite 中我们可以发现,save_path 的参数通过 POST 方式发送,所以应该把它转换成 hex

404.php 保存为 404.jpg,文件内容为 <?php phpinfo();?>

上传 404.jpg 文件,

同时使用 burpsuite 抓包,把 save_path 修改为 ../upload/404.php%00,由于 POST 方式并不进行 URL 解码,故转换成十六进制。

点击 Hex,找到对应的位置做修改,

返回 Raw 查看,

未点击 Forward 之前,服务端没有 404.php

点击 Forward,上传成功,查看服务端,

在浏览器中查看图片,

只留下 404.php

Pass-12 代码分析

Pass-11 主要的不同就是 save_path 传输方式。Pass-11GET 方式传递,Pass-12POST 方式传递。由于 POST 方式不会经过 URL 解码,所以需要在数据包中把 %00 修改为 Hex 方式。