Russ Allbery <ea...@eyrie.org> writes: > I suspect that the rsync protocol is also vulnerable to a version of > this same bug if .ssh/config is writable and is used as the ssh client > path and the ssh binary is available on the server side, by sending an > rsync command that tries to copy a file to localhost: similar to your > second attack example. I believe tightening rssh's check that the rsync > command line starts with --server would address that, since I think (but > haven't confirmed) that will disable remote copies.
> It seems likely that there's some way of abusing cvs as well, given its > huge command surface. Hi all, I finally had a chance to dig into this a little more, since I owe rssh users in Debian stable some security support. The short version is that I think I've patched up the obvious ways to exploit this cluster of vulnerabilities via rsync or scp, but I'm not at all convinced that I've found all the other routes in (and I think it's quite likely there's some lingering way in via the cvs support, since the attack surface for cvs is just so large). Attached are three patches: the first adds argument checking for scp, the second is an update of the earlier rsync patch that tightens its argument checking and requires rsync be put into server mode (which looks like it will prevent initiation of outbound ssh connections, which would be another route to this same exploit), and the third applies argument checking to commands run inside a chroot. It turns out that rssh was never checking arguments if run via a chroot, and I didn't notice because I personally use it as a mild defense in depth where a chroot didn't feel important. (This last was pointed out to me by another list member in private email. I'm not naming you explicitly since I wasn't sure if you wanted to be mentioned on-list, but feel free to claim credit -- that was a great catch!) I've uploaded a new version of the Debian package to unstable with these fixes and will work with the Debian security team on a stable fix. However, once the new Debian package propagates to testing, I expect to request that it be removed (in advance of our upcoming new Debian stable release), so it will not be in the next release of Debian. I think it's time; we know the security model isn't the greatest, and I suspect we'll uncover more problems like this and I don't want to provide security support through another release cycle. -- Russ Allbery (ea...@eyrie.org) <http://www.eyrie.org/~eagle/>
From: Russ Allbery <r...@debian.org> Date: Sat, 7 Dec 2013 18:32:56 -0800 Subject: Handle rsync v3 -e protocol option As of rsync 3, rsync reused the -e option to pass protocol information from the client to the server. We therefore cannot reject all -e options to rsync, only ones not sent with --server or containing something other than protocol information as an argument. Be stricter about the rsync command line and require --server as the first argument, which disables attempts to initiate rsync outbound from the server and in turn could trigger running code specified in ssh client configuration options. Also scan the rsync command line for any --rsh option and reject it as well. This replaces and improves the upstream strategy for rejecting that command-line option, taking advantage of the parsing added to check the -e option. Based on work by Robert Hardy. Debian Bug#471803 --- util.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/util.c b/util.c index ef1a5d8..6ee0799 100644 --- a/util.c +++ b/util.c @@ -56,6 +56,7 @@ #ifdef HAVE_LIBGEN_H #include <libgen.h> #endif /* HAVE_LIBGEN_H */ +#include <regex.h> /* LOCAL INCLUDES */ #include "pathnames.h" @@ -197,6 +198,69 @@ bool check_command( char *cl, ShellOptions_t *opts, char *cmd, int cmdflag ) } +/* + * rsync_okay() - require --server on all rsh command lines, check that -e + * contains only protocol information, and reject any --rsh + * option. Returns FALSE if the command line should not be + * allowed, TRUE if it is okay. + */ +static int rsync_okay( char **vec ) +{ + regex_t re; + int server = FALSE; + int e_found = FALSE; + + /* + * rsync will send -e, followed by either just "." (meaning no special + * protocol) or "N.N" (meaning a pre-release protocol version), + * followed by some number of alphabetic flags indicating various + * supported options. There may be other options between - and the e, + * but -e will always be the last option in the string. A typical + * option passed by the client is "-ltpre.iL". + * + * Note that if --server is given, this should never be parsed as a + * shell, but we'll tightly verify it anyway, just in case. + * + * This regex matches the acceptable flags containing -e, so if it + * does not match, the command line should be rejected. + */ + static const char pattern[] + = "^-[a-df-zA-Z]*e[0-9]*\\.[0-9]*[a-zA-Z]*$"; + + /* + * Only recognize --server if it's the first option. rsync itself + * always passes it that way, and if it's not the first argument, it + * could be hidden from the server as an argument to some other + * option. + */ + if ( !(vec && vec[0] && vec[1] && strcmp(vec[1], "--server") == 0) ) + return FALSE; + + /* Check the remaining options for -e or --rsh. */ + if ( regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0 ){ + return FALSE; + } + while (vec && *vec){ + if ( strcmp(*vec, "--") == 0 ) break; + if ( strcmp(*vec, "--rsh") == 0 + || strncmp(*vec, "--rsh=", strlen("--rsh=")) == 0 ){ + regfree(&re); + return FALSE; + } + if ( strncmp(*vec, "--", 2) != 0 && opt_exist(*vec, 'e') ){ + e_found = TRUE; + if ( regexec(&re, *vec, 0, NULL, 0) != 0 ){ + regfree(&re); + return FALSE; + } + } + vec++; + } + regfree(&re); + return TRUE; +} + + /* * check_command_line() - take the command line passed to rssh, and verify * that the specified command is one the user is @@ -230,14 +294,10 @@ char *check_command_line( char **cl, ShellOptions_t *opts ) if ( check_command(*cl, opts, PATH_RSYNC, RSSH_ALLOW_RSYNC) ){ /* filter -e option */ - if ( opt_filter(cl, 'e') ) return NULL; - while (cl && *cl){ - if ( strstr(*cl, "--rsh" ) ){ - fprintf(stderr, "\ninsecure --rsh= not allowed."); - log_msg("insecure --rsh option in rsync command line!"); - return NULL; - } - cl++; + if ( !rsync_okay(cl) ){ + fprintf(stderr, "\ninsecure rsync options not allowed."); + log_msg("insecure rsync options in rsync command line!"); + return NULL; } return PATH_RSYNC; }
From: Russ Allbery <r...@debian.org> Date: Thu, 17 Jan 2019 19:21:40 -0800 Subject: Verify scp command options ESnet discovered a security vulnerability in the scp backend for rssh. Since the arguments to scp on the server side were not checked, the client could pass in arbitrary scp command-line flags, including setting arbitrary scp options. This allows setting the option PKCS11Provider, which loads and executes code from a shared module. Even if the -o flag is blocked, this is still possible via -F to load an already-uploaded ssh configuration file, or, if .ssh/config is writable, by just uploading that configuration file directly first. Attempt to protect against this attack by checking the command line of scp and only allowing the options that are passed to the server end of the connection. Specifically, do not allow multiple non-option arguments, which attempts to prevent causing the server to initiate an scp command. (This will break scp -3 through rssh, which seems like an acceptable tradeoff.) Debian Bug#919623 --- util.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/util.c b/util.c index 3ec01e1..6e668c9 100644 --- a/util.c +++ b/util.c @@ -264,6 +264,45 @@ static int rsync_okay( char **vec ) } +/* + * scp_okay() - take the command line and check that it is a hopefully-safe scp + * server command line, accepting only very specific options. + * Returns FALSE if the command line should not be allowed, TRUE + * if it is okay. + */ +static int scp_okay( char **vec ) +{ + int saw_file = FALSE; + int saw_end = FALSE; + + for ( vec++; vec && *vec; vec++ ){ + /* Allowed options. */ + if ( !saw_end ) { + if ( strcmp(*vec, "-v") == 0 ) continue; + if ( strcmp(*vec, "-r") == 0 ) continue; + if ( strcmp(*vec, "-p") == 0 ) continue; + if ( strcmp(*vec, "-d") == 0 ) continue; + if ( strcmp(*vec, "-f") == 0 ) continue; + if ( strcmp(*vec, "-t") == 0 ) continue; + } + + /* End of arguments. One more argument allowed after this. */ + if ( !saw_end && strcmp(*vec, "--") == 0 ){ + saw_end = TRUE; + continue; + } + + /* No other options allowed, but allow file starting with -. */ + if ( *vec[0] == '-' && !saw_end ) return FALSE; + if ( saw_file ) return FALSE; + saw_file = TRUE; + } + + /* We must have seen a single file. */ + return saw_file; +} + + /* * check_command_line() - take the command line passed to rssh, and verify * that the specified command is one the user is @@ -279,8 +318,11 @@ char *check_command_line( char **cl, ShellOptions_t *opts ) return PATH_SFTP_SERVER; if ( check_command(*cl, opts, PATH_SCP, RSSH_ALLOW_SCP) ){ - /* filter -S option */ - if ( opt_filter(cl, 'S') ) return NULL; + if ( !scp_okay(cl) ){ + fprintf(stderr, "\ninsecure scp option not allowed."); + log_msg("insecure scp option in scp command line"); + return NULL; + } return PATH_SCP; }
From: Russ Allbery <r...@debian.org> Date: Mon, 28 Jan 2019 20:15:30 -0800 Subject: Check command line after chroot When a command was configured with a chroot, rssh did not check the safety of the command line after chroot, allowing various vectors of remote code execution inside the chroot environment. Perform the same check after chroot as is performed before running the command when a chroot is not configured. --- rssh_chroot_helper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rssh_chroot_helper.c b/rssh_chroot_helper.c index 8a35cdc..73d8c7b 100644 --- a/rssh_chroot_helper.c +++ b/rssh_chroot_helper.c @@ -218,6 +218,12 @@ int main( int argc, char **argv ) ch_fatal_error("build_arg_vector()", argv[2], "bad expansion"); + /* check the command for safety */ + if ( !check_command_line(argvec, &opts) ){ + fprintf(stderr, "\n"); + exit(1); + } + /* * This is the old way to figure out what program to run. Since we're * re-parsing the config file in rssh_chroot helper, we could get rid
_______________________________________________ rssh-discuss mailing list rssh-discuss@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rssh-discuss