| On some platforms (e.g., Win32), svn client can create files in bad places |
| |
| Summary: |
| ======== |
| |
| This vulnerability requires prior write access to the repository. |
| |
| In Subversion 1.4.4 and earlier versions, on platforms where the |
| directory separator is "\" (or anything other than "/"), the client |
| libraries can allow files outside the working copy to be created |
| during a checkout or update. This could, in theory, be used to |
| place arbitrary code at arbitrary locations on the client machine, |
| for example, in system startup scripts. |
| |
| Known vulnerable: |
| ================= |
| |
| Subversion clients <= 1.4.4 (including clients like TortoiseSVN) |
| |
| Known fixed: |
| ============ |
| |
| Subversion 1.4.5 |
| |
| (Search for "Patch" below to see the patch from 1.4.4 -> 1.4.5. |
| Search for "Recommendation" to get URLs for the 1.4.5 release.) |
| |
| Details: |
| ======== |
| |
| The Subversion client libraries fail to validate that filenames |
| obtained from the Subversion server during checkout do not contain |
| "..\". This allows the creation of files outside the checkout |
| directory. Users on operating systems where "\" is not used to |
| separate directory paths can commit files with "..\" in the path. |
| When these files are checked out onto systems where "\" is a |
| directory separator, hilarity may ensue. To reproduce: |
| |
| On a UNIX system, create a file "..\DIRNAME/exploit.exe" and check |
| it into a repository on the top level. Then check out that |
| repository to a Win32 system. The file will appear outside of the |
| checkout directory and instead under "DIRNAME". |
| |
| Severity: |
| ========= |
| |
| Med (arbitrary file creation on client, possibly over system startup files) |
| |
| An adversary with write access to the repository could create a file |
| at an arbitrary path on the victim's machines. This could be used |
| to install code on the system, for example by placing executable |
| code into the startup sequence. |
| |
| The attacker first requires write access to the repository from |
| which the file will be checked out, and requires that others not |
| notice the commit of the dangerous file. Thus, at first glance it |
| might seem that some social engineering is necessary for a full |
| exploit. However, if the repository administrator is the attacker, |
| little or no social engineering is required. |
| |
| References: |
| =========== |
| |
| CVE-2007-3846 (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846) |
| |
| http://crisp.cs.du.edu/?q=node/36 |
| |
| Reported by: |
| ============ |
| |
| Nils Durner and Christian Grothoff, Colorado Research Institute for |
| Security and Privacy, http://crisp.cs.du.edu/. |
| |
| Recommendation: |
| =============== |
| |
| Upgrade clients to use Subversion 1.4.5 libraries: |
| |
| http://subversion.tigris.org/project_packages.html |
| |
| Workarounds: |
| ============ |
| |
| These workarounds apply only to the repository (server) side. They |
| cannot protect a client from a malicious repository administrator. |
| |
| * Scan existing repositories for paths containing "\", rename them. |
| |
| * Install a pre-commit hook that checks for "\" in filenames. |
| Below is such a hook script, indented by four spaces: |
| |
| #!/bin/sh |
| |
| ### backslash-check.py: A Subversion pre-commit hook script to prevent |
| ### files containing "\" from being added to the repository. |
| ### |
| ### See http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846 |
| |
| ### *** NOTE: *** |
| ### Because Subversion hook scripts execute in a scrubbed environment, |
| ### we use an absolute path to the svnlook binary. You might need to |
| ### adjust it for your system. |
| SVNLOOK="/usr/bin/svnlook" |
| |
| ### You shouldn't need to change anything below this line. |
| REPOS=${1} |
| TXN=${2} |
| |
| if ${SVNLOOK} changed -t ${TXN} ${REPOS} | grep -E "^A +.*\\\\"; then |
| echo "" >&2 |
| echo "Cannot commit paths containing '\\':" >&2 |
| echo "" >&2 |
| # Show the actual paths: |
| ${SVNLOOK} changed -t ${TXN} ${REPOS} \ |
| | grep -E "^A +.*\\\\" | cut -c5- >&2 |
| echo "" >&2 |
| exit 1 |
| else |
| exit 0 |
| fi |
| |
| Patch: |
| ====== |
| |
| This log message and patch applies to Subversion 1.4.4. |
| |
| [[[ |
| CVE-2007-3846: arbitrary path creation during updates and checkouts. |
| |
| * subversion/libsvn_wc/update_editor.c |
| (check_path_under_root): New helper function. |
| (delete_entry, add_or_open_file, open_directory, add_directory): |
| Call above, to prevent paths above cwd from being affected. |
| |
| Patch by: Nils Durner <ndurner@web.de> |
| kfogel |
| ]]] |
| |
| Index: subversion/libsvn_wc/update_editor.c |
| =================================================================== |
| --- subversion/libsvn_wc/update_editor.c (revision 26049) |
| +++ subversion/libsvn_wc/update_editor.c (working copy) |
| @@ -793,6 +793,46 @@ |
| return SVN_NO_ERROR; |
| } |
| |
| + |
| +/* Check that when ADD_PATH is joined to BASE_PATH, the resulting path |
| + * is still under BASE_PATH in the local filesystem. If not, return |
| + * SVN_ERR_WC_OBSTRUCTED_UPDATE; else return success. |
| + * |
| + * This is to prevent the situation where the repository contains, |
| + * say, "..\nastyfile". Although that's perfectly legal on some |
| + * systems, when checked out onto Win32 it would cause "nastyfile" to |
| + * be created in the parent of the current edit directory. |
| + * |
| + * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846) |
| + */ |
| +static svn_error_t * |
| +check_path_under_root(const char *base_path, |
| + const char *add_path, |
| + apr_pool_t *pool) |
| +{ |
| + char *newpath; |
| + apr_status_t retval; |
| + |
| + retval = apr_filepath_merge |
| + (&newpath, base_path, add_path, |
| + APR_FILEPATH_NOTABOVEROOT | APR_FILEPATH_SECUREROOTTEST, |
| + pool); |
| + |
| + if (retval != APR_SUCCESS) |
| + { |
| + return svn_error_createf |
| + (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, |
| + _("Path '%s' is not in the working copy"), |
| + /* Not using newpath here because it might be NULL or |
| + undefined, since apr_filepath_merge() returned error. |
| + (Pity we can't pass NULL for &newpath in the first place, |
| + but the APR docs don't bless that.) */ |
| + svn_path_local_style(svn_path_join(base_path, add_path, pool), pool)); |
| + } |
| + |
| + return SVN_NO_ERROR; |
| +} |
| + |
| |
| /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/ |
| |
| @@ -1033,6 +1073,8 @@ |
| apr_pool_t *pool) |
| { |
| struct dir_baton *pb = parent_baton; |
| + SVN_ERR(check_path_under_root(pb->path, svn_path_basename(path, pool), |
| + pool)); |
| return do_entry_deletion(pb->edit_baton, pb->path, path, &pb->log_number, |
| pool); |
| } |
| @@ -1057,6 +1099,8 @@ |
| || ((! copyfrom_path) && (SVN_IS_VALID_REVNUM(copyfrom_revision)))) |
| abort(); |
| |
| + SVN_ERR(check_path_under_root(pb->path, db->name, pool)); |
| + |
| /* There should be nothing with this name. */ |
| SVN_ERR(svn_io_check_path(db->path, &kind, db->pool)); |
| if (kind != svn_node_none) |
| @@ -1168,6 +1212,8 @@ |
| struct dir_baton *db = make_dir_baton(path, eb, pb, FALSE, pool); |
| *child_baton = db; |
| |
| + SVN_ERR(check_path_under_root(pb->path, db->name, pool)); |
| + |
| /* Mark directory as being at target_revision and URL, but incomplete. */ |
| tmp_entry.revision = *(eb->target_revision); |
| tmp_entry.url = db->new_URL; |
| @@ -1451,6 +1497,8 @@ |
| |
| fb = make_file_baton(pb, path, adding, pool); |
| |
| + SVN_ERR(check_path_under_root(fb->dir_baton->path, fb->name, subpool)); |
| + |
| /* It is interesting to note: everything below is just validation. We |
| aren't actually doing any "work" or fetching any persistent data. */ |
| |