The Git credential protocol is text-based over standard input/output, and consists of a series of lines of key-value pairs in the format key=value. Git's documentation restricts the use of the NUL (\0) character and newlines to form part of the keys[^1] or values.
When Git reads from standard input, it considers both LF and CRLF[^2] as newline characters for the credential protocol by virtue of calling <code>strbuf_getline</code> that calls to strbuf_getdelim_strip_crlf. Git also validates that a newline is not present in the value by checking for the presence of the line-feed character (LF, \n), and errors if this is the case. This captures both LF and CRLF-type newlines.
Git Credential Manager uses the .NET standard library <code>StreamReader</code> class to read the standard input stream line-by-line and parse the key=value credential protocol format. The implementation of the <code>ReadLineAsync</code> method considers LF, CRLF, and CR as valid line endings. This is means that .NET considers a single CR as a valid newline character, whereas Git does not.
This mismatch of newline treatment between Git and GCM means that an attacker can craft a malicious remote URL such as:
https://\rhost=targethost@badhost
..which will be interpreted by Git as:
protocol=https
host=badhost
username=\rhost=targethost
This will instead be parsed by GCM as if the following has been passed by Git:
protocol=https
host=badhost
username=
host=targethost
This results in the host field being resolved to the targethost value. GCM will then return a credential for targethost to Git, which will then send this credential to the badhost host.
When a user clones or otherwise interacts[^3] with a malicious repository that requires authentication, the attacker can capture credentials for another Git remote. The attack is also heightened when cloning from repositories with submodules when using the --recursive clone option as the user is not able to inspect the submodule remote URLs beforehand.
https://github.com/git-ecosystem/git-credential-manager/compare/749e287571c78a2b61f926ccce6a707050871ab8...99e2f7f60e7364fe807e7925f361a81f3c47bd1b
Only interacting with trusted remote repositories, and do not clone with --recursive to allow inspection of any submodule URLs before cloning those submodules.
This issue is fixed as of version 2.6.1.
{
"github_reviewed_at": "2025-01-14T19:40:54Z",
"severity": "HIGH",
"github_reviewed": true,
"cwe_ids": [
"CWE-200",
"CWE-436"
],
"nvd_published_at": "2025-01-14T19:15:31Z"
}