越权操作位于 XMLRPC 文章编辑操作中,涉及文件 /wp-includes/class-wp-xmlrpc-server.php (5042-5327) 其中关键代码分析:
<pre>
public function mw_editPost( $args ) {
$this->escape( $args );
$post_ID = (int) $args[0]; // 获取需要编辑的文章ID (用户所属)
$username = $args[1]; // 从请求的xml中获取用户名
$password = $args[2]; // 从请求的xml中获取用户密码
$content_struct = $args[3]; // 从请求的xml中获取结构
$publish = isset( $args[4] ) ? $args[4] : 0;
(...省略)
if ( isset( $content_struct["{$post_type}_status"] ) ) {
switch( $content_struct["{$post_type}_status"] ) {
case 'draft':
case 'pending':
case 'private':
case 'publish':
$post_status = $content_struct["{$post_type}_status"]; // 数据库中存储的文章类型为post,所以取的是xml中 post_status 参数的值
break;
default:
$post_status = $publish ? 'publish' : 'draft';
break;
}
}
</pre>
<p>首先处理程序获取提交参数并验证当前用户权限,对于草稿或者未审核的文章,其数据库中存储的文章类型为 "post",所以在取值 $content_struct["{$post_type}_status"] 时,获取的是提交参数中 "post_status" 的值。</p>
<pre>
(...省略)
// 当用户不具有文章发布权限时,`publish` 操作会被禁止
// 但是这里并没有限制 `private` 的情况
// 所以若xml中 post_status 参数值为 private 则跳过检查
if ( ('publish' == $post_status) ) {
if ( ( 'page' == $post_type ) && ! current_user_can( 'publish_pages' ) ) {
return new IXR_Error( 401, __( 'Sorry, you do not have the right to publish this page.' ) );
} elseif ( ! current_user_can( 'publish_posts' ) ) {
return new IXR_Error( 401, __( 'Sorry, you do not have the right to publish this post.' ) );
}
}
</pre><p>接着,程序会验证其提交的需要更新的文章状态。当用户通过 XMLRPC 进行文章编辑时,若想发布一篇未发布的文章时,会检查用户是否具有文章发布的权限。但是该检查判断了将文章状态变为发布状态的情况下(post_status == publish),而针对将文章状态变为私有状态的情况代码中并没有进行检查。程序上的判断疏忽,致使我们可以使用一个不具有文章发布权限的帐号将自己一篇 `未通过审核` 或者 `存于垃圾箱` 的文章的转台通过该过程将其改为私有("private"),让该文章以私文的形式在前台显示出来,管理员以及其他具有权限的用户都能浏览到。</p><p>另一个需要说明的点就是,通过 XMLRPC 操作编辑文章时,可以将文章进行置顶,具体代码为:</p><pre class="lang-php" data-lang="php"> (...省略)
// 将文章置顶(4.3.0 版本后不能将未设密码的私有文章置顶)
// Only posts can be sticky
if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
$data = $newpost;
$data['sticky'] = $content_struct['sticky'];
$data['post_type'] = 'post';
$error = $this->_toggle_sticky( $data, true );
if ( $error ) {
return $error;
}
}
</pre><p>但是该问题在 WordPress 4.3.0 版本中已经得到了限制:</p><pre class="lang-php" data-lang="php"> private function _toggle_sticky( $post_data, $update = false ) {
$post_type = get_post_type_object( $post_data['post_type'] );
// Private and password-protected posts cannot be stickied.
if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) {
// 如果需要置顶的文章为私有状态,并且未设访问密码,不能将其置顶,并自动取消之前的置顶状态
// Error if the client tried to stick the post, otherwise, silently unstick.
if ( ! empty( $post_data['sticky'] ) ) {
return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
}
if ( $update ) {
unstick_post( $post_data['ID'] );
}
} elseif ( isset( $post_data['sticky'] ) ) {
// 如果需要置顶的文章为私有状态,并且设有访问密码,且具有编辑其他文章的权限,则将其所置顶的文章置顶
if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
}
$sticky = wp_validate_boolean( $post_data['sticky'] );
if ( $sticky ) {
stick_post( $post_data['ID'] );
} else {
unstick_post( $post_data['ID'] );
}
}
}
</pre>
<p>未设密码访问的私有文章已经无法再通过 XMLRPC 编辑文章操作将文章置顶。</p><p>下面通过示例来说明如何通过 XMLRPC 编辑文章操作将文章状态修改为 "私有"。为了方便演示,这里事先注册好一个用户(投稿者权限,其投稿的文章状态为 "pending"),并提交一篇文章投递申请:</p><p><img alt="5.png" src="https://images.seebug.org/contribute/42dd31e3-8b4f-4785-a535-4e223ed0c2f9-5.png" data-image-size="2560,1466"><br></p><p>查看一下待审文章在数据库中的状态:</p><p><img alt="6.png" src="https://images.seebug.org/contribute/95631ba7-4ae6-477b-8b23-b64bf29d2de8-6.png" data-image-size="2182,630"><br></p><p>然后根据上面所分析的权限提升细节,构造 payload ,将此待审文章状态更改为 private:</p><p><img alt="7.png" src="https://images.seebug.org/contribute/93e91ce3-0d12-4957-bb92-914d41a012ac-7.png" data-image-size="2100,1248"><br></p><p>可以看到返回消息中提示置顶私有文章失败,这是因为测试时使用的 WordPress 4.3.0 版本,该版本中已经修复了私有文章任意置顶的问题。</p><p>然后查看一下通过 XMLRPC 编辑文章后数据库中待审核文章的状态:</p><p><img alt="8.png" src="https://images.seebug.org/contribute/6ea85914-93f5-458b-9333-6712e3b58e40-8.png" data-image-size="2182,612"><br></p><p>数据库中文章状态已经变为私有,再到前台查看首页:</p><p><img alt="10.png" src="https://images.seebug.org/contribute/379ef946-5873-450f-904d-9c50fb45241c-10.png" data-image-size="2560,1388"><br></p><p>由投稿用户提交的待审核文章已经变为私有状态显示在前台页面中,并且管理员能看到所有的私有文章。</p>
暂无评论