@matheusbsbs / matheus-bagliano / commits / 1a3609e402
Fork 0

fsck: reject URL with empty host in .gitmodules

Git's URL parser interprets https:///example.com/repo.git to have no host and a path of "example.com/repo.git". Curl, on the other hand, internally redirects it to https://example.com/repo.git. As a result, until "credential: parse URL without host as empty host, not unset", tricking a user into fetching from such a URL would cause Git to send credentials for another host to example.com. Teach fsck to block and detect .gitmodules files using such a URL to prevent sharing them with Git versions that are not yet protected. A relative URL in a .gitmodules file could also be used to trigger this. The relative URL resolver used for .gitmodules does not normalize sequences of slashes and can follow ".." components out of the path part and to the host part of a URL, meaning that such a relative URL can be used to traverse from a https://foo.example.com/innocent superproject to a https:///attacker.example.com/exploit submodule. Fortunately, redundant extra slashes in .gitmodules are rare, so we can catch this by detecting one after a leading sequence of "./" and "../" components. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Reviewed-by: Jeff King <peff@peff.net>

Jonathan Nieder committed Apr 18, 2020 at 20:57 UTC 1a3609e402a062ef7b11f197fe96c28cabca132c
2 files changed +39 -3
fsck.c
+7 -3
@@ -1064,17 +1064,21 @@ static int check_submodule_url(const char *url)
1064 /*
1065 * URLs which escape their root via "../" can overwrite
1066 * the host field and previous components, resolving to
1067 - * URLs like https::example.com/submodule.git that were
1067 + * URLs like https::example.com/submodule.git and
1068 + * https:///example.com/submodule.git that were
1069 * susceptible to CVE-2020-11008.
1070 */
1071 if (count_leading_dotdots(url, &next) > 0 &&
1071 - *next == ':')
1072 + (*next == ':' || *next == '/'))
1073 return -1;
1074 }
1075
1076 else if (url_to_curl_url(url, &curl_url)) {
1077 struct credential c = CREDENTIAL_INIT;
1077 - int ret = credential_from_url_gently(&c, curl_url, 1);
1078 + int ret = 0;
1079 + if (credential_from_url_gently(&c, curl_url, 1) ||
1080 + !*c.host)
1081 + ret = -1;
1082 credential_clear(&c);
1083 return ret;
1084 }
t/t7416-submodule-dash-url.sh
+32
@@ -124,6 +124,38 @@ test_expect_success 'fsck rejects relative URL resolving to empty scheme' '
124 grep gitmodulesUrl err
125 '
126
127 +test_expect_success 'fsck rejects empty hostname' '
128 + git checkout --orphan empty-host &&
129 + cat >.gitmodules <<-\EOF &&
130 + [submodule "foo"]
131 + url = http:///one.example.com/foo.git
132 + EOF
133 + git add .gitmodules &&
134 + test_tick &&
135 + git commit -m "gitmodules with extra slashes" &&
136 + test_when_finished "rm -rf dst" &&
137 + git init --bare dst &&
138 + git -C dst config transfer.fsckObjects true &&
139 + test_must_fail git push dst HEAD 2>err &&
140 + grep gitmodulesUrl err
141 +'
142 +
143 +test_expect_success 'fsck rejects relative url that produced empty hostname' '
144 + git checkout --orphan messy-relative &&
145 + cat >.gitmodules <<-\EOF &&
146 + [submodule "foo"]
147 + url = ../../..//one.example.com/foo.git
148 + EOF
149 + git add .gitmodules &&
150 + test_tick &&
151 + git commit -m "gitmodules abusing relative_path" &&
152 + test_when_finished "rm -rf dst" &&
153 + git init --bare dst &&
154 + git -C dst config transfer.fsckObjects true &&
155 + test_must_fail git push dst HEAD 2>err &&
156 + grep gitmodulesUrl err
157 +'
158 +
159 test_expect_success 'fsck permits embedded newline with unrecognized scheme' '
160 git checkout --orphan newscheme &&
161 cat >.gitmodules <<-\EOF &&