Bitbucket Server CVE-2022-36804 漏洞分析

8 月 24 日,Atlassian 发布安全公告,披露了 Bitbucket Server 和 Data Center 在 7.0.0 版中引入了一个严重安全漏洞。

Bitbucket 是 Atlassian 公司提供的一个基于 web 的版本库托管服务,支持 Mercurial 和 Git 版本控制系统。支持私有化部署,根据国内某资产测绘平台数据显示,近一年全球有超过 1w+ 相关服务对外开放。

官方漏洞公告中描述 Bitbucket Server 和 Data Center 多个 API 端点存在命令注入漏洞,漏洞触发条件是攻击者具备公开项目的访问权限或者私有项目的可读权限,影响版本从 7.0 到 8.3 , 官方自评是 CVSS 9.9 分。


历史漏洞回顾

猜测可能是一个 git参数注入漏洞,往前翻了翻历史上 bitbucket 出现过的 git参数注入漏洞, 可以熟悉一下相关的系统框架。

CVE-2019-15000 分析

这是一个很有意思的参数注入,可以模拟如下指令:

/usr/bin/git diff -C --color=never -U10000 --dst-prefix=dst:// --src-prefix=src:// <可控sinceId><可控untilId> -- <可控待diff文件名>

使用参数 --no-index 时,可以指定不在目录git仓库下的文件,

逃逸 ---- 之后的部分不再包含 参数选项

// 使用output 生成一个 -- 文件git diff -C --color=never -U10000 --dst-prefix=dst:// --src-prefix=src:// --output=-- edc5e66fd8789e07fbcd45ffcd5284e0ba1b426e  -- README.md// 第一个可控 -- 为选项,第二个 -- 为文件,/usr/bin/git diff -C --color=never -U10000 --dst-prefix=dst:// --src-prefix=src:// --no-index -- -- /etc/passwd

这样可以输出 diff 结果,达成任意文件读,如果 README可控(项目写入权限),也可以控制 output 达成任意文件写,CVE-2019-20097 中使用 post-receive 方式升级利用从任意文件写到 RCE。

CVE-2019-15010 分析

CVE-2019-15000 的修复是对 commitId 进行检测,对用户输入出现 -- 开头进行阻止。

CVE-2019-15010 的exp 使用 %0a 作为开头绕过了 CVE-2019-15000 的补丁,利用方式和 CVE-2019-15000 相同。

CVE-2019-15010 补丁中,对 commitId 进行了更为严苛的检测。


CVE-2022-36804 补丁diff

回归到本次关注漏洞的补丁,在官方通告中并没有针对性地给出单个 jar 包的热补丁,使用 8.3.08.3.1 版本,对比反编译前后的更新代码。

代码变动不算太大,很快定位到 NioProcessParametersNuProcessBuilder 关键修改之处。

领悟一下其修改逻辑,删除了参数和环境变量中的 \00 字符。

继续跟进关键库 Nuprocess

NuProcess 执行命令实现类,关注 LinuxProcess 实现

构造命令关键函数 prepareProcess

privatevoidprepareProcess(List command, String[] environment, Path cwd)throws IOException {     // 需要执行 git 命令数组        String[] cmdarray = (String[])command.toArray(new String[0]);        byte[][] args = newbyte[cmdarray.length - 1][];        int size = args.length;    // 取 git 命令数组参数,第0位之后,存储到 args[][]for(int i = 0; i < args.length; ++i) {            args[i] = cmdarray[i + 1].getBytes();            size += args[i].length;        }    // 最终存储参数的 byte数组 argBlock byte[] argBlock = newbyte[size];        int i = 0;        byte[][] var9 = args;        int var10 = args.length;        for(int var11 = 0; var11 < var10; ++var11) {            byte[] arg = var9[var11];        // 使用 system.arraycopy 将 arg[][] 二维数组拷贝到 argBlock            System.arraycopy(arg, 0, argBlock, i, arg.length);            i += arg.length + 1;        }        byte[] envBlock = toEnvironmentBlock(environment);        this.createPipes();        try {            int[] child_fds = newint[]{this.stdinWidow, this.stdoutWidow, this.stderrWidow};            if (Constants.JVM_MAJOR_VERSION >= 10) {                this.pid = LibJava10.Java_java_lang_ProcessImpl_forkAndExec(JNIEnv.CURRENT, this, LinuxProcess.LaunchMechanism.VFORK.ordinal() + 1, toCString(System.getProperty("java.home") + "/lib/jspawnhelper"), toCString(cmdarray[0]), argBlock, args.length, envBlock, environment.length, cwd != null ? toCString(cwd.toString()) : null, child_fds, (byte)0);            } else {                this.pid = LibJava8.Java_java_lang_UNIXProcess_forkAndExec(JNIEnv.CURRENT, this, LinuxProcess.LaunchMechanism.VFORK.ordinal() + 1, toCString(System.getProperty("java.home") + "/lib/jspawnhelper"), toCString(cmdarray[0]), argBlock, args.length, envBlock, environment.length, cwd != null ? toCString(cwd.toString()) : null, child_fds, (byte)0);            }        } finally {            this.closePipes();        }    }

注意在 执行 数组拷贝时,目的地址是在当前参数之后 +1

可以看出,参数之间使用 0 来分割,那么答案呼之欲出,在 Linux 环境下,可以 \00 来实现参数注入。


EXP 构造

git diff 任意文件写

利用历史漏洞思路 git diff 在有项目内容控制权限的情况下 达成任意文件写

参数注入代码执行

官方披露的漏洞效果是仅在有只读权限情况下可以进行命令执行,通过枚举应用所有只读权限情况下可以构造的 git 指令,找到一处进行参数注入,构造恶意 url 访问即可造成任意命令执行。


国内免备案VPS301跳转服务器国内免备案服务器域名被墙跳转301,绕过信息安全中心不放违反法律法规内容!(北京免备案 镇江免备案 江苏免备案 辽宁免备案vps 山东联通免备案
 
在线咨询