文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)

文件上传漏洞-upload靶场1-2关 通过笔记(区分前段验证和后端验证)

前言

upload是一个文件上传的专用靶场,搭设也非常简单,只需要把相关源码文件放到apache的网站目录下即可使用,或者去github下载一键绿化包进行安装链接如下:

[Releases · c0ny1/upload-labs (github.com)]

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第1张图片

下载后按照使用说明安装即可,在安装该靶场时最好安装在虚拟机中,在此也不做过多的解析了,如有不明白可以私聊我。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第2张图片

upload靶场能帮助我们,实践复现文件上传漏洞,帮助我们更好的学习该漏洞形成的原因,该工具一共20关,除了第一关是前段验证,其他19关都是关于后端验证。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第3张图片

upload的难度也是逐步上升,第十关是一个分水岭,后面的内容也是非常硬核

在开始挑战关卡前,我们先了解下什么才是一次成功的攻击:

  1. webshell要成功的上传到服务器中
  2. 要知道webshell在服务器中的路径
  3. 上传的webshell能被正常解析

只有满足这三条条件,才能算一次成功的攻击。

upload第一关(JS验证)

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第4张图片

javascript是一个前端常用的语言,用它写的代码在一般情况都是属于前端,不过当你使用node.js为服务器环境时候,javascript也可以作为后端语言来使用。

判断思路

在上传webshell时,我们并不知道该网站使用了那些方法来验证上传的文件,所以我们要从分析源码、抓取流量、随意上传一个文件等方法去分析它的验证方式,以便找到适合的方法去进行攻击。

第一种方法:分析源码
            <h3>任务h3>
            <p>上传一个<code>webshellcode>到服务器。p>
        li>
        <li>
            <h3>上传区h3>
            <form enctype="multipart/form-data" method="post" onsubmit="return checkFile()">
                <p>请选择要上传的图片:<p>
                <input class="input_file" type="file" name="upload_file"/>
                <input class="button" type="submit" name="submit" value="上传"/>
            form>


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

以上源码是从该网站中找到的,其中有一个form表单和一个js脚本较为关键,在form表单中又有两个input标签

form表单有三个属性分别是:

  • enctype属性:表单中用于指定在提交表单数据时使用的编码类型
    • 它的值为multipart/form-data:
      • 表单数据会被分割为多个部分(multipart),每个部分都包含一个表单字段的数据。这种编码类型可以同时传输文本数据和二进制数据,比如上传的文件
  • mothod属性:表示这个表单接收数据的方法
    • 它的值为post:
      • 使用post方法来接收数据
  • onsubmit属性:用于指定在提交表单时执行的 JavaScript 代码
    • 它的值为return checkFile():
      • 用来指定在表单提交之前执行的 JavaScript 函数 checkFile()

input标签解析:

  • - 该代码可以用于创建一个文件上传的输入字段,允许用户选择本地计算机上的文件并上传到服务器。它的表示name值为upload_file
  • - 创建一个提交按钮,并在用户界面中显示 "上传" 文本。当用户点击此按钮时,表单的提交动作将被触发,将表单数据发送到服务器。

从下面的js代码中我们发现了 checkFile()的函数,它的第一句,就是使用 document.getElementsByName() 方法来获取带有 name="upload_file" 属性的元素,并通过索引 [0] 来获取第一个匹配的元素,然后使用 .value 属性获取其值,如果没有上传文件,或上传空文件则提示请选择要上传的文件。

在下半段js代码中,首先限定了上传文件后缀,只能上传这三种后缀的文件,又通过调用 file.lastIndexOf(".") 方法,可以获取文件名中最后一个点(.)的索引。然后,通过调用 file.substring() 方法,可以从该索引位置开始截取字符串,得到文件名中的扩展名进行判断,如果上传的文件不符合要求,则提示用户上传正确的格式的文件

通过这一段代码的分析,我们就可以判断它是一个前端验证的文件上传,确定了是前段验证,那么问题就简单了,所谓前端验证都是纸老虎可不是开玩笑的哈哈。

js代码函数分析

document.getElementsByName()

  • 它是JavaScript 中的一个 DOM 方法,用于根据元素的 name 属性获取文档中所有匹配的元素集合。

  • 该方法接收一个字符串参数,表示要获取的元素的 name 属性的值,返回一个类数组对象,包含所有与指定 name 属性匹配的元素。

对象.lastIndexOf()

  • 它是 JavaScript 字符串的方法,用于获取字符串中最后一个出现的指定字符或子字符串的索引位置。

对象.substring()

  • 它是 JavaScript 字符串的方法,用于获取字符串中指定索引位置之间的子字符串。
第二种方法抓包

首先我们打开Burp抓取一个upload上传的包

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第5张图片

把这个包放到repeater(重放器)中进行分析

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第6张图片

在此处我们发现,我们上传了一个名为123.jpg的文件,

Content-Type: image/jpeg 表示我们发送的是一个图片文件

在此我们对我们上传的文件后缀名进行更改

  • 如果是前端验证,更改文件后缀名后依旧能正常发送,因为它已经经过了验证。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第7张图片

在修改文件后缀为asp后,也是成功发送给后端服务器,这也能证明它只使用前端验证

第三种方法:随意上传文件分析

这种方法是最为快捷的,但是判断是否是前段验证或后端验证,这种方法就非常考验我们的经验。

如果是前端验证,它的报错响应速度会非常快,因为它一直在你本地的前端,如果是后端服务器验证,相反它的响应速度就会有一定的延迟,具体还得看网络情况,为什么会有延迟呢,我统计了一下情况供大家参考:

  1. 网络延迟:前端验证是在客户端执行的,而后端验证涉及将数据发送到服务器并等待服务器响应。这意味着在进行后端验证时,会出现网络延迟的情况,这可能会导致更长的验证时间。

  2. 请求处理时间:后端验证涉及将数据发送到服务器上的处理程序,然后由服务器上的应用程序进行验证并返回响应。与前端验证相比,服务器的请求处理时间会更长,因为服务器可能涉及其他任务、处理其他请求或执行其他复杂的验证逻辑。

  3. 数据传输量:后端验证涉及将数据上传到服务器进行处理,这需要将数据通过网络传输到服务器,这可能会涉及较大的数据量,因此相对于前端验证来说可能更慢。

攻击思路

前端验证最大的缺点,就是它可以被客户端篡改,这文件就在我本地计算机中,我想咋改就咋改,而后端服务器就比较麻烦了,在后面也会详细的去介绍如何成功上传。

前段验证的攻击方法:

1、直接修改源码:

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第8张图片

使用浏览器自带的检查工具,找到前端验证的代码,直接删除。

2、使用bp抓包工具:

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第9张图片

直接在抓包工具中修改文件后缀名,因为它已经通过了前段的验证,这时候修改对上传的文件毫无影响。

3、使用br禁用网页的js功能

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第10张图片

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第11张图片

4、使用浏览器自带的禁用js功能(火狐浏览器)

1.在火狐浏览器的url界面中输入 about:config 进入高级设置界面

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第12张图片

找到javascript.enabled

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第13张图片

将javascript.enabled的true改为false

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第14张图片

同样也能达到br抓包禁用的效果,不过在这里不建议使用浏览器的禁用方法,它会把所有的js全部都禁用掉,一些正常的js也会变层无法使用。

最后上传一个简单的webshell脚本,来测试是否成功。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第15张图片

已经成功获取的网站权限!


upload第二关(MIME验证)

什么是MIME?

MIME(Multipurpose Internet Mail Extensions)是一种用于标识文件类型的标准。它是在互联网上发送邮件和其他数据的一种常用方式。

MIME 类型由两部分组成:主类型和子类型,之间用斜杠(/)分隔。主类型表示文件的大类别,而子类型表示具体的文件类型。例子如下:

  • 文本文件:text/plain
  • HTML 文件:text/html
  • JPEG 图像文件:image/jpeg
  • PNG 图像文件:image/png
  • JSON 数据文件:application/json
  • PDF 文档:application/pdf

MIME 类型主要用于在 HTTP 协议中指定传输的数据类型,并且还在其他应用程序中进行使用,例如电子邮件、文件上传等。通过使用正确的 MIME 类型,服务器和客户端能够理解传输的数据类型,并进行相应的解析和处理。

MIME 类型还可用于指定数据的字符编码、语言和其他相关信息,这些信息用于确保数据的正确显示和处理。

在 HTTP 协议中,MIME 类型常用于请求报文和响应报文的 Content-Type 头字段中,用于指定将要发送或接收的数据的类型。

判断思路

首先还是随意上传一个文件,来判断一下它究竟是前段验证,还是后端服务器验证。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第16张图片

查看源码发现,from表单也有一个onsubmit属性和第一关的一模一样,难道它也是前段验证?抱着怀疑的我们按第一关的操作,把这个属性删除在尝试上传,看看是否能成功。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第17张图片

删除该属性后,尝试上传文件,任然提示文件类型不正确,此时我们就拿出最终绝招,用bp抓个数据包,也按照第一关的方法,去尝试是否能成功上传。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第18张图片

我们上传一个jpeg的图片文件,经过bp后把后缀名改为php,然后放行,看看是否能够成功上传。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第19张图片

居然成功上传了,难道它也是前段验证?那为什么我们删除验证的js脚本函数后,又不能成功上传呢?带着这些疑问,我又开始抓包研究。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第20张图片

在此我有了发现,在第一关我开启Bp抓包的时候,上传一个任意文件,如果上传的文件类型错误,bp就抓不到数据包,而是在网页中直接弹出一个警告框

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第21张图片

而到了第二关,我上传一个随意文件时,Bp能抓到一个请求包,并在Content-Type中显示了当前上传文件的类型,text/plain,在此我就可以判断第二关的验证是后端服务器,下面是我画的一张前端验证、后端验证的流程图。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第22张图片

总结一下思路,

  • 如果是前段验证,当bp开启抓包的时候,文件类型错误,将抓不到任何请求包。
  • 如果是服务器后端验证,无论是否成功,Bp都能抓到对应的响应包。
  • 这也就是前段验证和后端验证的最大区别。

攻击思路:

当我们分清楚前端验证和后端验证的区别后,就可以开始分析如何在后端验证的情况,上传一个webshell。

在判断思路中,我们使用第一关的攻击思路,上传一个图片格式的文件,用bp抓包并修改文件后缀,能够成功将webshell上传到站点,但是使用其他格式缺总是失败,而且我们还发现上传不同类型的文件,content-Type的值也是不一样的,这一点证明的后端服务器,可能是开启了一个白名单模式,只有指定的文件类型才能成功上传,那么我们是否可以用bp抓包,对content_type的值进行篡改,来实现攻破后端服务器验证的关卡呢?心动不如行动,我们一起来实践一下。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第23张图片

首先我们先上传一个webshell,后缀为php,在用bp抓住这个包,使用repeter进行验证我们的想法。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第24张图片

我们只修改Content-type的值尝试,能不能正常上传。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第25张图片

果然和我想的一样,只要我们修改了Content-type的值为图片类型,我们就可以上传任意文件,包括webshell。再来尝试上传的webshell是否能正常解析。

文件上传漏洞-upload靶场1-2关 通过笔记(如何区分前段验证和后端验证)_第26张图片

使用post方法进行传参,能够成功执行phpinfo();函数,这叫代表我们已经成功闯过了第二关,也就初步了掌握了文件上传的基础技巧,后面还有18个关卡,我们还要继续努力。

后端源码分析
$is_upload = false;
$msg = null;
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.'文件夹不存在,请手工创建!';
    }
}


在解析源码前,我们先看看这串源码中,使用了哪些函数
  1. isset():用于检查变量或表单字段是否已设置和存在。
  2. file_exists():用于检查指定文件或目录是否存在。
  3. $_POST:是一个包含通过 POST 方法提交的表单字段和值的关联数组。在这里,$_POST['submit'] 用于检查表单是否通过提交按钮进行了提交。
  4. $_FILES:是一个包含通过文件上传的文件信息的关联数组。在这里,$_FILES['upload_file'] 包含了关于上传文件的信息,如文件名、临时路径、文件大小、文件类型等。
  5. move_uploaded_file():用于将上传的文件移动到目标位置。

以下是对代码的解释和说明:

在这段代码中,首先定义了一个布尔变量 $is_upload 并初始化为 false,用于表示文件是否成功上传。还定义了一个变量 $msg ,用于存储上传过程中的提示信息。

通过使用 isset($_POST['submit']) 检查表单是否通过提交按钮进行了提交。

使用 file_exists(UPLOAD_PATH) 来检查指定的上传目录是否存在。UPLOAD_PATH 是一个用于存储上传文件的目录路径。

使用 $_FILES['upload_file']['type'] 来获取上传文件的 MIME 类型(例如:image/jpegimage/pngimage/gif)。

通过使用 $_FILES['upload_file']['tmp_name'] 获取上传文件的临时路径。

通过使用 $_FILES['upload_file']['name']UPLOAD_PATH 来创建存储上传文件的目标路径。

使用 move_uploaded_file($temp_file, $img_path) 将上传的临时文件移动到目标路径。

当移动文件成功时,将 $is_upload 设置为 true,如果移动文件失败,则将 $msg 设置为 ‘上传出错!’。

如果上传文件的类型不是 ‘image/jpeg’、‘image/png’ 或 ‘image/gif’,将 $msg 设置为 ‘文件类型不正确,请重新上传!’。

如果上传目录不存在,将 $msg 设置为 UPLOAD_PATH 所指定的目录路径后面跟上 ‘文件夹不存在,请手工创建!’。

这个代码片段主要用于处理文件上传过程,并设置content-type的值为image/jpeg、png、gif,也就是说只要请求头中的content-type字段的值是上诉的文件类型,都可以成功上传。
文件移动到目标路径。

当移动文件成功时,将 $is_upload 设置为 true,如果移动文件失败,则将 $msg 设置为 ‘上传出错!’。

如果上传文件的类型不是 ‘image/jpeg’、‘image/png’ 或 ‘image/gif’,将 $msg 设置为 ‘文件类型不正确,请重新上传!’。

如果上传目录不存在,将 $msg 设置为 UPLOAD_PATH 所指定的目录路径后面跟上 ‘文件夹不存在,请手工创建!’。

这个代码片段主要用于处理文件上传过程,并设置content-type的值为image/jpeg、png、gif,也就是说只要请求头中的content-type字段的值是上诉的文件类型,都可以成功上传。

你可能感兴趣的:(网络安全专栏,upload靶场通关,笔记)