WordPress5.0.0 远程代码执行

原文链接:https://blog.ripstech.com/2019/wordpress-image-remote-code-execution/


a06603d0617c5d4e5b9c883234c74675.png

这篇博文讲述了利用目录穿越和本地文件包含漏洞的“组合拳”在WordPress核心功能实现远程代码执行。过去6年,该WordPress漏洞一直未被揭示。

影响

POC视频1

攻击者需要在目标WordPress网站拥有至少作者级权限的账户,然后就能在后台服务器执行任意的php代码,从而远程攻陷目标。我们向WordPress安全团队报告了另一个漏洞的详情,该漏洞可以让攻击者入侵任何的WordPress站点,目前尚在修复中。

受影响版本

受一个安全补丁的限制,文中描述的漏洞在这4.9.9和5.0.1版本中无法利用。但目录穿越漏洞依然存在并且未被修补。本漏洞可利用在安装了对Post Meta记录处理不当的插件的WordPress网站上。在之前的WordPress安全月活动中,我们已经见识过一些存在问题的安装量过百万的插件。

据WordPress网站的下载页面称,全网超过33%的网站在使用WordPress。考虑到插件可能会重新引入漏洞,以及其他因素如网站WordPress版本过旧等,受影响的网站数仍然是百万级。

技术分析

目录穿越和本地文件包含漏洞都是由我公司的主打的SAST(静态应用安全测试)解决方案RIPS自动检测到的,你只需要点击按钮,它就会在3分钟内完成扫描。不过,这些bug一开始看上去无法利用。事实证明,利用这些漏洞要复杂的多,好在有可能利用。

POC视频2

背景知识——WordPress图片管理

当图片上传到WordPress网站时,它先被存储到上传目录(wp-content/uploads)下。WordPress会在数据库中生成一条关于图片的引用,来记录图片属主、上传时间等元信息。这些元信息在库中以Post Meta记录的形式存在,每条记录都是键值对,并被分配一个确定ID。

0a026c44b09f5cdedda315b8ef791cd9.png

如上图,已为图片分配的post_ID号为50,如果用户以后想使用或修改ID所属的图片,WordPress将查找匹配_wp_attached_file的元记录,并用它的值去wp-content/uploads目录下寻找文件。

核心问题——Post Meta记录被覆盖

在WordPress4.9.9和5.0.1之前的版本中,主要问题在于可以修改任意Post Meta记录并将设置它们的值。当图片更新时(例如,它的描述发生变化)会调用edit_post()函数。该函数直接操作$ _POST数组。

1   function edit_post( $post_data = null ) {
2
3       if ( empty($postarr) )
4            $postarr = &$_POST;
56       if ( ! empty( $postarr['meta_input'] ) ) {
7           foreach ( $postarr['meta_input'] as $field => $value ) {
8               update_post_meta( $post_ID, $field, $value );
9           }
10     }

如上所示,我们可以注入任意的Post Meta记录。因为没有关于记录修改得检查,攻击者可以更新_wp_attached_file 元记录并把它设置为任何值。这并不会重命名真实文件,但它会更改WordPress在编辑图像时要寻找的文件名。这将导致之后的目录穿越漏洞。

修改Post Meta造成目录穿越

当用户裁剪图片时会调用wp_crop_image()函数,目录穿越漏洞就发生在这里。

该函数获取要裁剪图片的ID($attachment_id)并从数据库中提取对应的_wp_attached_filePost Meta记录。

还记得么,由于edit_post()函数的缺陷,$src_file可以被设定成任意值。

1   function wp_crop_image( $attachment_id, $src_x, ...) {
2
3       $src_file = $file = get_post_meta( $attachment_id, '_wp_attached_file' );
4

接下来,WordPress必须确保图片实际存在并加载它。 WordPress有两种加载指定图片的方法。第一种是简单地查找wp-content/uploads目录中的由_wp_attached_filePost Meta记录提供的文件名(下一代码段的第2行)。

如果这一方法失败,WordPress将尝试从其自己的服务器下载图片以作备用。为此,它将生成一个下载URL,该URL由wp-content/uploads目录的URL和存储在_wp_attached_filePost Meta记录中的文件名构成(下一代码段的第6行)。

举个栗子:如果存储在_wp_attached_filePost Meta记录中的值是evil.jpg,那么WordPress将首先尝试检查文件wp-content/uploads/evil.jpg是否存在。如果不存在,它将从以下URL下载该文件:https://targetserver.com/wp-content/uploads/evil.jpg

之所以选择下载图片而不是在本地查找,是因为有时某些插件会在用户访问URL时动态生成图像。

请注意,这里没有任何过滤。WordPress将简单地拼接上传目录的基础URL和用户输入的$src_file

一旦WordPress通过wp_get_image_editor()函数成功加载了有效图片,它便开始裁剪图片。

   12    if ( ! file_exists( "wp-content/uploads/" . $src_file ) ) {
   3            // If the file doesn't exist, attempt a URL fopen on the src link.
   4            // This can occur with certain file replication plugins.
   5         $uploads = wp_get_upload_dir();
   6         $src = $uploads['baseurl'] . "/" . $src_file;
   7     } else {
   8         $src = "wp-content/uploads/" . $src_file;
   9     }
  10
  11     $editor = wp_get_image_editor( $src );
  12

接着,裁剪后的图片会被保存回文件系统(无论是下载的还是本地的)。生成的文件名是get_post_meta()返回的$src_file的值,它正好可以由攻击者控制。生成的文件名由一条规则生成:在文件的原始名称前添加cropped-(下一代码段的第4行)。在本例中,生成的文件名为cropped-evil.jpg

然后WordPress就会通过wp_mkdir_p()函数(第6行)在结果路径中创建任何之前不存在的目录。

最后,它使用图像编辑器对象的save()方法将其写入文件系统。save()方法也不对给定的文件名做目录穿越漏洞检查。

    12   $src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
    3
    4   $dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
    5
    6   wp_mkdir_p( dirname( $dst_file ) );
    7
    8   $result = $editor->save( $dst_file );

利用思路

到目前为止,我们发现由于没有做检查,可以确定哪个文件被加载到图像编辑器中。但是,如果文件不是张有效图像,图像编辑器对象将抛出异常。这里可以假设:WordPress只能裁剪上传目录之外的图片。

但是,如果WordPress未找到图片,会尝试下载图片,将会导致远程执行代码漏洞。

本地文件 HTTP下载
已上传文件 evil.jpg evil.jpg
_wp_attached_file evil.jpg?shell.php evil.jpg?shell.php
加载时结果文件 wp-content/uploads/evil.jpg?shell.php https://targetserver.com/wp-content/uploads/evil.jpg?shell.php
实际位置 wp-content/uploads/evil.jpg https://targetserver.com/wp-content/uploads/evil.jpg
生成的文件名 None-image loading fails evil.jpg?cropped-shell.php

我们的思路是将_wp_attached_file设置为evil.jpg?shell.php,这将导致对以下URL发出HTTP请求:https://targetserver.com/wp-content/uploads/evil.jpg?shell.php,这个请求会返回一个有效的图像文件。因为在此处的上下文中,文件名?之后的部分会被忽略。生成的文件名将是evil.jpg?shell.php

图像编辑器的save()方法虽然不会检查目录穿越漏洞,但它会添加加载图片的mime类型的扩展名到生成的文件名中。在本例中,生成的文件名是evil.jpg?cropped-shell.php.jpg。这使得新创建的文件再次无法利用。

但是,仍然可以使用诸如evil.jpg?/../../evil.jpg之类的有效攻击载荷将生成的图像植入任何目录。

在主题目录下利用目录穿越&LFI

每个WordPress主题都是wp-content/themes目录下的一个目录,并为不同的情形提供模板。例如,如果博客的访问者想看帖子,WordPress会在当前活动主题的目录中查找post.php文件。如果该PHP文件找到了模板,就会include()它。

WordPress中允许为某些帖子选择自定义模板。用户需要将数据库中的_wp_page_templatePost Meta记录设定为特定的文件名。这里的唯一限制是被include()的文件必须位于当前活动主题的目录中。

通常,该目录无法访问,也无法上载文件。但是,通过利用上述的目录穿越漏洞,可以将恶意制作的图像植入到当前使用的主题的目录中。然后,攻击者可以利用同一漏洞创建一个新帖子,使他能够更新_wp_attached_file的Post Meta记录来include()图像。通过将PHP代码注入图片数据中,攻击者可以拿到远程执行代码漏洞。

构造恶意图片——GD和Imagick

WordPress的PHP支持两种图像编辑扩展:GD和Imagick。它们的区别在于Imagick不会删除图像的exif元数据,而exif中可以存储PHP代码。 GD会压缩它编辑的每个图像并删除所有exif元数据。

但是,通过精心构造图片的像素仍然可以实现利用。这些像素将在GD处理完图像后以某种方式翻转,最终执行PHP代码。在研究PHP GD扩展的内部结构时,我们在libgd中发现了可利用的内存损坏漏洞。(CVE-2019-69772)

时间线

日期 事件
2018/10/16 在Hackerone上向WordPress安全团队报告了漏洞。
2018/10/18 WordPress安全团队成员确认该报告,并表示他们将在报告得到验证后返回。
2018/10/19 另一位WordPress安全团队成员要求提供更多信息。
2018/10/22 我们为WordPress提供了更多信息,并提供了完整的270行漏洞利用脚本来帮助验证漏洞,
2018/11/15 WordPress对漏洞进行了分类,并表示他们能够复制漏洞。
2018/12/06 WordPress 5.0已发布,没有针对该漏洞的补丁。
2018/12/12 WordPress 5.0.1已发布,是一个安全更新。其中一个补丁通过阻止攻击者设置任意设置元记录,使漏洞无法利用。但是,目录穿越仍然可以使用,如果安装了错误处理Post Meta记录的插件,则可以利用它。 WordPress 5.0.1不解决路径遍历或本地文件包含漏洞。
2018/12/19 WordPress 5.0.2发布了。没有针对漏洞的补丁。
2019/01/09 WordPress 5.0.3发布了。没有针对漏洞的补丁。
2019/01/28 我们要求WordPress提供下一个安全版本的ETA,以方便我们安排博客日程,并在WordPress新版本发布后发表博客。
2019/02/14 WordPress提出了一个补丁。
2019/02/14 我们提供有关补丁的反馈,并验证它是否可以防止利用。

总结

本文详细介绍了WordPress核心功能中的远程执行代码,该漏洞已存在超过6年。根据RIPS的报告,在5.0.1和4.9.9版本中由于另一个漏洞的补丁,它变得不可利用。但是,目录穿越仍然可以使用,如果安装了仍允许任意覆盖Post Data的插件,则可以利用该漏洞。由于在攻击目标WordPress站点时需要通过身份认证,因此我们决定在首次报告漏洞的4个月后公开此漏洞。

感谢WordPress安全团队的志愿者,他们在这个问题上与我们合作得十分友好,也很专业。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章