尽最大可能分析上传源码及漏洞利用方式

0x00 简单源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
if ((($_FILES["file"]["type"] == "image/gif") //检测Content-type值
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000)) //检测文件大小
{
$ext = end(explode('.', $_FILES["file"]["name"])); //获取最后“.”的后缀
if($ext === 'php') //检测是否为php后缀
{
exit('error');
}
if ($_FILES["file"]["error"] > 0) //返回上传错误码
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else //返回上传成功信息
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";
if (file_exists("upload/" . $_FILES["file"]["name"])) //检测文件是否存在
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else //将上传的临时文件转移到指定存放文件夹
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
}
}
else
{
echo "Invalid file"; //返回无效文件的错误信息
}
?>

0x01 详细分析过程

1) 直接使用用户上传文件名,没有过滤特殊字符,存在漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if ((($_FILES["file"]["type"] == "image/gif") //文件类型检测
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000))
{
$ext = end(explode('.', $_FILES["file"]["name"])); //文件后缀检测
if($ext === 'php'){
exit('error');
}
...... //省略了中间的一些代码
```
以下代码的作用是,将上传到临时文件夹的文件移到upload的目录下
```php
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}

1.1分析:

从以上代码可以看出,可以上传图片文件,对文件拓展名检测是通过end(explode(‘.’,$_FILES[“file”][“name”]))函数实现(在这里先不说文件类型验证的问题)explode(‘.’,$_FILES[“file”][“name”])用来把上传的文件名作为字符串,以.(点)来分割字符串来生成数组,而end()函数则是将数组内部指针指向最后一个元素,并返回该元素的值,例如,当上传x.php文件时,最终就会获取到php。
而对于$_FILES里面获取变量,是直接来自http request请求,它跟普通获取其它get,post变量一样。 比如在post上传时,我们可以通过抓包来截获(常用神器burpsuite)这一过程,由于在上传的检测中没有对文件名进行检测和过滤及处理,因此,我们可以将name构造一个特殊文件名,然后,再将post数据提交,就可以达到上传绕过对文件扩展名的检测。

1.2利用:

比如我们可以在本地上将一个php文件命名为x.php.jpg在上传文件时,抓包post,将filename修改为x.php\0.jpg再提交post数据,那么在保存我们修改后的文件时,\0后面的所有字符将(如.jpg)被自动截断,最终生成我们想要目标文件格式(如本例的x.php),以至于可以上传任意恶意的php脚本。从网上得到说明Php4的版本可以利用这个漏洞,php5版本以上会自动过滤掉’”/0”,另外很多asp,jsp也存在此类截断上传的漏洞。

2)文件类型验证不严格,存在漏洞*

1
2
3
4
5
6
7
8
9
10
<?php
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000))
{
$ext = end(explode('.', $_FILES["file"]["name"]));
if($ext === 'php'){
exit('error');
}

2.1分析:

从代码可以看出是通过读取文件的type直接来做文件类型的判断,同样我们可以通过抓取post数据,将Content-type值修改为允许上传的MIME类型,在这里有image/gif等三种,从而绕过对文件type的检查,虽然之后代码对文件扩展名进行了检测(那两个个函数对文件扩展名检测的大慨过程就是判断文件名最后”.”的最后字符,example.php.bak最终得到是bak),并判断是否是php后缀名。

2.2利用:

1.如果没有对apache默认支持解析文件方式进行修改时(即apache解析漏洞,很多网站管理员由于安全意识薄弱或其他情况往往没有修改),那么在绕过对type的检测之后,最简单的利用方式就是我们可以直接上传一个恶意的名为x.php.ext,这里的ext可以是多种,只要不是php和apache可以解析其他扩展名(如txt后缀)就可以,那么按照apache默认从右往左直到遇到可支持解析的文件的解析方式,那么就会把x.php.ext当成x.php来解析。

2.在绕过对文件type检查之后,我们还可以将本地的x.php文件修改为x.php1,x.php3,x.php4或者x.php5再上传,默认的apache服务器将会解析为php。

3)使用php的全等于来判断是否为php文件类型,存在漏洞

1
2
3
4
$ext = end(explode('.', $_FILES["file"]["name"]));
if($ext === 'php'){
exit('error');
}

3.1分析:

我们知道在php语法中全等于”===”的作用是先判断等号左右两边的数据类型是否一样,再判断等号两边的值是否相等,如果都相等返回true,否者返回false。

3.2利用:

在明白全等于”===”的判断原理之后,我们就可以进行利用了,还是以本地的x.php举例,我们可以将x.php修改为Php, x.PHp, PHP…在绕过对type的检测之后再上传,以上传的x.PHP来分析,首先上传截获到的是PHP后缀,此时$ext=PHP,再将进行下一步的验证,$ext和php都是相同的数据类型,但是在验证它们值时却不相等,从而绕过上传。

而默认的web应用程序都是默认将Phpx.PHp,PHP…解析为php执行。

4)对上传文件内容没有做检测,存在漏洞

4.1分析:

从upload.php源代码分析来看,在整个过程没有对上传文件内容进行检测,过滤和处理,可以通过上传图片木马或其他方式来到达恶意上传的目的。

4.2利用:

1.如果的网站是用Nginx的Web应用程序且版本<8.03,那我们就可以利用Nginx默认开启Fast-CGI而造成的畸形解析漏洞,以下是具体利用方式说明。在默认Fast-CGI开启状况下,我们可以上传一个名为x.jpg,其内容为

1
<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>

的文件,之后我们再访问x.jpg/.php这个网站路径,在这个目录下就会生成一句话木马shell.php。

2.如果的网站是用Nginx的Web应用程序且版本在(0.5.*,0.6.*,0.7,0.8<=0.7.65<=0.8.37)其中,那我们还可以利用Nginx空字节代码执行漏洞来达到上传任意恶意代码并执行。具体利用方法是:将x.jpg图片中嵌入PHP代码然后通过访问x.jpg%00.php来执行其中的恶意代码。

3.如果网站的Apache Web应用程序中.htaccess可被执行且可被上传,那可以尝试在.htaccess中写入:
<FilesMatch "example.jpg"> SetHandler application/x-httpd-php </FilesMatch>
这段代码的意思就是将把目录下的所有后缀为jpg的文件当做可执行的php脚本进行解析并执行。因此,我们可以上传一个x.jpg的木马, 这样x.jpg就可解析为php文件。