|
| 1 | + mod_dav_svn denial-of-service via control characters in paths |
| 2 | + |
| 3 | +Summary: |
| 4 | +======== |
| 5 | + |
| 6 | + It has been discovered that the patch for CVE-2013-1968 was incomplete |
| 7 | + and unintentionally left mod_dav_svn vulnerable to control characters |
| 8 | + in filenames. |
| 9 | + |
| 10 | + If a path or a revision-property which contains control characters is |
| 11 | + committed to a repository then SVN operations served by mod_dav_svn |
| 12 | + can be disrupted. |
| 13 | + |
| 14 | +Known vulnerable: |
| 15 | +================= |
| 16 | + |
| 17 | + Subversion mod_dav_svn servers through 1.14.4 (inclusive). |
| 18 | + |
| 19 | +Known fixed: |
| 20 | +============ |
| 21 | + |
| 22 | + Servers running Subversion 1.14.5 |
| 23 | + |
| 24 | +Details: |
| 25 | +======== |
| 26 | + |
| 27 | + If a path which contains control characters is committed to a repository |
| 28 | + then SVN operations served by mod_dav_svn can be disrupted by encoding |
| 29 | + errors raised from the XML library. |
| 30 | + |
| 31 | + This leads to disruption for users accessing the repository via HTTP. |
| 32 | + Affected repositories can be repaired (see "Recommendations" below). |
| 33 | + However, restoring proper operation might take some time because a |
| 34 | + full dump/load cycle may be required. |
| 35 | + |
| 36 | + Local repositories and svnserve repository servers (accessed via a |
| 37 | + file://, svn://, or svn+ssh:// URL) are not affected. In these cases, |
| 38 | + control characters have been rejected since CVE-2013-1968 was patched |
| 39 | + in Subversion 1.6.21 and Subversion 1.7.9. |
| 40 | + |
| 41 | + Known symptoms of the problem include: |
| 42 | + |
| 43 | + 1) 'svn checkout', 'svnsync', and other operations that attempt to |
| 44 | + read the affected revision may produce errors like: |
| 45 | + |
| 46 | + svn: E175009: The XML response contains invalid XML |
| 47 | + svn: E130003: Malformed XML: not well-formed (invalid token) |
| 48 | + |
| 49 | + 2) Attempts to browse affected files or directories via the web |
| 50 | + interface will cause the server to return: |
| 51 | + |
| 52 | + 500 Internal Server Error |
| 53 | + |
| 54 | + Apache Subversion clients have always rejected filenames with control |
| 55 | + characters, so control characters cannot be introduced with stock |
| 56 | + Subversion clients. They could, however, be triggered by custom |
| 57 | + malicious Subversion clients or by third-party client implementations. |
| 58 | + |
| 59 | + Servers updated to Subversion 1.14.5 will reject control characters in |
| 60 | + all cases. |
| 61 | + |
| 62 | +Severity: |
| 63 | +========= |
| 64 | + |
| 65 | + CVSSv3.1 Base Score: 3.1 |
| 66 | + CVSSv3.1 Base Vector: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L |
| 67 | + |
| 68 | + A remote authenticated attacker with commit access may be able to |
| 69 | + corrupt repositories on a Subversion server and cause disruption for |
| 70 | + other users. |
| 71 | + |
| 72 | + Configurations that allow anonymous write access to the repository |
| 73 | + will be vulnerable to this without authentication. |
| 74 | + |
| 75 | +Recommendations: |
| 76 | +================ |
| 77 | + |
| 78 | + We recommend all users to upgrade their servers to a known fixed |
| 79 | + release of Subversion. |
| 80 | + |
| 81 | + Users who are unable to upgrade may apply the patch included below. |
| 82 | + |
| 83 | + New Subversion packages can be found at: |
| 84 | + http://subversion.apache.org/packages.html |
| 85 | + |
| 86 | + Repositories affected by this problem can be repaired manually: |
| 87 | + |
| 88 | + Bad revision properties can be repaired by using svn propedit over |
| 89 | + the file://, svn:// or svn+ssh:// protocols. |
| 90 | + |
| 91 | + Bad paths which have entered a repository need to be removed from |
| 92 | + history with a dump/load cycle, using svnadmin dump --exclude to |
| 93 | + filter out the bad paths, and loading the result into a fresh |
| 94 | + repository with svnadmin load. |
| 95 | + |
| 96 | +References: |
| 97 | +=========== |
| 98 | + |
| 99 | + CVE-2024-46901 (Subversion) |
| 100 | + CVE-2013-1968 (Subversion) |
| 101 | + |
| 102 | + XML Characters: https://www.w3.org/TR/xml/#charsets |
| 103 | + |
| 104 | +Reported by: |
| 105 | +============ |
| 106 | + |
| 107 | + HaoZi, WordPress China |
| 108 | + |
| 109 | +Patches: |
| 110 | +======== |
| 111 | + |
| 112 | + Patch against Subversion 1.14.4: |
| 113 | + |
| 114 | +Link: https://subversion.apache.org/security/CVE-2024-46901-advisory.txt |
| 115 | + |
| 116 | +[[[ |
| 117 | +Index: subversion/include/private/svn_repos_private.h |
| 118 | +=================================================================== |
| 119 | +--- subversion/include/private/svn_repos_private.h (revision 1921550) |
| 120 | ++++ subversion/include/private/svn_repos_private.h (working copy) |
| 121 | +@@ -390,6 +390,14 @@ svn_repos__get_dump_editor(const svn_delta_editor_ |
| 122 | + const char *update_anchor_relpath, |
| 123 | + apr_pool_t *pool); |
| 124 | + |
| 125 | ++/* Validate that the given PATH is a valid pathname that can be stored in |
| 126 | ++ * a Subversion repository, according to the name constraints used by the |
| 127 | ++ * svn_repos_* layer. |
| 128 | ++ */ |
| 129 | ++svn_error_t * |
| 130 | ++svn_repos__validate_new_path(const char *path, |
| 131 | ++ apr_pool_t *scratch_pool); |
| 132 | ++ |
| 133 | + #ifdef __cplusplus |
| 134 | + } |
| 135 | + #endif /* __cplusplus */ |
| 136 | +Index: subversion/libsvn_repos/commit.c |
| 137 | +=================================================================== |
| 138 | +--- subversion/libsvn_repos/commit.c (revision 1921550) |
| 139 | ++++ subversion/libsvn_repos/commit.c (working copy) |
| 140 | +@@ -308,8 +308,7 @@ add_file_or_directory(const char *path, |
| 141 | + svn_boolean_t was_copied = FALSE; |
| 142 | + const char *full_path, *canonicalized_path; |
| 143 | + |
| 144 | +- /* Reject paths which contain control characters (related to issue #4340). */ |
| 145 | +- SVN_ERR(svn_path_check_valid(path, pool)); |
| 146 | ++ SVN_ERR(svn_repos__validate_new_path(path, pool)); |
| 147 | + |
| 148 | + SVN_ERR(svn_relpath_canonicalize_safe(&canonicalized_path, NULL, path, |
| 149 | + pool, pool)); |
| 150 | +Index: subversion/libsvn_repos/repos.c |
| 151 | +=================================================================== |
| 152 | +--- subversion/libsvn_repos/repos.c (revision 1921550) |
| 153 | ++++ subversion/libsvn_repos/repos.c (working copy) |
| 154 | +@@ -2092,3 +2092,13 @@ svn_repos__fs_type(const char **fs_type, |
| 155 | + svn_dirent_join(repos_path, SVN_REPOS__DB_DIR, pool), |
| 156 | + pool); |
| 157 | + } |
| 158 | ++ |
| 159 | ++svn_error_t * |
| 160 | ++svn_repos__validate_new_path(const char *path, |
| 161 | ++ apr_pool_t *scratch_pool) |
| 162 | ++{ |
| 163 | ++ /* Reject paths which contain control characters (related to issue #4340). */ |
| 164 | ++ SVN_ERR(svn_path_check_valid(path, scratch_pool)); |
| 165 | ++ |
| 166 | ++ return SVN_NO_ERROR; |
| 167 | ++} |
| 168 | +Index: subversion/mod_dav_svn/lock.c |
| 169 | +=================================================================== |
| 170 | +--- subversion/mod_dav_svn/lock.c (revision 1921550) |
| 171 | ++++ subversion/mod_dav_svn/lock.c (working copy) |
| 172 | +@@ -36,6 +36,7 @@ |
| 173 | + #include "svn_pools.h" |
| 174 | + #include "svn_props.h" |
| 175 | + #include "private/svn_log.h" |
| 176 | ++#include "private/svn_repos_private.h" |
| 177 | + |
| 178 | + #include "dav_svn.h" |
| 179 | + |
| 180 | +@@ -717,6 +718,12 @@ append_locks(dav_lockdb *lockdb, |
| 181 | + |
| 182 | + /* Commit a 0-byte file: */ |
| 183 | + |
| 184 | ++ if ((serr = svn_repos__validate_new_path(resource->info->repos_path, |
| 185 | ++ resource->pool))) |
| 186 | ++ return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, |
| 187 | ++ "Request specifies an invalid path.", |
| 188 | ++ resource->pool); |
| 189 | ++ |
| 190 | + if ((serr = dav_svn__get_youngest_rev(&rev, repos, resource->pool))) |
| 191 | + return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, |
| 192 | + "Could not determine youngest revision", |
| 193 | +Index: subversion/mod_dav_svn/repos.c |
| 194 | +=================================================================== |
| 195 | +--- subversion/mod_dav_svn/repos.c (revision 1921550) |
| 196 | ++++ subversion/mod_dav_svn/repos.c (working copy) |
| 197 | +@@ -2928,6 +2928,16 @@ open_stream(const dav_resource *resource, |
| 198 | + |
| 199 | + if (kind == svn_node_none) /* No existing file. */ |
| 200 | + { |
| 201 | ++ serr = svn_repos__validate_new_path(resource->info->repos_path, |
| 202 | ++ resource->pool); |
| 203 | ++ |
| 204 | ++ if (serr != NULL) |
| 205 | ++ { |
| 206 | ++ return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, |
| 207 | ++ "Request specifies an invalid path.", |
| 208 | ++ resource->pool); |
| 209 | ++ } |
| 210 | ++ |
| 211 | + serr = svn_fs_make_file(resource->info->root.root, |
| 212 | + resource->info->repos_path, |
| 213 | + resource->pool); |
| 214 | +@@ -4120,6 +4130,14 @@ create_collection(dav_resource *resource) |
| 215 | + return err; |
| 216 | + } |
| 217 | + |
| 218 | ++ if ((serr = svn_repos__validate_new_path(resource->info->repos_path, |
| 219 | ++ resource->pool)) != NULL) |
| 220 | ++ { |
| 221 | ++ return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, |
| 222 | ++ "Request specifies an invalid path.", |
| 223 | ++ resource->pool); |
| 224 | ++ } |
| 225 | ++ |
| 226 | + if ((serr = svn_fs_make_dir(resource->info->root.root, |
| 227 | + resource->info->repos_path, |
| 228 | + resource->pool)) != NULL) |
| 229 | +@@ -4194,6 +4212,12 @@ copy_resource(const dav_resource *src, |
| 230 | + return err; |
| 231 | + } |
| 232 | + |
| 233 | ++ serr = svn_repos__validate_new_path(dst->info->repos_path, dst->pool); |
| 234 | ++ if (serr) |
| 235 | ++ return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, |
| 236 | ++ "Request specifies an invalid path.", |
| 237 | ++ dst->pool); |
| 238 | ++ |
| 239 | + src_repos_path = svn_repos_path(src->info->repos->repos, src->pool); |
| 240 | + dst_repos_path = svn_repos_path(dst->info->repos->repos, dst->pool); |
| 241 | + |
| 242 | +@@ -4430,6 +4454,12 @@ move_resource(dav_resource *src, |
| 243 | + if (err) |
| 244 | + return err; |
| 245 | + |
| 246 | ++ serr = svn_repos__validate_new_path(dst->info->repos_path, dst->pool); |
| 247 | ++ if (serr) |
| 248 | ++ return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, |
| 249 | ++ "Request specifies an invalid path.", |
| 250 | ++ dst->pool); |
| 251 | ++ |
| 252 | + /* Copy the src to the dst. */ |
| 253 | + serr = svn_fs_copy(src->info->root.root, /* the root object of src rev*/ |
| 254 | + src->info->repos_path, /* the relative path of src */ |
| 255 | +]]] |
0 commit comments