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仓库下的文件,
逃逸 --
, --
之后的部分不再包含 参数选项
git diff -C --color=never -U10000 --dst-prefix=dst:/usr/bin/git diff -C --color=never -U10000 --dst-prefix=dst:
这样可以输出 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.0
和 8.3.1
版本,对比反编译前后的更新代码。
代码变动不算太大,很快定位到 NioProcessParameters
和 NuProcessBuilder
关键修改之处。
领悟一下其修改逻辑,删除了参数和环境变量中的 \00
字符。
继续跟进关键库 Nuprocess
NuProcess
执行命令实现类,关注 LinuxProcess
实现
构造命令关键函数 prepareProcess
privatevoidprepareProcess(List<String> command, String[] environment, Path cwd)throws IOException { String[] cmdarray = (String[])command.toArray(new String[0]); byte[][] args = newbyte[cmdarray.length - 1][]; int size = args.length; for(int i = 0; i < args.length; ++i) { args[i] = cmdarray[i + 1].getBytes(); size += args[i].length; } 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, 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 访问即可造成任意命令执行。