Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 18 Aug 1997 19:26:01 -0700
From:      Cy Schubert <cy@cwsys.cwent.com>
To:        security-officer@freebsd.org
Cc:        freebsd-security@freebsd.org
Subject:   ftp(1) security hole, and suggested fixes
Message-ID:  <199708190226.TAA02995@cwsys.cwent.com>

next in thread | raw e-mail | index | archive | help
There's been some discussion on NetBSD's tech-security list about a security
hole in the ftp client.  Enclosed is the original posting and a proposed
fix for the problem.

Does anyone have any thoughts on this?


Regards,                       Phone:  (250)387-8437
Cy Schubert                      Fax:  (250)387-5766
UNIX Support                   OV/VM:  BCSC02(CSCHUBER)
ITSD                          BITNET:  CSCHUBER@BCSC02.BITNET
Government of BC            Internet:  cschuber@uumail.gov.bc.ca
                                       cschuber@bcsc02.gov.bc.ca

		"Quit spooling around, JES do it."


------- Forwarded Message

Received: (from uucp@localhost) by passer.osg.gov.bc.ca (8.8.5/8.6.10) id HAA23925 for <cy@passer.osg.gov.bc.ca>; Sun, 17 Aug 1997 07:26:28 -0700 (PDT)
X-UIDL: 871832360.000
Resent-Message-Id: <199708171426.HAA23925@passer.osg.gov.bc.ca>
Received: from localhost(127.0.0.1), claiming to be "passer.osg.gov.bc.ca"
 via SMTP by localhost, id smtpdAAAaaxBba; Sun Aug 17 07:26:22 1997
Received: (from uucp@localhost) by passer.osg.gov.bc.ca (8.8.5/8.6.10) id HAA24452 for <cschuber@passer.osg.gov.bc.ca>; Sun, 17 Aug 1997 07:26:18 -0700 (PDT)
Received: from orca.gov.bc.ca(142.32.102.25)
 via SMTP by passer.osg.gov.bc.ca, id smtpdAAAaaxcja; Sun Aug 17 07:26:13 1997
Received: from homeworld.cygnus.com by orca.gov.bc.ca (5.4R3.10/200.1.1.4)
	id AA22609; Sun, 17 Aug 1997 07:26:11 -0700
Received: (qmail 9384 invoked by uid 605); 17 Aug 1997 14:25:42 -0000
Received: (qmail 9378 invoked from network); 17 Aug 1997 14:25:37 -0000
Received: from goanna.cs.rmit.edu.au (lm@131.170.24.40)
  by homeworld.cygnus.com with SMTP; 17 Aug 1997 14:25:37 -0000
Received: (from lm@localhost)
	by goanna.cs.rmit.edu.au (8.8.6/8.8.6) id AAA15240
	for tech-security@netbsd.org; Mon, 18 Aug 1997 00:25:29 +1000 (EST)
Message-Id: <199708171425.AAA15240@goanna.cs.rmit.edu.au>
Subject: ftp(1) security hole, and suggested fixes
To: tech-security@netbsd.org
Date: Mon, 18 Aug 1997 00:25:29 +1000 (EST)
From: Luke Mewburn <lm@rmit.edu.au>
Reply-To: Luke Mewburn <lm@rmit.edu.au>
X-Mailer: ELM [version 2.4 PL25]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Sender: tech-security-owner@netbsd.org
Precedence: list
Delivered-To: tech-security@NetBSD.ORG
Resent-To: cy@passer.osg.gov.bc.ca
Resent-Date: Sun, 17 Aug 1997 07:26:20 -0700
Resent-From: Cy Schubert - ITSD Open Systems Group <cschuber@passer.osg.gov.bc.ca>

Recently someone noted on BUGTRAQ that ftp(1) has two security
problems:

Problem:
    a remote ftp server can create unwanted files by returning a list of
    filenames to mget that aren't what the client asked for. Depending
    upon the complexity of attack by the hostile server, it may be
    rather difficult for a client to detect this in situ.
    E.g, if ftp is run in ~/foo, and "mget *" returns a list of filenames
    including "../.rhosts" with appropriate contents, then an unwary
    user or one who has disabled prompting may find their account wide
    open. Other attacks are possible.

Suggested fix:
    check the returned filenames against the local glob rules, and
    discard those that don't match (e.g, "../.forward" doesn't match
    "foo*"). this could be configurable with an option, and default
    to "do the check".
    I haven't done this yet, as I'm awaiting feedback on the idea.

Problem:
    it is possible to trick the client into executing arbitrary code
    on the client's machine by returning a filename such as '|sh',
    whose contents are the list of shell commands to execute.

Suggested fix:
    modify recvrequest() to take an extra argument, which means
    "ignore special names such as '-' and '|*'". use this flag
    when calling recvrequest() from mget().
    I've done this, and it appears to work.


Comments?

- -- 
Luke Mewburn, <lukem@netbsd.org>

(Message inbox:4)
Received: (from uucp@localhost) by passer.osg.gov.bc.ca (8.8.5/8.6.10) id LAA24720 for <cy@passer.osg.gov.bc.ca>; Sun, 17 Aug 1997 11:43:48 -0700 (PDT)
X-UIDL: 871951866.002
Resent-Message-Id: <199708171843.LAA24720@passer.osg.gov.bc.ca>
Received: from localhost(127.0.0.1), claiming to be "passer.osg.gov.bc.ca"
 via SMTP by localhost, id smtpdAAAaaykra; Sun Aug 17 11:43:39 1997
Received: (from uucp@localhost) by passer.osg.gov.bc.ca (8.8.5/8.6.10) id LAA24658 for <cschuber@passer.osg.gov.bc.ca>; Sun, 17 Aug 1997 11:43:38 -0700 (PDT)
Received: from orca.gov.bc.ca(142.32.102.25)
 via SMTP by passer.osg.gov.bc.ca, id smtpdAAAaayqBa; Sun Aug 17 11:43:37 1997
Received: from homeworld.cygnus.com by orca.gov.bc.ca (5.4R3.10/200.1.1.4)
	id AA22681; Sun, 17 Aug 1997 11:43:34 -0700
Received: (qmail 20625 invoked by uid 605); 17 Aug 1997 18:42:50 -0000
Received: (qmail 20619 invoked from network); 17 Aug 1997 18:42:44 -0000
Received: from burgundy.eecs.harvard.edu (dholland@140.247.60.165)
  by homeworld.cygnus.com with SMTP; 17 Aug 1997 18:42:44 -0000
Received: (from dholland@localhost)
	by burgundy.eecs.harvard.edu (8.8.5/8.8.5) id OAA03099;
	Sun, 17 Aug 1997 14:42:28 -0400 (EDT)
From: David Holland <dholland@eecs.harvard.edu>
Message-Id: <199708171842.OAA03099@burgundy.eecs.harvard.edu>
Subject: Re: ftp(1) security hole, and suggested fixes
To: lm@rmit.edu.au
Date: Sun, 17 Aug 1997 14:42:28 -0400 (EDT)
Cc: tech-security@netbsd.org
In-Reply-To: <199708171425.AAA15240@goanna.cs.rmit.edu.au> from "Luke Mewburn" at Aug 18, 97 00:25:29 am
X-Mailer: ELM [version 2.4 PL25]
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Sender: tech-security-owner@netbsd.org
Precedence: list
Delivered-To: tech-security@NetBSD.ORG
Resent-To: cy@passer.osg.gov.bc.ca
Resent-Date: Sun, 17 Aug 1997 11:43:39 -0700
Resent-From: Cy Schubert - ITSD Open Systems Group <cschuber@passer.osg.gov.bc.ca>

 > Recently someone noted on BUGTRAQ that ftp(1) has two security
 > problems:
 >  [...]
 >
 > Problem:
 >     it is possible to trick the client into executing arbitrary code
 >     on the client's machine by returning a filename such as '|sh',
 >     whose contents are the list of shell commands to execute.
 > 
 > Suggested fix:
 >     modify recvrequest() to take an extra argument, which means
 >     "ignore special names such as '-' and '|*'". use this flag
 >     when calling recvrequest() from mget().
 >     I've done this, and it appears to work.

On second thought I don't think this is ideal: you also, if possible,
want to protect against someone seeing a file called '|sh' on an ftp
server and trying to get it. If you do "get |sh", at present, the
contents of the file on the server will be piped to sh since get
copies its argv[1] to argv[2] if the user doesn't supply one.

So I propose instead prepending "./" to any automatically generated
local name, both in get and mget, that begins with '|' or is '-'.
Also, names beginning with '/' should have a '.' prepended to them --
otherwise the server can send "/root/.rhosts" or
"/home/victim/.rhosts" or whatever.

Additionally, everything that mget generates should have ".." path
elements filtered out. I've left off this processing for get, on the
grounds that someone who does "get ../foo" should expect the file to
appear in the parent directory. Maybe this should be changed to be
more consistent.

My fix does reasonably unhappy things if you do "mget ../*".
Suggestions for improvements are welcome.

The following patch fixes the Linux ftp client, and should be pretty
readily adaptable to the NetBSD one. 

--- cmds.c	1997/06/13 07:25:11	1.20
+++ cmds.c	1997/08/17 18:19:47
@@ -34,9 +34,9 @@
 /*
  * from: @(#)cmds.c	5.26 (Berkeley) 3/5/91
  */
 char cmds_rcsid[] = 
-   "$Id: cmds.c,v 1.20 1997/06/13 07:25:11 dholland Exp $";
+   "$Id: cmds.c,v 1.23 1997/08/17 18:18:30 dholland Exp $";
 
 /*
  * FTP User Program -- Command Routines.
  */
@@ -90,8 +90,67 @@
 static void quote1(const char *initial, int argc, char **argv);
 
 
 /*
+ * pipeprotect: protect against "special" local filenames by prepending
+ * "./". Special local filenames are "-" and "|..." AND "/...".
+ */
+static char *pipeprotect(char *name) 
+{
+	char *nu;
+	if (strcmp(name, "-") && *name!='|' && *name!='/') {
+		return name;
+	}
+
+	/* We're going to leak this memory. XXX. */
+	nu = malloc(strlen(name)+3);
+	if (nu==NULL) {
+		perror("malloc");
+		code = -1;
+		return NULL;
+	}
+	strcpy(nu, ".");
+	if (*name != '/') strcat(nu, "/");
+	strcat(nu, name);
+	return nu;
+}
+
+/*
+ * Look for embedded ".." in a pathname and change it to "!!", printing
+ * a warning.
+ */
+static char *pathprotect(char *name)
+{
+	int gotdots=0, i, len;
+	
+	/* Convert null terminator to trailing / to catch a trailing ".." */
+	len = strlen(name)+1;
+	name[len-1] = '/';
+
+	/*
+	 * State machine loop. gotdots is < 0 if not looking at dots,
+	 * 0 if we just saw a / and thus might start getting dots,
+	 * and the count of dots seen so far if we have seen some.
+	 */
+	for (i=0; i<len; i++) {
+		if (name[i]=='.' && gotdots>=0) gotdots++;
+		else if (name[i]=='/' && gotdots<0) gotdots=0;
+		else if (name[i]=='/' && gotdots==2) {
+		    printf("Warning: embedded .. in %.*s (changing to !!)\n",
+			   len-1, name);
+		    name[i-1] = '!';
+		    name[i-2] = '!';
+		    gotdots = 0;
+		}
+		else if (name[i]=='/') gotdots = 0;
+		else gotdots = -1;
+	}
+	name[len-1] = 0;
+	return name;
+}
+
+
+/*
  * `Another' gets another argument, and stores the new argc and argv.
  * It reverts to the top level (via main.c's intr()) on EOF/error.
  *
  * Returns false if no new arguments have been added.
@@ -594,9 +653,17 @@
 	char *oldargv1, *oldargv2;
 
 	if (argc == 2) {
 		argc++;
-		argv[2] = argv[1];
+		/* 
+		 * Protect the user from accidentally retrieving special
+		 * local names.
+		 */
+		argv[2] = pipeprotect(argv[1]);
+		if (!argv[2]) {
+			code = -1;
+			return 0;
+		}
 		loc++;
 	}
 	if (argc < 2 && !another(&argc, &argv, "remote-file"))
 		goto usage;
@@ -768,10 +835,21 @@
 			}
 			if (mapflag) {
 				tp = domap(tp);
 			}
-			recvrequest("RETR", tp, cp, "w",
-			    tp != cp || !interactive);
+			/* Reject embedded ".." */
+			tp = pathprotect(tp);
+
+			/* Prepend ./ to "-" or "!*" or leading "/" */
+			tp = pipeprotect(tp);
+			if (tp == NULL) {
+				/* hmm... how best to handle this? */
+				mflag = 0;
+			}
+			else {
+				recvrequest("RETR", tp, cp, "w",
+					    tp != cp || !interactive);
+			}
 			if (!mflag && fromatty) {
 				ointer = interactive;
 				interactive = 1;
 				if (confirm("Continue with","mget")) {


-- 
   - David A. Holland             |    VINO project home page:
     dholland@eecs.harvard.edu    | http://www.eecs.harvard.edu/vino



------- End of Forwarded Message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199708190226.TAA02995>