From 9de0ee114f7bef6e991eb904d37418116768b292 Mon Sep 17 00:00:00 2001 From: fly_fzc <2385803914@qq.com> Date: Wed, 9 Jul 2025 17:11:28 +0800 Subject: [PATCH] Fix CVE-2025-48384 CVE-2025-48385 CVE-2025-48386 (cherry picked from commit 52178c06a0fa675662da242043740dadca44c7e8) --- ...quote-values-containing-CR-character.patch | 123 ++++++++++++++ ...rbitrary-file-writes-via-parameter-i.patch | 160 ++++++++++++++++++ ...red-avoid-buffer-overflow-in-wcsncat.patch | 93 ++++++++++ git.spec | 18 +- 4 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2025-48384-config-quote-values-containing-CR-character.patch create mode 100644 backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch create mode 100644 backport-CVE-2025-48386-wincred-avoid-buffer-overflow-in-wcsncat.patch diff --git a/backport-CVE-2025-48384-config-quote-values-containing-CR-character.patch b/backport-CVE-2025-48384-config-quote-values-containing-CR-character.patch new file mode 100644 index 0000000..5f7641d --- /dev/null +++ b/backport-CVE-2025-48384-config-quote-values-containing-CR-character.patch @@ -0,0 +1,123 @@ +From 05e9cd64ee23bbadcea6bcffd6660ed02b8eab89 Mon Sep 17 00:00:00 2001 +From: Justin Tobler +Date: Mon, 19 May 2025 21:26:04 -0500 +Subject: [PATCH] config: quote values containing CR character + +When reading the config, values that contain a trailing CRLF are +stripped. If the value itself has a trailing CR, the normal LF that +follows results in the CR being unintentionally stripped. This may lead +to unintended behavior due to the config value written being different +when it gets read. + +One such issue involves a repository with a submodule path containing a +trailing CR. When the submodule gets initialized, the submodule is +cloned without being checked out and has "core.worktree" set to the +submodule path. The git-checkout(1) that gets spawned later reads the +"core.worktree" config value, but without the trailing CR, and +consequently attempts to checkout to a different path than intended. + +If the repository contains a matching path that is a symlink, it is +possible for the submodule repository to be checked out in arbitrary +locations. This is extra bad when the symlink points to the submodule +hooks directory and the submodule repository contains an executable +"post-checkout" hook. Once the submodule repository checkout completes, +the "post-checkout" hook immediately executes. + +To prevent mismatched config state due to misinterpreting a trailing CR, +wrap config values containing CR in double quotes when writing the +entry. This ensures a trailing CR is always separated for an LF and thus +prevented from getting stripped. + +Note that this problem cannot be addressed by just quoting each CR with +"\r". The reading side of the config interprets only a few backslash +escapes, and "\r" is not among them. This fix is sufficient though +because it only affects the CR at the end of a line and any literal CR +in the interior is already preserved. + +Co-authored-by: David Leadbeater +Signed-off-by: Justin Tobler +Signed-off-by: Taylor Blau +--- + config.c | 2 +- + t/t1300-config.sh | 11 +++++++++++ + t/t7450-bad-git-dotfiles.sh | 33 +++++++++++++++++++++++++++++++++ + 3 files changed, 45 insertions(+), 1 deletion(-) + +diff --git a/config.c b/config.c +index 9ff6ae1cb9..629981451d 100644 +--- a/config.c ++++ b/config.c +@@ -2999,7 +2999,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value, + if (value[0] == ' ') + quote = "\""; + for (i = 0; value[i]; i++) +- if (value[i] == ';' || value[i] == '#') ++ if (value[i] == ';' || value[i] == '#' || value[i] == '\r') + quote = "\""; + if (i && value[i - 1] == ' ') + quote = "\""; +diff --git a/t/t1300-config.sh b/t/t1300-config.sh +index f4e2752134..1010410b7e 100755 +--- a/t/t1300-config.sh ++++ b/t/t1300-config.sh +@@ -2590,4 +2590,15 @@ test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such included files' ' + grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err + ' + ++test_expect_success 'writing value with trailing CR not stripped on read' ' ++ test_when_finished "rm -rf cr-test" && ++ ++ printf "bar\r\n" >expect && ++ git init cr-test && ++ git -C cr-test config --add core.foo $(printf "bar\r") && ++ git -C cr-test config --get core.foo >actual && ++ ++ test_cmp expect actual ++' ++ + test_done +diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh +index 5b845e899b..2026285566 100755 +--- a/t/t7450-bad-git-dotfiles.sh ++++ b/t/t7450-bad-git-dotfiles.sh +@@ -347,4 +347,37 @@ test_expect_success 'git dirs of sibling submodules must not be nested' ' + test_grep "is inside git dir" err + ' + ++test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into different directory' ' ++ test_when_finished "rm -rf sub repo bad-clone" && ++ ++ git init sub && ++ write_script sub/post-checkout <<-\EOF && ++ touch "$PWD/foo" ++ EOF ++ git -C sub add post-checkout && ++ git -C sub commit -m hook && ++ ++ git init repo && ++ git -C repo -c protocol.file.allow=always submodule add "$PWD/sub" sub && ++ git -C repo mv sub $(printf "sub\r") && ++ ++ # Ensure config values containing CR are wrapped in quotes. ++ git config --unset -f repo/.gitmodules submodule.sub.path && ++ printf "\tpath = \"sub\r\"\n" >>repo/.gitmodules && ++ ++ git config --unset -f repo/.git/modules/sub/config core.worktree && ++ { ++ printf "[core]\n" && ++ printf "\tworktree = \"../../../sub\r\"\n" ++ } >>repo/.git/modules/sub/config && ++ ++ ln -s .git/modules/sub/hooks repo/sub && ++ git -C repo add -A && ++ git -C repo commit -m submodule && ++ ++ git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone && ++ ! test -f "$PWD/foo" && ++ test -f $(printf "bad-clone/sub\r/post-checkout") ++' ++ + test_done +-- +2.33.0 + diff --git a/backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch b/backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch new file mode 100644 index 0000000..60afc44 --- /dev/null +++ b/backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch @@ -0,0 +1,160 @@ +From 35cb1bb0b92c132249d932c05bbd860d410e12d4 Mon Sep 17 00:00:00 2001 +From: Patrick Steinhardt' via Git Security +Date: Wed, 14 May 2025 08:32:02 +0200 +Subject: [PATCH] bundle-uri: fix arbitrary file writes via parameter injection + +We fetch bundle URIs via `download_https_uri_to_file()`. The logic to +fetch those bundles is not handled in-process, but we instead use a +separate git-remote-https(1) process that performs the fetch for us. The +information about which file should be downloaded and where that file +should be put gets communicated via stdin of that process via a "get" +request. This "get" request has the form "get $uri $file\n\n". As may be +obvious to the reader, this will cause git-remote-https(1) to download +the URI "$uri" and put it into "$file". + +The fact that we are using plain spaces and newlines as separators for +the request arguments means that we have to be extra careful with the +respective vaules of these arguments: + + - If "$uri" contained a space we would interpret this as both URI and + target location. + + - If either "$uri" or "$file" contained a newline we would interpret + this as a new command. + +But we neither quote the arguments such that any characters with special +meaning would be escaped, nor do we verify that none of these special +characters are contained. + +If either the URI or file contains a newline character, we are open to +protocol injection attacks. Likewise, if the URI itself contains a +space, then an attacker-controlled URI can lead to partially-controlled +file writes. + +Note that the attacker-controlled URIs do not permit completely +arbitrary file writes, but instead allows an attacker to control the +path in which we will write a temporary (e.g., "tmp_uri_XXXXXX") +file. + +The result is twofold: + + - By adding a space in "$uri" we can control where exactly a file will + be written to, including out-of-repository writes. The final + location is not completely arbitrary, as the injected string will be + concatenated with the original "$file" path. Furthermore, the name + of the bundle will be "tmp_uri_XXXXXX", further restricting what an + adversary would be able to write. + + Also note that is not possible for the URI to contain a newline + because we end up in `credential_from_url_1()` before we try to + issue any requests using that URI. As such, it is not possible to + inject arbitrary commands via the URI. + + - By adding a newline to "$file" we can inject arbitrary commands. + This gives us full control over where a specific file will be + written to. Potential attack vectors would be to overwrite hooks, + but if an adversary were to guess where the user's home directory is + located they might also easily write e.g. a "~/.profile" file and + thus cause arbitrary code execution. + + This injection can only become possible when the adversary has full + control over the target path where a bundle will be downloaded to. + While this feels unlikely, it is possible to control this path when + users perform a recursive clone with a ".gitmodules" file that is + controlled by the adversary. + +Luckily though, the use of bundle URIs is not enabled by default in Git +clients (yet): they have to be enabled by setting the `bundle.heuristic` +config key explicitly. As such, the blast radius of this parameter +injection should overall be quite contained. + +Fix the issue by rejecting spaces in the URI and newlines in both the +URI and the file. As explained, it shouldn't be required to also +restrict the use of newlines in the URI, as we would eventually die +anyway in `credential_from_url_1()`. But given that we're only one small +step away from arbitrary code execution, let's rather be safe and +restrict newlines in URIs, as well. + +Eventually we should probably refactor the way that Git talks with the +git-remote-https(1) subprocess so that it is less fragile. Until then, +these two restrictions should plug the issue. + +Reported-by: David Leadbeater +Based-on-patch-by: David Leadbeater +Signed-off-by: Patrick Steinhardt +Signed-off-by: Taylor Blau +--- + bundle-uri.c | 22 ++++++++++++++++++++++ + t/t5558-clone-bundle-uri.sh | 23 +++++++++++++++++++++++ + 2 files changed, 45 insertions(+) + +diff --git a/bundle-uri.c b/bundle-uri.c +index ca32050a78..a6a3c1c4b3 100644 +--- a/bundle-uri.c ++++ b/bundle-uri.c +@@ -292,6 +292,28 @@ static int download_https_uri_to_file(const char *file, const char *uri) + struct strbuf line = STRBUF_INIT; + int found_get = 0; + ++ /* ++ * The protocol we speak with git-remote-https(1) uses a space to ++ * separate between URI and file, so the URI itself must not contain a ++ * space. If it did, an adversary could change the location where the ++ * downloaded file is being written to. ++ * ++ * Similarly, we use newlines to separate commands from one another. ++ * Consequently, neither the URI nor the file must contain a newline or ++ * otherwise an adversary could inject arbitrary commands. ++ * ++ * TODO: Restricting newlines in the target paths may break valid ++ * usecases, even if those are a bit more on the esoteric side. ++ * If this ever becomes a problem we should probably think about ++ * alternatives. One alternative could be to use NUL-delimited ++ * requests in git-remote-http(1). Another alternative could be ++ * to use URL quoting. ++ */ ++ if (strpbrk(uri, " \n")) ++ return error("bundle-uri: URI is malformed: '%s'", file); ++ if (strchr(file, '\n')) ++ return error("bundle-uri: filename is malformed: '%s'", file); ++ + strvec_pushl(&cp.args, "git-remote-https", uri, NULL); + cp.err = -1; + cp.in = -1; +diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh +index 996a08e90c..2af523aaa4 100755 +--- a/t/t5558-clone-bundle-uri.sh ++++ b/t/t5558-clone-bundle-uri.sh +@@ -1052,6 +1052,29 @@ test_expect_success 'bundles are downloaded once during fetch --all' ' + trace-mult.txt >bundle-fetches && + test_line_count = 1 bundle-fetches + ' ++ ++test_expect_success 'bundles with space in URI are rejected' ' ++ test_when_finished "rm -rf busted repo" && ++ mkdir -p "$HOME/busted/ /$HOME/repo/.git/objects/bundles" && ++ git clone --bundle-uri="$HTTPD_URL/bogus $HOME/busted/" "$HTTPD_URL/smart/fetch.git" repo 2>err && ++ test_grep "error: bundle-uri: URI is malformed: " err && ++ find busted -type f >files && ++ test_must_be_empty files ++' ++ ++test_expect_success 'bundles with newline in URI are rejected' ' ++ test_when_finished "rm -rf busted repo" && ++ git clone --bundle-uri="$HTTPD_URL/bogus\nget $HTTPD_URL/bogus $HOME/busted" "$HTTPD_URL/smart/fetch.git" repo 2>err && ++ test_grep "error: bundle-uri: URI is malformed: " err && ++ test_path_is_missing "$HOME/busted" ++' ++ ++test_expect_success 'bundles with newline in target path are rejected' ' ++ git clone --bundle-uri="$HTTPD_URL/bogus" "$HTTPD_URL/smart/fetch.git" "$(printf "escape\nget $HTTPD_URL/bogus .")" 2>err && ++ test_grep "error: bundle-uri: filename is malformed: " err && ++ test_path_is_missing escape ++' ++ + # Do not add tests here unless they use the HTTP server, as they will + # not run unless the HTTP dependencies exist. + +-- +2.33.0 + diff --git a/backport-CVE-2025-48386-wincred-avoid-buffer-overflow-in-wcsncat.patch b/backport-CVE-2025-48386-wincred-avoid-buffer-overflow-in-wcsncat.patch new file mode 100644 index 0000000..8e993ae --- /dev/null +++ b/backport-CVE-2025-48386-wincred-avoid-buffer-overflow-in-wcsncat.patch @@ -0,0 +1,93 @@ +From 9de345cb273cc7faaeda279c7e07149d8a15a319 Mon Sep 17 00:00:00 2001 +From: Taylor Blau +Date: Mon, 19 May 2025 18:30:29 -0400 +Subject: [PATCH] wincred: avoid buffer overflow in wcsncat() + +The wincred credential helper uses a static buffer ("target") as a +unique key for storing and comparing against internal storage. It does +this by building up a string is supposed to look like: + + git:$PROTOCOL://$USERNAME@$HOST/@PATH + +However, the static "target" buffer is declared as a wide string with no +more than 1,024 wide characters. The first call to wcsncat() is almost +correct (it copies no more than ARRAY_SIZE(target) wchar_t's), but does +not account for the trailing NUL, introducing an off-by-one error. + +But subsequent calls to wcsncat() have an additional problem on top of +the off-by-one. They do not account for the length of the existing +wide string being built up in 'target'. So the following: + + $ perl -e ' + my $x = "x" x 1_000; + print "protocol=$x\nhost=$x\nusername=$x\npath=$x\n" + ' | + C\:/Program\ Files/Git/mingw64/libexec/git-core/git-credential-wincred.exe get + +will result in a segmentation fault from over-filling buffer. + +This bug is as old as the wincred helper itself, dating back to +a6253da0f3 (contrib: add win32 credential-helper, 2012-07-27). Commit +8b2d219a3d (wincred: improve compatibility with windows versions, +2013-01-10) replaced the use of strncat() with wcsncat(), but retained +the buggy behavior. + +Fix this by using a "target_append()" helper which accounts for both the +length of the existing string within the buffer, as well as the trailing +NUL character. + +Reported-by: David Leadbeater +Helped-by: David Leadbeater +Helped-by: Jeff King +Signed-off-by: Taylor Blau +--- + .../wincred/git-credential-wincred.c | 22 +++++++++++++------ + 1 file changed, 15 insertions(+), 7 deletions(-) + +diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c +index 4cd56c42e2..ceff44207a 100644 +--- a/contrib/credential/wincred/git-credential-wincred.c ++++ b/contrib/credential/wincred/git-credential-wincred.c +@@ -37,6 +37,14 @@ static void *xmalloc(size_t size) + static WCHAR *wusername, *password, *protocol, *host, *path, target[1024], + *password_expiry_utc; + ++static void target_append(const WCHAR *src) ++{ ++ size_t avail = ARRAY_SIZE(target) - wcslen(target) - 1; /* -1 for NUL */ ++ if (avail < wcslen(src)) ++ die("target buffer overflow"); ++ wcsncat(target, src, avail); ++} ++ + static void write_item(const char *what, LPCWSTR wbuf, int wlen) + { + char *buf; +@@ -294,17 +302,17 @@ int main(int argc, char *argv[]) + + /* prepare 'target', the unique key for the credential */ + wcscpy(target, L"git:"); +- wcsncat(target, protocol, ARRAY_SIZE(target)); +- wcsncat(target, L"://", ARRAY_SIZE(target)); ++ target_append(protocol); ++ target_append(L"://"); + if (wusername) { +- wcsncat(target, wusername, ARRAY_SIZE(target)); +- wcsncat(target, L"@", ARRAY_SIZE(target)); ++ target_append(wusername); ++ target_append(L"@"); + } + if (host) +- wcsncat(target, host, ARRAY_SIZE(target)); ++ target_append(host); + if (path) { +- wcsncat(target, L"/", ARRAY_SIZE(target)); +- wcsncat(target, path, ARRAY_SIZE(target)); ++ target_append(L"/"); ++ target_append(path); + } + + if (!strcmp(argv[1], "get")) +-- +2.33.0 + diff --git a/git.spec b/git.spec index 1d43dfc..dd5dcd7 100644 --- a/git.spec +++ b/git.spec @@ -1,7 +1,7 @@ %global gitexecdir %{_libexecdir}/git-core Name: git Version: 2.43.0 -Release: 6 +Release: 8 Summary: A popular and widely used Version Control System License: GPLv2+ or LGPLv2.1 URL: https://git-scm.com/ @@ -24,6 +24,9 @@ Patch8: backport-CVE-2024-50349-credential_format-also-encode-host-port.patc Patch9: backport-CVE-2024-50349-credential-sanitize-the-user-prompt.patch Patch10: backport-CVE-2024-52006-credential-disallow-Carriage-Returns-in-the-protocol.patch Patch11: backport-CVE-2024-52005.patch +Patch12: backport-CVE-2025-48384-config-quote-values-containing-CR-character.patch +Patch13: backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch +Patch14: backport-CVE-2025-48386-wincred-avoid-buffer-overflow-in-wcsncat.patch BuildRequires: gcc gettext BuildRequires: openssl-devel libcurl-devel expat-devel systemd asciidoc xmlto glib2-devel libsecret-devel pcre2-devel desktop-file-utils @@ -308,6 +311,19 @@ make %{?_smp_mflags} test %{_mandir}/man7/git*.7.* %changelog +* Wed Jul 9 2025 fuanan - 2.43.0-8 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:fix malformed patch: + backport-CVE-2025-48385-bundle-uri-fix-arbitrary-file-writes-via-parameter-i.patch + +* Wed Jul 9 2025 fuanan - 2.43.0-7 +- Type:CVE +- ID:CVE-2025-48384 CVE-2025-48385 CVE-2025-48386 +- SUG:NA +- DESC:Fix CVE-2025-48384 CVE-2025-48385 CVE-2025-48386 + * Fri Jan 17 2025 fuanan - 2.43.0-6 - Type:CVE - ID:CVE-2024-52005 -- Gitee