CVE-2020-11890 漏洞分析


README.md

前言

网上关于CVE-2020-11890 的复现文章有很多,但是大多都是给了一个脚本,docker pull一下,然后攻击就算是复现。现如今,我已经对脚本小子这个标签有着很深的怨念,所以并不想只是单纯的拿脚本EXP攻击、结束这样,而是真正理解攻击手法,学到一些有价值的东西。


环境搭建

1.Vmware bugku靶场镜像

2.python3环境


指令:

docker pull hoangkien1020/joomla:3.9.16
docker run -d --rm -it -p 8080:80 hoangkien1020/joomla:3.9.16

5f4eecf12cdb2.png

得到虚拟机IP:192.168.247.128

于是可以在本机打开192.168.247.128:8080,就可以访问漏洞场景。

漏洞分析

分析肯定是要看源码的。docker中,查看容器相关信息的指令为如下。

docker inspect  容器id(可只选前几位,不用全部输入)

结果如下:

5f4eee5bb09bf.png

找到如图所示的路径,大概率就是文件路径,但是我没有查阅过资料三种文件夹的属性,我都是直接一个个尝试

5f4eef4e21ff2.png

/var/lib/docker/overlay2/4db83ff0b3b3d18da4cc5a8e64a0bb74d7f2965d78ae6dbec3a050c45ff47548/merged

然后进入var/www/html 即可看到源码(有时候源码存在于opt或者root目录下,不会花费很多时间,动手翻一下就行)


由于这个只是个鸡肋的漏洞,是可以将低权限用户提升为高级管理员。

那么与之相关的一定是对于roleid等类似参数的越权修改,与权限检查规避。从数据库中获取数据结构,在usergroups表中存在以下字段。

id parent_id lft rgt  title    其中super user的 id为8 administrator的id为7.parent id代表他的亲节点。

5f4ef535ad5f2.png

根据两个参数可以画出以下权限关系

5f4ef6a792623.png

这里另外两个参数 lft 和rft。根据思考后找到规律,如果当前记录的 rft-lft == 1 则代表其下没有子节点(参考publisher和super user)。相反,rft-lft如果大于0,则代表存在子节点。且子节点的个数为((rft-lft)-1)/2

全局搜索源代码,找到修改权限为super user的相关代码

5f4ef2cf7dccd.png

可见,只要checkgroup为admin,则当前管理员账号是administrator,则直接抛出异常结束程序,导致无法修改。

于是进入checkgroup方法


5f4ef7b98b452.png

继续查看getgrouppath

5f4ef84ad99c2.png

方法的作用就是,根据获取的groupid,返回用户组groups列表的path属性。

那么这个path属性是如何产生的,就需要去查看初始化groups列表时候的一系列方法了。

 5f4ef91ecc3b1.png


此方法对每个用户组数据进行填充,path属性即为该用户组的祖先数组。level为层次数。

以pulic用户组为例,它非常特殊因为在数据库中他的parentid为0,故进入分支使得他的层次为0,他的path为自己的id1。

动态调试后,可得path为array(0 => '1'), level为0。

以guest为例,动态调试,Guest节点的path为array (0 => '1',1 =>'9',),level为1。同理,Administrator用户组节点的祖先数组path为array (0 => '1', 1 =>'6', 2 => '7',),level为2。


回到checkgroup方法,  我们肯定是想设置他的id为8,成为superuser,于是会返回path,为array (0 => '1',1 =>'8',)。这里getassetrules方法,会去访问获取当前用户不可访问的的用户组id。这在libraries\src\Access\Rule.php的allow方法中。

5f4efbaec37ac.png


$this->data数组代表目前用户不可以访问的节点id。我们使用的是administrator用户账号,通过此方法,可得,不可以操作的用户组节点id为8,即super user,因此$this->data数组值为array (8 ->1),而这个数组值存在于祖先数组中(path的获取),导致程序错误,抛出异常说我不是超级管理员的错误。


而漏洞的利用,通过poc可知,他将public的双亲节点设置了一个很大的数字,而不是单纯的0。

为什么administrator可以操作public?上面已经解释了,通过rule方法返回,只是返回了不可操作的用户数组为array (8 ->1),没有public相关的array(0 => '1')。

public的双亲节点由0修改为poc中的任意数字后,程序在处理Public用户组时,不再进入if ($parentId ===0)的判断分支,但id为其他数字(足够大)对应的parentGroup并不存在,使得$parentGroup->path值为null。

之后,array_merge将null和数组进行拼接,使得程序产生错误(array_merge首个参数不能为null),导致$group->path也变成null,最后由 count($group->path) – 1把level值变为-1。

5f4efdc787b15.png

根节点Public的path为null,使得所有后续节点的path都是基于其双亲节点的path值array_merge计算而来,所以所有节点的祖先数组全都为null。回到rule方法,在检查当前用户无法访问的节点是否在path中,所有节点的path都为null,所有无法将祖先数组节点值与无法访问节点列表中的值进行比较,从而顺利bypass检查,导致权限提升。


所以,简单的来说,这个漏洞的产生是因为没有对于public权限进行防护,仅仅对于superuser有保护。进而修改双亲节点,导致判断错乱,使得逃避了检查权限提升。


后记

利用地址:https://github.com/HoangKien1020/CVE-2020-11890

About

阅读前,请务必阅读《阅读指南》

Notice

接各类后端开发业务,老板可联系~

Contact

邮箱:xb@whitecap100.org