upload labs-1
判断检测点
1、先把muma.php改后缀为muma.jpg上传,如果能够上传,则说明不会进行文件内容检测,只需判断是黑名单或者白名单即可。此时再改为任意后缀比如muma.xyz,如果还能上传,则代表是黑名单,如果不能则说明是白名单。如果muma.jpg不能上传,则说明他进行了文件内容的检测,有时会是几种检测的混合搭配,比如既检测了文件内容,又是黑名单。
2、发现burp没任何反应,可能没抓到包?也可能就是个前端验证,压根没有请求通过。f12打开看下,确实有js验证。
验证代码
<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name) == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
</script>
绕过
将muma.php改为jpg|png|gif后缀,然后上传,成功后在brup进行改包,将其后缀改为php即可。
打开图片地址,找到路径,连接即可。
upload labs-2
判断检测点
1.直接上传muma.php看看。提示文件类型不正确。第一反应是content-type检测
2.抓包看看 content-type是application,问题很可能就是这个了。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name'];
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
就是说如果上传问题的type不是 image/jpeg|image/png|image/gif的话就会报错。
绕过
1、上传muma.php然后burp抓包后,改掉其content-type
2、直接上传图片一句话,直接绕过。
upload labs-3
判断检测点
1、直接上传muma..php,提示不允许上传.asp,.aspx,.php,.jsp后缀文件,判断其很可能是个黑名单
代码验证
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
if(!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
服务器端黑名单过滤,过滤了asp,.aspx,.php,.jsp
绕过
1、常见的其他形式.phtml .phps .php5 .pht 这里需要注意:如果上传的是.php5这种类型文件的话,如果想要被当成php执行的话,需要有个前提条件,即Apache的httpd.conf有如下配置代码
AddType application/x-httpd-php .php .phtml .phps .php5 .pht
AddType 指令
作用:在给定的文件扩展名与特定的内容类型之间建立映射
语法:AddType MIME-type extension [extension] ...
AddType指令在给定的文件扩展名与特定的内容类型之间建立映射关系。MIME-type指明了包含extension扩展名的文件的媒体类型。
AddType 是与类型表相关的,描述的是扩展名与文件类型之间的关系。
2、这里也可以上传.htaccess htaccess文件生效前提条件为 1.mod_rewrite模块开启。2. AllowOverride All. 构造.htaccess文件,内容如下:AddType application/x-httpd-php .jpg. 这里代码的意思可以让 .jpg后缀名文件格式的文件名以php格式解析,因此达到了可执行的效果。所以我们可以把要上传的php文件的后缀名改为.jpg格式从而绕过
成功上传,但是可以看到文件被重命名了,但是后缀还保留着。用菜刀无法连接,查看httpd.conf配置文件,其并没有映射php3且没有开启。
修改httpd.conf配置后,发现能够连接了。
这里尝试下htaccess。先上传.htaccess文件 再上传图片马(php马改为jpg后缀也可) 发现不行啊不能解析啊。
看看是不是开启了htaccess的配置呢。原来是AllowOverride没有改为all状态。重启再试试呢
还是不行!咋回事啊?再检查了一遍上传的文件,发现 上传的 htaccess文件被重命名了额。这下没招了,看来上传htaccess在这里行不通啊。不过如上面所写,用php3,php5等后缀绕过还是可以的。
upload labs-4
判断检测点
1、直接上传muma.php,提示此文件不许上传,且没有提示具体哪些文件不让上传。在黑盒情况下,仅凭此提示,可能有3个方向,一个是黑名单检测,另一个是文件内容检测,一个是白名单。只能挨个尝试,优先考虑黑名单,然后再试试文件内容。如果真的是文件内容的话,2个方案,一个是上传php然后再抓包修改各项内容,另一个是直接上传图片马,但是图片马上传后要想解析就只能找找文件包含漏洞或者解析漏洞了。黑名单内容就多了。本例中也先尝试黑名单。首先尝试各种不同的后缀也即是语言特性。常见后缀的似乎都不行了。再尝试操作系统特性,在尝试各种中间件,包括一些解析漏洞和htaccess。这里先尝试htaccess。很幸运,这里htaccess并没有被过滤掉。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
可以看到,他基本过滤了常见后缀,也就是说语言特性基本行不通了,然后又处理了windows下的操作特性,再然后就试试中间件特性了。他这里并没有过滤htaccess且并没有进行重命名。
绕过
先上传.htaccess文件,再上传muma.jpg文件。注意AllowOverride需要改为All,然后访问这个jpg路径即可。但在上例中,htaccess被重命名了,本例中是可以连接一句话的。
upload labs-5
判断检测点
直接上传muma.jpg,提示成功。在随便换个后缀muma.xxsw,依然成功上传,说明是个黑名单过滤。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
可以看到,各种过滤,htaccess也被过滤了。但是ini没有被过滤掉。
绕过
挨个尝试,发现ini没有被过了。
这里没有复现成功,看到有人说phpstudy 8.1版可以,2016版不行,我没有复现成功。
参考:
https://www.cert.ac.cn/sec/473.html
https://www.cnblogs.com/anbuxuan/p/11812573.html
https://www.colabug.com/2020/0118/6876098/
upload labs-6
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里他并没有像之前那样将所有的文件名后缀进行强制的小写转换。所以可以利用黑名单外的大小写进行绕过。
绕过
上传muma.php,然后抓包修改后缀为PHp即可。他也被重命名了。不过没事,仍然是php后缀。
upload labs-7
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里他没有对末尾的字符进行处理去空格处理,仅仅只是删除了末尾的点。可以在末尾添加空格
绕过
上传muma.php然后抓包改包,在php后面加上空格。windows里会将空格等自动去除。
Win下xx.jpg[空格] 或xx.jpg.这两类文件都是不允许存在的,若这样命名,windows会默认除去空格或点
此处会删除末尾的点,但是没有去掉末尾的空格,因此上传一个.php[空格]文件即可
upload labs-8
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里没有对尾部的点进行处理。
绕过
upload labs-9
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
绕过
这里并没有对DATA进行处理。
Windows系统下, 如果上传的文件名中test.php::DATA会在服务器上⽣成⼀个test.php的⽂件, 其中内容和所上传⽂件内容相同, 并被解析。 即上传⽂件xxx.php::$DATA = xxx.php
upload labs-10
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。这里好像基本都不行了。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里看起来防守得很严密。
绕过
这里黑名单类型全,大小写经过转换,去除了文件名末尾的点,去除了文件名尾空格,还去除了::$DATA。。但是,这里还是可以绕过的。这里的代码逻辑是先删除文件名末尾的点,再进行首尾去空。都只进行一次。故可以构造点空格点进行绕过,也就是后缀名改为xx.php. .,也是利用了Windows的特性。之前的关卡都可用此法绕过,不过这个只是windows下的。
upload labs-11
判断检测点
和 upload labs-4一样,先从黑名单开始尝试。挨个尝试,不要漏掉。这里同样尝试了点空格点,上传是成功了,但是当访问时,发现后缀没了,变成muma.
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
可以看到,这里没有了之前的去掉点空格之类的,有可能是针对非windows系统的,因为那些特性只能在win下面使用。这里他直接把后缀黑名单里的后缀给替换成空格了,真牛逼。而且不区分大小写,所以大小写绕过也没啥用了。
绕过
但是这里是把他替换成了空字符,可以使用双写来 phphpp来绕过,替换php后就是php了
upload labs-12
判断检测点
正如之前一样,先把muma.php改后缀为muma.jpg上传,如果能够上传,则说明不会进行文件内容检测,只需判断是黑名单或者白名单即可。此时再改为任意后缀比如muma.xyz,如果还能上传,则代表是黑名单,如果不能则说明是白名单。如果muma.jpg不能上传,则说明他进行了文件内容的检测。
这里muma.jpg能够上传并且被重命名了,则我们需要进一步判断是白名单还是黑名单。
更改任意后缀后再次上传,提示只允许上传jpg|png|gif类型,说明此处是个白名单过滤。
验证代码
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
可以看到在进行move_uploaded_file前。利用$_GET[‘save_path’]和随机时间函数进行拼接,拼接成文件存储路径。这里利用_GET传入,GET是我们可以控制输入的,导致服务器最终存储的文件名可控。故可以利用这个点进行绕过。这里利用的是00截断。即move_uploaded_file函数的底层实现类似于C语言,遇到0x00会截断
截断条件:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态
当抓包后有路径时,可以在路径save_path=../upload/那里拼接,也可以在filename那里拼接。因为最终的文件总是要被存储的。只不过有时会没有存储路径显示。
当抓包后没有路径时,可以在filename那里拼接。
一般来说,有路径时优先尝试路径,最保险的也是修改路径。路径有时会在头部,有时会在中间。filename最主要的弊端就是1、他可能会被重命名。比如你穿上去了1.php%00.jpg,但是直接被重命名为了202004144.jpg 2、压根就传不上去。可以对pass-12 13关进行尝试,在filename下分别尝试%00和%00转换为空格。
存储路径 save_path可能会用post也可能会用get去接收,当他是post时不会像get那样对%00进行解码。只能通过十六进制中去修改20为00,因此这种方法适用性更广一些。
绕过
这里先尝试下filename处的截断,修改filename=”muma.php%00.jpg” 很不幸,这里被重命名了。所以filename处修改不成功。
我们打开这个路径,发现404了,不过没有关系。实际上在后台已经保存为了1.php了。
实际上已经保存为1.php了。
菜刀连接成功。
upload labs-13
判断检测点
这里与pass-12一样的判断方法。白名单过滤。尝试00截断。
验证代码
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_POST['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传失败";
}
} else {
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
依然是上传路径可控
这里与pass-12的区别在于其save_path采用post方式。而对于00截断来说,post方式并不会对%00进行解码,需要在十六进制里对其进行操作。
绕过
在path那里添加1.php , 然后修改空格的hex20为00,可以看到上传成功,连接。
总之,关于00截断。成功率最高的是直接修改路径。当get时,%00和空格均可用,当post时,空格才能用,因为post下%00不会被url解码。直接改filename的话,有时可以有时不可以。有时只能%00可以,空格不可以。可能与具体的后台提前后缀名代码方式有关。还要注意filename被重命名的情况,比如pass-12。总之挨个试一下。
upload labs-14
判断检测点
先上传muma.jpg,提示文件未知,说明有文件内容检测。
验证代码
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
这里会检测文件头。并且还会把上传的文件重命名为检测到的文件类型。
绕过
常见的文件头如下
gif(GIF89a) : 47 49 46 38 39 61
jpg、jpeg : FF D8
png : 89 50 4E 47 0D 0A
在绕过时,直接利用burp的hex功能,在php内容前面添加文件头即可。
以jpg为例,利用burp在php代码之前随便输入2个字符,然后到hex处修改即可。然后利用文件包含漏洞进行解析。
文件包含漏洞 http://www.test.com/upload-labs/include.php?file=./upload/7120200418095207.jpg
以png为例。
然后hex里修改为89 50 4E 47 0D 0A
上传成功,然后连接即可。
以gif为例。
利用include文件包含连接。
这里 gif文件其实有个更简单的方法。不用麻烦的去改hex值,只需在一句话前添加相应的头GIF89a即可。
以gif为例。直接添加GIF89a即可。上传成功。
upload labs-15
判断检测点
先上传muma.jpg,提示文件未知,说明有文件内容检测。
验证代码
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
这里用到了getimagesize()函数来检查是否为图片文件。getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
绕过
这里的绕过仍然采用修改文件头的方式,然后利用文件包含漏拿到webshell。
对于GIF,直接添加GIF89a头,也可直接使用一句话马。
对于png和jpg,先上传一个正常的图片,然后抓包在其文件末尾添加一句话即可。
当然了,其实都可以直接使用一句话图片马
以png为例,上传正常png图片,抓包,在文件末尾添加一句话然后上传,成功上传,文件包含漏洞连接。
upload labs-17
判断检测点
先上传muma.jpg,提示该文件不是jpg格式的图片!,说明有文件内容检测。
验证代码
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=UPLOAD_PATH.'/'.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
这个看起来复杂的多。这里先是判断Content-Type,然后再用imagecreatefrom[gif|png|jpg]函数判断是否是图片格式,如果是图片的话再用image[gif|png|jpg]函数对其进行二次渲染。
我们可以上传一个正常的图片文件,观察其上传前和上传后图片的二进制流是否发生变化。
先把一个图片马用16进制打开,可以看到在文件末尾有一句话木马。
然后将此图片马上传。下载服务器保存的被二次渲染的图片,同样的十六进制打开。
对比之前的会发现,除了图片变小之外,在末尾的一句话也被去除了。
绕过
由上面分析可知,如果想要绕过二次渲染的话,就要搞清楚二次渲染后,源文件哪些区域不会被修改或压缩。这里因为gif、jpg、png三种不同图片文件的文件格式不同,所以图片马的构造方法也不同。
gif文件绕过
gif二次渲染绕过说是最简单的。将源文件和二次渲染过的文件进行比较,找出源文件中没有被修改的那段区域,在那段区域写入php代码即可。
使用gif图片马,上传服务器,下载渲染后的gif图。使用 ultraedit 软件对2个文件进行对比,迅速找到匹配的地方,然后插入php一句话代码。下图红色区域为不同的地方。
emmm,理论看起来很简单,但是没成功。直接在别人的gif上改得了。–改也没改成功,郁闷啊。换成一句话就会报错呢。
jpg文件绕过复现也没成功呢
参考:
https://www.jianshu.com/p/aabc1e7408d5
https://xz.aliyun.com/t/2657
upload labs-18
判断检测点
先上传muma.jpg,直接成功了。再进一步测试黑名单和白名单。随便修改个后缀,提示只允许上传jpg|png|gif类的文件,白名单过滤。黑盒的情况下,一般尝试00截断,解析漏洞,大小写,方法并不多。
验证代码
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
这里是先move_uploaded_file函数将上传文件临时保存,再进行判断,如果不在白名单里则unlink删除,在的话就rename重命名,所以这里存在条件竞争,通过条件竞争的方式在unlink之前,访问上传文件。
绕过
利用条件竞争删除文件时间差绕过。
没有复现成功。看别人测试5000次里有3次成功。
upload labs-19
判断检测点
先上传muma.jpg,直接成功了。再进一步测试黑名单和白名单。随便修改个后缀,提示:上传失败,无法上传该类型文件。白名单过滤。黑盒的情况下,一般尝试00截断,解析漏洞,大小写,方法并不多。
验证代码
这里不贴了。好几个文件呢。
绕过
对文件后缀名做了白名单判断,然后检查文件大小、文件是否存在等等。
将文件上传后,对文件重新命名,同样存在条件竞争的漏洞
初始文件命名规则this->cls_upload_dir .this->cls_filename,重命名规则this->cls_upload_dir .this->cls_file_rename_to,当大量文件需要被重命名时就会出现条件竞争
move在rename之前,move操作进行了一次文件保存, 然后rename进行了一次更改文件名。
利用burp不间断地发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功。
upload labs-20
判断检测点
先上传muma.jpg,直接成功了。再进一步测试黑名单和白名单。随便修改个后缀,也成功了,那这是一个黑名单啊。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
/*
$file_name = trim($_POST['save_name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
*/
$file_name = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION);
if(!in_array($file_ext,$deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
}else{
$msg = '上传出错!';
}
}else{
$msg = '禁止保存为该类型文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里保存的文件名是可以自己控制的。本关考察CVE-2015-2348 move_uploaded_file() 00截断,上传webshell,同时自定义保存名称,直接保存为php是不行的
绕过
把保存文件名改为upload-19.php看看,报错叻。
保存名称是自定义的。这里可以构造一个00截断的形式。先尝试2.php%00.jpg,发现貌似不行。虽然成功了,但是保存的仍是jpg啊,没有达到00截断的目的。
然后想起post方式是不能解码%00的,尝试下urlcode呢?将%00在burp右键进行url-decode,此时%00变成了空格。再次上传,发现成功了。
然后连接一句话、
然后看看别人的,还有另一种方法。
move_uploaded_file会忽略掉文件末尾的/.
所以可以构造save_path=1.php/.,这样file_ext值就为空,就能绕过黑名单,而move_uploaded_file函数忽略文件末尾的/.可以实现保存文件为.php
抓包修改文件名为3.php/. 然后上传成功。实际上在后台可以看到保存的是3.php文件,末尾的/.直接被忽略了。
直接连接。
upload labs-21
判断检测点
先上传muma.jpg,直接成功了。再进一步测试黑名单和白名单。随便修改个后缀,也成功了,那这是一个黑名单啊。但是常规方法不行额。
验证代码
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//mime check
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//check filename
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
上面第6行先进行了一个Content-Type判断,10-13行,如果save_name是字符串的话就通过explode函数,将post进去的save_name转成小写后按’.’打散成数组。而15-20行里的file) . ‘.’ . file) – 1];处理,而file[count(file)-1]和end(file)是相等的,也就是说,如果save_name是字符串形式传入的话,想要绕过白名单话,file_name必为gif、png、jpg,无法达到上传php的目的。 所以save_name不能以字符串形式传入。而应该以数组形式传入,从而绕过explode过程,构建特殊数组,使得end(file)能绕过白名单,而file[count(file) – 1]不等于jpg或png或gif。这里可以构造save_name[0] = 1.php/,save_name[2] = jpg,这样的话end(file)为jpg,而file[count(file) – 1]为file[1]为空。所以最终file_name=1.php/.,到这里就跟Pass-19一样了。
绕过
直接构造如下图的payload再上传,提示上传成功。
新窗口打开此上传文件,发现已经是命名为1.php了。连接试试呢?也是成功连接。
参考:
https://www.jianshu.com/p/aabc1e7408d5
https://segmentfault.com/a/1190000019450720