8

Wordpress4.2.3提权与SQL注入漏洞(CVE-2015-5623)分析 | WooYun知识库

 6 years ago
source link:
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Wordpress4.2.3提权与SQL注入漏洞(CVE-2015-5623)分析

这是这几天一直关注的漏洞了,wordpress上个礼拜发布的4.2.4版本,其中提到修补了可能存在的SQL漏洞和多个XSS。 Check point也很快发出了分析,我也来分析与复现一下最新的这个漏洞。

0x01 GP混用造成的越权漏洞


首先,说明一下背景。wordpress中用户权限分为订阅者、投稿者、作者、编辑和管理员。

权限最低的是订阅者,订阅者只有订阅文章的权限,wordpress开启注册后默认注册的用户就是订阅者。国内很多知名网站,如Freebuf,用户注册后身份即为“订阅者”。

我们先看到一个提权漏洞,通过这个提权漏洞,我们作为一个订阅者,可以越权在数据库里插入一篇文章。

Wordpress检查用户权限是调用current_user_can函数,我们看到这个函数:

enter image description here

调用的has_cap方法,跟进

enter image description here

再次跟进map_meta_cap函数:

enter image description here

可以见到,这个函数是真正检查权限的。出错误的代码在检查’edit_post’’edit_page’的部分:

enter image description here

可见,这里当$post不存在的时候,直接breakswitch逻辑了,后面所有检查的代码都没有执行。

$post是要编辑的文章的ID,也就是说,如果我要编辑一篇不存在的文章,这里不检查权限直接返回。

正常情况下是没有问题的,因为不存在的文章也没有编辑一说了。

我们再看到后台编辑文章的部分:/wp-admin/post.php

enter image description here

这里首先获取$_GET[‘post’],找不到才获取$_POST[‘post_ID’],也就是可以说此时的$post_ID是来自GET的。

但我们后面调用current_user_can函数时传入的post_ID却是来自POST的:

enter image description here

这里就是一个逻辑问题,当我们在GET参数中传入正确的postid(这样在get_post的时候不会产生错误),而在POST参数中传入一个不存在的postid,那么就能够绕过检查edit_post权限的步骤。

但是这个逻辑错误暂时不能造成严重的危害,因为实际上编辑文章的代码在edit_post函数中,而这个函数取的post_ID来自$_POST

0x02获取_wpnonce绕过CSRF防御


wordpress对于CSRF漏洞的防御措施是使用_wpnonce(也就是token),而且它的token很严格,不同的操作有不同的token。

比如我们这里,如果想调用edit_post函数,需要经过以下逻辑:

enter image description here

check_admin_referer就是检查_wpnonce的函数,当$post_type==’postajaxpost’的时候,此时_wpnonce的名字就是“add-postajaxpost”

那么怎么获取名字为”add-postajaxpost”_wpnonce呢?

看到上面一点的位置:

enter image description here

有个post-quickdraft-save操作。这个操作是用来临时储存草稿的,只要用户访问这个操作,就会在数据库post表中插入一个statusauto-draft的新文章。

如上图画出来的步骤,因为我们不知道名字为”add-post”_wpnonce,所以进入到wp_dashboard_quick_press函数,跟进:

enter image description here

见上图,很幸运的是,在这个函数中wordpress居然自己把此时的_wpnonce输出在表单里了。

所以,只要我们访问一次post-quickdraft-save,就可以获得add-post_wpnonce,从而绕过check_admin_referer函数。

0x03 竞争漏洞导致的逻辑漏洞


这一节实际上是这个提权洞的真正核心,在我们拿到_wpnonce后,进入edit_post函数。

我们目的是去update一篇文章,但刚才0x01中说到,如果要绕过权限检查的函数,需要传入一个“不存在”的文章id。那么即使可以执行update,我们也不可能修改已经存在的文章呀?

这里实际上涉及到一个由竞争造成的逻辑漏洞。看到edit_post函数代码:

enter image description here

上面两个图应该很直观了。在0x01中说到的current_user_can被绕过以后,到最终执行update语句中间,这一段代码的执行时间是真空的。

比如我们传入的tax_input=1,2,3,4…10000,那么实际上那条查询语句就要执行10000次,这是需要执行很长时间的。(在我自己的虚拟机上测试,执行10000次这条语句,大概需要5~10秒左右)

那么假设在这段时间内,有新插入的文章,那么我们之前那个“不存在”的id,不就可能可以存在了吗(只需要把id设置为最新一篇文章id+1)? 但有个问题是,我们怎么在这段时间内插入一篇新的文章?因为在0x02中为了获取_wpnonce,已经执行过post-quickdraft-save了。执行post-quickdraft-save可以在数据库插入一篇statusauto-draft的文章,但每个用户最多只会插入一篇文章。

check-point的原文中,它提到的方法是,等待一个星期,wordpress会自动将这篇文章删除,而_wpnonce会多保留一天,这样在这天我们再次执行post-quickdraft-save又可以插入一篇文章了。

我自己想了一下,其实没必要这么麻烦。如果我们能够再注册一个身份为订阅者的账号,就可以再插入一篇文章了,所以我的POC是不需要等待一个礼拜的。

这三个漏洞组合起来,造成了一个提权漏洞。针对第一篇文章描述的提权漏洞,我写了一个EXP,执行后订阅者就可以在垃圾桶内插入一篇文章:

enter image description here

访问文章编辑页面可以看到这篇文章:

enter image description here

0x04 untrash文章时造成的SQL注入漏洞


那么,仅仅是一个这样的提权漏洞,实际上没有太大意义。Check-point第二篇文章里提到了一个因为这个提权漏洞导致的SQL注入。

先说这个注入的原理。

/wp-includes/post.php

enter image description here

如上图。Wordpress很多地方执行SQL语句使用的预编译,但仅限于直接接受用户输入的地方。而上图中明显是一个二次操作,先用get_post_meta函数从数据库中取出meta,之后以字符串拼接的方式插入SQL语句。

这个地方造成一个二次注入。

我们来看看第一次是如何入库的。首先wp_trash_post是将文章删除的方法,其中删除文章后又调用wp_trash_post_comments将文章下的评论也删除了:

enter image description here

跟进wp_trash_post_comments函数:

enter image description here

如上图,可以看到这个“comment_approved”其实也是从数据库中取出来的。所以这个注入我称之为“三次注入”。

那么我再继续跟进,看看最早的comment_approved是从哪来的。

实际上看到图中的SQL语句就大概知道了,这个comment_approvedcomments(评论)表的一个字段,我分别看了新增评论、修改评论两个函数,发现修改评论的函数(edit_comment)中,有涉及到这个字段:

enter image description here

所以,这一连串操作最后造成的结果就是一个SQL注入漏洞。

总结一下1234,整个利用过程如下:

利用快速草稿插入文章->越权编辑文章->插入评论->修改评论(恶意数据入库)->删除文章(恶意数据进入另一个库)->反删除文章(恶意数据被取出,直接插入SQL语句导致注入)

0x05 原文隐藏的部分与真实利用过程的研究


这里不得不提到check-point的原文,原文的第二篇全文只字未提wordpresstoken也就是_wpnonce,但wordpress后台几乎所有操作都需要特定的_wpnonce。在第一步中我们通过一处泄露点获取了“add-postajaxpost”的_wpnonce,但实际上后面的每一步(增加、编辑评论、trash文章、untrash文章)都需要不同的_wpnonce,那么这些_wpnonce我们怎么获得?

经过我的分析,最后实在找不到在订阅者权限下怎么获得“增加评论”和“反删除文章”的_wpnonce,而“修改评论”、“删除文章”的_wpnonce倒是可以在后台找到。

另外,虽然前台也可以增加评论,但前台增加评论会检查所属文章是否是草稿、状态是否是publicprivate,我们没法给这篇文章以及其派生的预览文章增加评论。

所以我把基础账号的权限提升一下,提升到可以发表文章与编辑文章的作者权限,再来对注入漏洞进行复现。

enter image description here

首先发表一篇文章,并在该文下回复一条评论:

enter image description here

我们再来修改这条评论:

enter image description here

在文章编辑页面找到trash_wpnonce,将该评论所属的文章丢入垃圾箱:

enter image description here

再找到反删除的_wpnonce,从垃圾箱里反删除这篇文章:

enter image description here

查看SQL执行记录,发现已经注入成功,引入单引号:

enter image description here

最后,我来总结一下这个漏洞。

这个漏洞有两个核心点,一是利用一个竞争条件逻辑错误,造成的一个越权漏洞;二是利用一个三次操作,导致最后的SQL注入漏洞。

这两个核心技术点都是很有代表性的,通篇学习下来,不得不佩服洞主的思路和对wordpress的研究深度。

但我也很遗憾,没能分析出在最低权限下怎样去注入,主要还是_wpnonce的获取导致漏洞利用上出现了一些问题。

另外,该SQL注入有一个不得不说的鸡肋点,在污染数据第一次入库的时候,数据库中保存这个数据的字段只有20字符长度,所以导致最后我们的注入点是一个“存在长度限制”的注入点,对于这个长度限制的利用,我也暂时没有想出更好的方法。

虽然存在长度限制,但因为注入点出现在update语句的评论表中,所以通过这个漏洞,可以将一整个站的评论全部置为0,对于像Freebuf这类社交性质的网站来说危害还是巨大的。

所以,我对这个SQL注入的评价是:利用上(可能)比较鸡肋,但思路是绝对一流的,值得学习。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK