首先来看一下哪些参数可控哪些不可控,以及各个参数在整个命令中的位置:
由于漏洞描述中说由于参数注入可以 造成命令执行,于是开始花了大量时间找类似于git ls-remote
命令的--upload-pack
参数;git grep
命令的--open-files-in-pager
参数进行漏洞注入的方式。但是看了很久git diff命令一直没找到合适的参数。看到的唯一带执行命令的参数是--ext-diff
Allow an external diff helper to be executed. If you set an external diff driver with gitattributes(5), you need to use this option with git-log(1) and friends.
然而尝试之后发现需要预先在.gitconfig
文件或者git全局配置中指定好外部diff命令的路径,于是这个漏洞就搁置了。
最近看到这篇文章中的分析,才恍然大悟。这里把我对这篇文章中利用方式的理解记录一下。
利用步骤如下:
第一步,这里使用--output=--
,通过在sinceId处注入--output
选项,指定diff结果导出到--
文件中,在工作目录下生成一个空的--
文件(后续用到);
第二步,无论 -- /etc/passwd
前面有0个、1个或者2个commit hash都会由于不可控参数--
的存在,而导致--
后面的文件被当作文件路径被解析,而且这个文件不能在工作区(working tree)之外。否则会提示:
而读取失败。
这里先生成一个--
文件,然后拼接出-- -- /etc/passwd
的参数列表。这里由于新插入的前面的--
的存在,将原本后面的--
参数当作一个文件看待;然后再通过注入--no-index
参数,拼接出
的命令(这个命令的含义是对两个文件--
和/etc/passwd
进行diff操作),利用其可以读取工作区(working tree)之外文件的特性,成功读取出工作区外的任意文件。
两个步骤模拟如下:
附录
git diff命令:
–output选项
–output=
Output to a specific file instead of stdout.
–no-index选项
You can omit the –no-index option when running the command in a working tree controlled by Git and at least one of the paths points outside the working tree, or when running the command outside a working tree controlled by Git.
官方热补丁
官方发布的hotfix的修复方式是拦截以--
开头的commitId然后响应400。
参考
- https://jira.atlassian.com/browse/BSERV-11947
- https://confluence.atlassian.com/bitbucketserver/bitbucket-server-security-advisory-2019-09-18-976762635.html
- https://mp.weixin.qq.com/s/8OSdYVTkv0J12ZKbLacITw
- https://git-scm.com/docs/git-diff
- https://mijingo.com/blog/what-is-the-working-tree-in-git