Fix for DoS in LIST command with GNU ls

Chris Butler <[email protected]> Tue, 22 Feb 2005 21:44:03 +0000
Newsgroups gmane.network.ftp.wuftpd.devel
Message-ID <[email protected]>
--ieNMXl1Fr3cevapt
Content-Type: multipart/mixed; boundary="CblX+4bnyfN0pR09"
Content-Disposition: inline


--CblX+4bnyfN0pR09
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi,

I've had a bug reported via the Debian Bug Tracking System about a DoS
attack against wu-ftpd, that exploits the --width argument to GNU 'ls'.
By repeatedly giving it a very large argument, an attacker can cause ls
to exhaust the system memory.

The attached patch adds a function to ftpcmd.y, called sanitise_ls_args,
which removes the -w argument and its argument from the arguments to the
ls command.

--=20
Chris Butler    <[email protected]>
 Debian WU-FTPD package maintainer
   GnuPG Key ID: 1024D/D097A261

--CblX+4bnyfN0pR09
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="dosfix.patch"
Content-Transfer-Encoding: quoted-printable

Index: ftpcmd.y
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- ftpcmd.y	(revision 33)
+++ ftpcmd.y	(working copy)
@@ -395,15 +395,22 @@
 	    }
 	}
     | LIST check_login SP pathname CRLF	{
+			char *ls_args;
+	=09
 	    if (log_commands)
 		syslog(LOG_INFO, "LIST %s", CHECKNULL($4));
 	    if ($2 && $4 !=3D NULL && !restrict_list_check($4)) {
 		retrieve_is_data =3D 0;
 #ifndef INTERNAL_LS
+		ls_args =3D sanitise_ls_args($4);
+
 		if (anonymous && dolreplies)
-		    retrieve(ls_long, $4);
+		    retrieve(ls_long, ls_args);
 		else
-		    retrieve(ls_short, $4);
+		    retrieve(ls_short, ls_args);
+
+		if (ls_args !=3D NULL)
+			free(ls_args);
 #else
 		ls($4, 0);
 #endif
@@ -1937,3 +1944,38 @@
     (void) fflush(stdout);
     reply(214, "");
 }
+
+char *sanitise_ls_args(char *inp)
+{
+		/* sanitise arguments to ls, to avoid -w DoS with GNU ls */
+    char *ls_args, *inp_p, *ls_args_p;
+
+		if (inp =3D=3D NULL)
+			return NULL;
+							=09
+		ls_args =3D malloc(strlen(inp) + 1);
+    inp_p =3D inp;
+    ls_args_p =3D ls_args;
+							=09
+    while(*inp_p !=3D '\0') {
+      if (strncasecmp(inp_p,"-w",2) =3D=3D 0) {
+  	    /* skip -w and its argument */
+        inp_p +=3D 2;
+        for (;*inp_p !=3D '\0' && isspace(*inp_p);inp_p++);
+        for (;*inp_p !=3D '\0' && !isspace(*inp_p);inp_p++);
+      } else if (strncasecmp(inp_p,"--width",7) =3D=3D 0) {
+  	    /* same with the long option */
+        inp_p +=3D 7;
+        for (;*inp_p !=3D '\0' && isspace(*inp_p);inp_p++);
+        for (;*inp_p !=3D '\0' && !isspace(*inp_p);inp_p++);
+      } else {
+        *ls_args_p =3D *inp_p;
+				++ls_args_p;
+				++inp_p;
+			}
+    }
+
+		*ls_args_p =3D '\0';
+    return ls_args;
+}
+
Index: proto.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- proto.h	(revision 33)
+++ proto.h	(working copy)
@@ -135,6 +135,7 @@
 void alias(char *s);
 void cdpath(void);
 void print_groups(void);
+char *sanitise_ls_args(char *);
=20
 /*
    ** ftpd.c

--CblX+4bnyfN0pR09--

--ieNMXl1Fr3cevapt
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFCG6eiDzQFd9CXomERAqiLAJ9j1u8fw4tVkYe1X4MrZMbeHgFAVQCeJuRY
4ioPCAtW4D7+p/qOQhButRY=
=nhIP
-----END PGP SIGNATURE-----

--ieNMXl1Fr3cevapt--