CVE-2020-5260: Insufficiently Protected Credentials (CWE-522)
value: Affected versions of Git have a vulnerability whereby Git can be tricked into sending private credentials to a host controlled by an attacker. Git uses external "credential helper" programs to store and retrieve passwords or other credentials from secure storage provided by the operating system. Specially-crafted URLs that contain an encoded newline can inject unintended values into the credential helper protocol stream, causing the credential helper to retrieve the password for one server (e.g., good.example.com) for an HTTP request being made to another server (e.g., evil.example.com), resulting in credentials for the former being sent to the latter. There are no restrictions on the relationship between the two, meaning that an attacker can craft a URL that will present stored credentials for any host to a host of their choosing. The vulnerability can be triggered by feeding a malicious URL to git clone. However, the affected URLs look rather suspicious; the likely vector would be through systems which automatically clone URLs not visible to the user, such as Git submodules, or package systems built around Git. The problem has been patched in the versions published on April 14th, 2020, going back to v2.17.x. Anyone wishing to backport the change further can do so by applying commit 9a6bbee (the full release includes extra checks for git fsck, but that commit is sufficient to protect clients against the vulnerability). The patched versions are: 2.17.4, 2.18.3, 2.19.4, 2.20.3, 2.21.2, 2.22.3, 2.23.2, 2.24.2, 2.25.3, 2.26.1.
CWE description: The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.
CVSS v3 base score: 7.5
Published: 2020-04-14T23:15Z
Repository: https://github.com/git/git
Language: C
Files changed: credential.c, t/t0300-credentials.sh
Commit date: 2020-03-12 02:55:16 -0400
Commit message: credential: avoid writing values with newlines

The credential protocol that we use to speak to helpers can't represent
values with newlines in them. This was an intentional design choice to
keep the protocol simple, since none of the values we pass should
generally have newlines.

However, if we _do_ encounter a newline in a value, we blindly transmit
it in credential_write(). Such values may break the protocol syntax, or
worse, inject new valid lines into the protocol stream.

The most likely way for a newline to end up in a credential struct is by
decoding a URL with a percent-encoded newline. However, since the bug
occurs at the moment we write the value to the protocol, we'll catch it
there. That should leave no possibility of accidentally missing a code
path that can trigger the problem.

At this level of the code we have little choice but to die(). However,
since we'd not ever expect to see this case outside of a malicious URL,
that's an acceptable outcome.

Reported-by: Felix Wilhelm <fwilhelm@google.com>

Diff: diff --git a/credential.c b/credential.c
index 9747f47b18b..00ee4d62db1 100644
--- a/credential.c
+++ b/credential.c
@@ -192,10 +192,12 @@ int credential_read(struct credential *c, FILE *fp)
 
 static void credential_write_item(FILE *fp, const char *key, const char *value)
 {
 	if (!value)
 		return;
+	if (strchr(value, '\n'))
+		die("credential value for %s contains newline", key);
 	fprintf(fp, "%s=%s\n", key, value);
 }
 
 void credential_write(const struct credential *c, FILE *fp)
 {
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index 03bd31e9f22..15cc3c5abb5 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -307,6 +307,12 @@ test_expect_success 'empty helper spec resets helper list' '
 	--
 	verbatim: get
 	EOF
 '
 
+test_expect_success 'url parser rejects embedded newlines' '
+	test_must_fail git credential fill <<-\EOF
+	url=https://one.example.com?%0ahost=two.example.com/
+	EOF
+'
+
 test_done

Fixed code: 	if (strchr(value, '\n'))
		die("credential value for %s contains newline", key);
test_expect_success 'url parser rejects embedded newlines' '
	test_must_fail git credential fill <<-\EOF
	url=https://one.example.com?%0ahost=two.example.com/
	EOF
'