diff options
Diffstat (limited to 'meta/recipes-devtools/git/files/CVE-2023-22490-1.patch')
| -rw-r--r-- | meta/recipes-devtools/git/files/CVE-2023-22490-1.patch | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/meta/recipes-devtools/git/files/CVE-2023-22490-1.patch b/meta/recipes-devtools/git/files/CVE-2023-22490-1.patch new file mode 100644 index 0000000000..cc9b448c5c --- /dev/null +++ b/meta/recipes-devtools/git/files/CVE-2023-22490-1.patch | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | From 58325b93c5b6212697b088371809e9948fee8052 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Taylor Blau <me@ttaylorr.com> | ||
| 3 | Date: Tue, 24 Jan 2023 19:43:45 -0500 | ||
| 4 | Subject: [PATCH 1/3] t5619: demonstrate clone_local() with ambiguous transport | ||
| 5 | |||
| 6 | When cloning a repository, Git must determine (a) what transport | ||
| 7 | mechanism to use, and (b) whether or not the clone is local. | ||
| 8 | |||
| 9 | Since f38aa83 (use local cloning if insteadOf makes a local URL, | ||
| 10 | 2014-07-17), the latter check happens after the remote has been | ||
| 11 | initialized, and references the remote's URL instead of the local path. | ||
| 12 | This is done to make it possible for a `url.<base>.insteadOf` rule to | ||
| 13 | convert a remote URL into a local one, in which case the `clone_local()` | ||
| 14 | mechanism should be used. | ||
| 15 | |||
| 16 | However, with a specially crafted repository, Git can be tricked into | ||
| 17 | using a non-local transport while still setting `is_local` to "1" and | ||
| 18 | using the `clone_local()` optimization. The below test case | ||
| 19 | demonstrates such an instance, and shows that it can be used to include | ||
| 20 | arbitrary (known) paths in the working copy of a cloned repository on a | ||
| 21 | victim's machine[^1], even if local file clones are forbidden by | ||
| 22 | `protocol.file.allow`. | ||
| 23 | |||
| 24 | This happens in a few parts: | ||
| 25 | |||
| 26 | 1. We first call `get_repo_path()` to see if the remote is a local | ||
| 27 | path. If it is, we replace the repo name with its absolute path. | ||
| 28 | |||
| 29 | 2. We then call `transport_get()` on the repo name and decide how to | ||
| 30 | access it. If it was turned into an absolute path in the previous | ||
| 31 | step, then we should always treat it like a file. | ||
| 32 | |||
| 33 | 3. We use `get_repo_path()` again, and set `is_local` as appropriate. | ||
| 34 | But it's already too late to rewrite the repo name as an absolute | ||
| 35 | path, since we've already fed it to the transport code. | ||
| 36 | |||
| 37 | The attack works by including a submodule whose URL corresponds to a | ||
| 38 | path on disk. In the below example, the repository "sub" is reachable | ||
| 39 | via the dumb HTTP protocol at (something like): | ||
| 40 | |||
| 41 | http://127.0.0.1:NNNN/dumb/sub.git | ||
| 42 | |||
| 43 | However, the path "http:/127.0.0.1:NNNN/dumb" (that is, a top-level | ||
| 44 | directory called "http:", then nested directories "127.0.0.1:NNNN", and | ||
| 45 | "dumb") exists within the repository, too. | ||
| 46 | |||
| 47 | To determine this, it first picks the appropriate transport, which is | ||
| 48 | dumb HTTP. It then uses the remote's URL in order to determine whether | ||
| 49 | the repository exists locally on disk. However, the malicious repository | ||
| 50 | also contains an embedded stub repository which is the target of a | ||
| 51 | symbolic link at the local path corresponding to the "sub" repository on | ||
| 52 | disk (i.e., there is a symbolic link at "http:/127.0.0.1/dumb/sub.git", | ||
| 53 | pointing to the stub repository via ".git/modules/sub/../../../repo"). | ||
| 54 | |||
| 55 | This stub repository fools Git into thinking that a local repository | ||
| 56 | exists at that URL and thus can be cloned locally. The affected call is | ||
| 57 | in `get_repo_path()`, which in turn calls `get_repo_path_1()`, which | ||
| 58 | locates a valid repository at that target. | ||
| 59 | |||
| 60 | This then causes Git to set the `is_local` variable to "1", and in turn | ||
| 61 | instructs Git to clone the repository using its local clone optimization | ||
| 62 | via the `clone_local()` function. | ||
| 63 | |||
| 64 | The exploit comes into play because the stub repository's top-level | ||
| 65 | "$GIT_DIR/objects" directory is a symbolic link which can point to an | ||
| 66 | arbitrary path on the victim's machine. `clone_local()` resolves the | ||
| 67 | top-level "objects" directory through a `stat(2)` call, meaning that we | ||
| 68 | read through the symbolic link and copy or hardlink the directory | ||
| 69 | contents at the destination of the link. | ||
| 70 | |||
| 71 | In other words, we can get steps (1) and (3) to disagree by leveraging | ||
| 72 | the dangling symlink to pick a non-local transport in the first step, | ||
| 73 | and then set is_local to "1" in the third step when cloning with | ||
| 74 | `--separate-git-dir`, which makes the symlink non-dangling. | ||
| 75 | |||
| 76 | This can result in data-exfiltration on the victim's machine when | ||
| 77 | sensitive data is at a known path (e.g., "/home/$USER/.ssh"). | ||
| 78 | |||
| 79 | The appropriate fix is two-fold: | ||
| 80 | |||
| 81 | - Resolve the transport later on (to avoid using the local | ||
| 82 | clone optimization with a non-local transport). | ||
| 83 | |||
| 84 | - Avoid reading through the top-level "objects" directory when | ||
| 85 | (correctly) using the clone_local() optimization. | ||
| 86 | |||
| 87 | This patch merely demonstrates the issue. The following two patches will | ||
| 88 | implement each part of the above fix, respectively. | ||
| 89 | |||
| 90 | [^1]: Provided that any target directory does not contain symbolic | ||
| 91 | links, in which case the changes from 6f054f9 (builtin/clone.c: | ||
| 92 | disallow `--local` clones with symlinks, 2022-07-28) will abort the | ||
| 93 | clone. | ||
| 94 | |||
| 95 | Reported-by: yvvdwf <yvvdwf@gmail.com> | ||
| 96 | Signed-off-by: Taylor Blau <me@ttaylorr.com> | ||
| 97 | Signed-off-by: Junio C Hamano <gitster@pobox.com> | ||
| 98 | |||
| 99 | Upstream-Status: Backport | ||
| 100 | [https://github.com/git/git/commit/58325b93c5b6212697b088371809e9948fee8052] | ||
| 101 | CVE: CVE-2023-22490 | ||
| 102 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 103 | --- | ||
| 104 | t/t5619-clone-local-ambiguous-transport.sh | 63 ++++++++++++++++++++++ | ||
| 105 | 1 file changed, 63 insertions(+) | ||
| 106 | create mode 100644 t/t5619-clone-local-ambiguous-transport.sh | ||
| 107 | |||
| 108 | diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh | ||
| 109 | new file mode 100644 | ||
| 110 | index 0000000..7ebd31a | ||
| 111 | --- /dev/null | ||
| 112 | +++ b/t/t5619-clone-local-ambiguous-transport.sh | ||
| 113 | @@ -0,0 +1,63 @@ | ||
| 114 | +#!/bin/sh | ||
| 115 | + | ||
| 116 | +test_description='test local clone with ambiguous transport' | ||
| 117 | + | ||
| 118 | +. ./test-lib.sh | ||
| 119 | +. "$TEST_DIRECTORY/lib-httpd.sh" | ||
| 120 | + | ||
| 121 | +if ! test_have_prereq SYMLINKS | ||
| 122 | +then | ||
| 123 | + skip_all='skipping test, symlink support unavailable' | ||
| 124 | + test_done | ||
| 125 | +fi | ||
| 126 | + | ||
| 127 | +start_httpd | ||
| 128 | + | ||
| 129 | +REPO="$HTTPD_DOCUMENT_ROOT_PATH/sub.git" | ||
| 130 | +URI="$HTTPD_URL/dumb/sub.git" | ||
| 131 | + | ||
| 132 | +test_expect_success 'setup' ' | ||
| 133 | + mkdir -p sensitive && | ||
| 134 | + echo "secret" >sensitive/secret && | ||
| 135 | + | ||
| 136 | + git init --bare "$REPO" && | ||
| 137 | + test_commit_bulk -C "$REPO" --ref=main 1 && | ||
| 138 | + | ||
| 139 | + git -C "$REPO" update-ref HEAD main && | ||
| 140 | + git -C "$REPO" update-server-info && | ||
| 141 | + | ||
| 142 | + git init malicious && | ||
| 143 | + ( | ||
| 144 | + cd malicious && | ||
| 145 | + | ||
| 146 | + git submodule add "$URI" && | ||
| 147 | + | ||
| 148 | + mkdir -p repo/refs && | ||
| 149 | + touch repo/refs/.gitkeep && | ||
| 150 | + printf "ref: refs/heads/a" >repo/HEAD && | ||
| 151 | + ln -s "$(cd .. && pwd)/sensitive" repo/objects && | ||
| 152 | + | ||
| 153 | + mkdir -p "$HTTPD_URL/dumb" && | ||
| 154 | + ln -s "../../../.git/modules/sub/../../../repo/" "$URI" && | ||
| 155 | + | ||
| 156 | + git add . && | ||
| 157 | + git commit -m "initial commit" | ||
| 158 | + ) && | ||
| 159 | + | ||
| 160 | + # Delete all of the references in our malicious submodule to | ||
| 161 | + # avoid the client attempting to checkout any objects (which | ||
| 162 | + # will be missing, and thus will cause the clone to fail before | ||
| 163 | + # we can trigger the exploit). | ||
| 164 | + git -C "$REPO" for-each-ref --format="delete %(refname)" >in && | ||
| 165 | + git -C "$REPO" update-ref --stdin <in && | ||
| 166 | + git -C "$REPO" update-server-info | ||
| 167 | +' | ||
| 168 | + | ||
| 169 | +test_expect_failure 'ambiguous transport does not lead to arbitrary file-inclusion' ' | ||
| 170 | + git clone malicious clone && | ||
| 171 | + git -C clone submodule update --init && | ||
| 172 | + | ||
| 173 | + test_path_is_missing clone/.git/modules/sub/objects/secret | ||
| 174 | +' | ||
| 175 | + | ||
| 176 | +test_done | ||
| 177 | -- | ||
| 178 | 2.25.1 | ||
| 179 | |||
