Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析

0
0

作者: i9n0re

简介

看到了国外有大佬发了关于WordPress的一个非常有名的插件,contact form 7的漏洞,之前见到过很多WordPress站点使用这个插件,大佬写的比较笼统,一些详细的利用方式没有说的太明白.

漏洞成因

这个漏洞是由于插件开发者对WordPress的使用不当造成的,其实跟WordPress的逻辑有一定的关系,导致了可以发布普通文章的用户,可以绕过权限认证,进行发表原本插件作者只允许管理员创建和修改的自定义类型的"post"。

漏洞作者发现了contact from 7插件存在这样的缺陷,导致了任意一个可以发表普通文章的用户,可以新建一个contact,而且在5.0.3版本下,附件可以跨目录进行添加文件,进而可以读取网站的 wp-config.php

相关的技术点 nonce

首先我们了解下什么是nonce?nonce相当于 csrf token 是WordPress用来防御csrf问题的,并进行了相关的权限验证。

post_type

post_type 是插件作者注册的自定义 post 类型,与WordPress的文章类似,插件作者要实现一个页面来进行管理文章类型。只有在后台的新建或者编辑页面当中可以获取到 nonce 随机数,提交的时候只有代入了nonce才能进行相应的操作。

漏洞详情

以contact form 7 v5.0.3为例。

插件作者只允许WordPress的 editor 才能新建和编辑contact。


Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析

如果是文章的发布者,就没有修改和创建权限,会显示下面的页面。


Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析

contact form 7也是一种自定义类型的 post ,数据里面都是存在了 wp_posts 表当中,通过 post_type 进行区分。


Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析

正常情况,插件作者是通过 current_user_can('publish_pages') 进行权限的判定,也就是说editor以上的权限可以编辑,防止普通用户打开新建和修改文章的页面。

但是用户仍可以操作普通的文档,通过请求接口 wp-admin/post.php 的方式进行新建和编辑文章,只不过 post_type 变为了 post 等普通文档类型。由于插件作者在 register_post_type 的时候没有进行相关权限的配置,仅仅依靠了 current_user_can('publish_pages') 验证用户编辑权限,出现了安全问题。

接下来我们来看看普通文档的新建、编辑流程。

正常流程上,普通文档上传接口是请求 post-new.php后先生成一个 post ,然后再进行编辑,请求post.php,设置参数 action 为 editpost 。

// wp-admin/post.php case 'editpost': check_admin_referer('update-post_' . $post_id); $post_id = edit_post(); // Session cookie flag that the post was saved if ( isset( $_COOKIE['wp-saving-post'] ) && $_COOKIE['wp-saving-post'] === $post_id . '-check' ) { setcookie( 'wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); } redirect_post($post_id); // Send user on their way while we keep working exit();

通过函数 check_admin_referer 检测nonce是否合法。

// wp-includes/pluggable.php // Nonce generated 0-12 hours ago $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 ); if ( hash_equals( $expected, $nonce ) ) { return 1; } // Nonce generated 12-24 hours ago $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); if ( hash_equals( $expected, $nonce ) ) { return 2; }

漏洞利用的地方是通过传入action为 post ,调用下面这个逻辑。

// wp-admin/post.php case 'post': check_admin_referer( 'add-' . $post_type ); $post_id = 'postajaxpost' == $action ? edit_post() : write_post(); redirect_post( $post_id ); exit();

其中 post_type 是通过传入的 post_id 去数据库里面查询得到。

// wp-admin/post.php if ( $post_id ) $post = get_post( $post_id ); if ( $post ) { $post_type = $post->post_type; $post_type_object = get_post_type_object( $post_type ); }

可以看到,如果传入的 post_id 为正常帖子创建请求,这个地方的 nonce 普通用户就可以通过页面进行获取了。

绕过nonce检测 可以看到 post_id 是通过 $_GET['post'] 或者 $_POST['post_ID'] 两种方式获取。 // wp-admin/post.php if ( isset( $_GET['post'] ) ) $post_id = $post_ID = (int) $_GET['post']; elseif ( isset( $_POST['post_ID'] ) ) $post_id = $post_ID = (int) $_POST['post_ID']; else $post_id = $post_ID = 0;

所以如果我们构建一个存在的并且 post_type 为 post 的帖子ID作为参数传入的话, check_admin_referer 的参数变为了固定值 add-post 这样的话,如果我们拿到了nonce就可以绕过了检测。有同学会问,怎么得到这个nonce呢?通过跟代码,我发现在 dashboard 页面当中,下面这个功能里面就有我们需要的nonce,通过查看源代码,获取这个表单的input这样就绕过了检测。

<input type="hidden" id="_wpnonce" name="_wpnonce" value="xxx" />
Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析
创建自定义类型的post

绕过了nonce检测后,我们来看 post 那个case,流程会进入到 write_post 函数,然后我们看到这个逻辑。

// wp-includes/post.php if ( !current_user_can( $ptype->cap->edit_posts ) ) { if ( 'page' == $ptype->name ) return new WP_Error( 'edit_pages', __( 'Sorry, you are not allowed to create pages on this site.' ) ); else return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to create posts or drafts on this site.' ) ); }

问题就出现在这,因为作者在注册post_type的时候没有进行权限限制,导致了权限提升。

v5.0.3 的插件配置:

//wp-content/plugins/contact-form-7/includes/contact-form.php public static function register_post_type() { register_post_type( self::post_type, array( 'labels' => array( 'name' => __( 'Contact Forms', 'contact-form-7' ), 'singular_name' => __( 'Contact Form', 'contact-form-7' ), ), 'rewrite' => false, 'query_var' => false, ) ); }

就导致了如果绕过了nonce检测,普通用户也就可以成功的创建只有editer权限才可以创建的 contact form 7了。

进一步利用

权限提升已经完成,下面就是利用了contact from 7 v5.0.3的一个问题。


Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析

当发送邮件的时候,可以携带附件,但是这个附件可以跨目录读取,导致了用户可以直接携带 wp-config.php 进行发送,实现敏感信息的泄露。

漏洞利用

理清了漏洞触发逻辑,利用方式就简单了,在后台登录页面,直接引用 poc.js 。

注意修改几个参数

meta_input[_mail][recipient] meta_input[_mail][attachments]

然后可以在控制台里面引用,会发现新建了一个表单,然后在帖子里面正常引用这个表单,再页面中使用,并点击发送后,在自己的收件箱当中收到 wp-config.php 的附件。


Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析
漏洞修复

在注册 post_type 的时候,配置权限。

//wp-content/plugins/contact-form-7/includes/contact-form.php public static function register_post_type() { register_post_type( self::post_type, array( 'labels' => array( 'name' => __( 'Contact Forms', 'contact-form-7' ), 'singular_name' => __( 'Contact Form', 'contact-form-7' ), ), 'rewrite' => false, 'query_var' => false, 'capability_type' => 'page' ) ); }

如果进行了这样的配置的话,在进行 write_post 这个函数逻辑的时候

// wp-includes/post.php if ( !current_user_can( $ptype->cap->edit_posts ) ) { if ( 'page' == $ptype->name ) return new WP_Error( 'edit_pages', __( 'Sorry, you are not allowed to create pages on this site.' ) ); else return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to create posts or drafts on this site.' ) ); }

这个判断才会生效,导致权限认证失败。

原文链接

poc.js


Viewing all articles
Browse latest Browse all 12749