Sunday 18 September 2011

Exploiting format strings @ wu-ftpd 2.6.0 @ Ubuntu 10.04.03 LTS

Some time ago I read this post the-shellcoders-handbook-second-edition at xorl's blog. I decided to get a copy of the book and learn from it as much as I could. However, the book is a bit outdated (as xorl states also in aforementioned post) - it was published in 2006/07. For example in chapter 4 (dealing with format strings exploitation) there is a thorough example of how to perform an attack on wu-ftpd version 2.6.0. A bit excited, I set on working with it. And the "outdatedness" suddenly got in my way...


 Wu-ftpd is no longer supported, but still available in Ubuntu repositories. Thus, one can get running daemon as easily as this:

polishcode@U-10043-386:~$ sudo apt-get install wu-ftpd
[sudo] password for polishcode: 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  linux-headers-2.6.32-28 linux-headers-2.6.32-28-generic
Use 'apt-get autoremove' to remove them.
The following NEW packages will be installed:
  wu-ftpd
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 296kB of archives.
After this operation, 856kB of additional disk space will be used.
Get:1 http://pl.archive.ubuntu.com/ubuntu/ lucid/universe wu-ftpd 2.6.2-31ubuntu1 [296kB]
Fetched 296kB in 9s (30.0kB/s)                                                                                                                                         
Preconfiguring packages ...
Selecting previously deselected package wu-ftpd.
(Reading database ... 152837 files and directories currently installed.)
Unpacking wu-ftpd (from .../wu-ftpd_2.6.2-31ubuntu1_amd64.deb) ...
Processing triggers for ureadahead ...
ureadahead will be reprofiled on next reboot
Processing triggers for man-db ...
Setting up wu-ftpd (2.6.2-31ubuntu1) ...
 * Starting FTP server wu-ftpd                                                                                                                                   [ OK ] 

polishcode@U-10043-386:~$ telnet localhost 21
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 U-10043-386 FTP server (Version wu-2.6.2(1) Wed Sep 30 08:46:23 UTC 2009) ready.
quit
221 Goodbye.
Connection closed by foreign host.

The default installed version is 2.6.2, not susceptible to format string flaws:

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ telnet localhost 21
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 U-10043-386 FTP server (Version wu-2.6.2(1) Wed Sep 30 08:44:57 UTC 2009) ready.
USER polishcode
331 Password required for polishcode.
PASS dupa
230 User polishcode logged in.
site exec %x %x %x %x %x %x %x %x
200-%x %x %x %x %x %x %x %x
200  (end of '%x %x %x %x %x %x %x %x')
site index %x %x %x %x %x %x %x %x
200-index %x %x %x %x %x %x %x %x
200  (end of 'index %x %x %x %x %x %x %x %x')
quit
221-You have transferred 0 bytes in 0 files.
221-Total traffic for this session was 444 bytes in 0 transfers.
221-Thank you for using the FTP service on U-10043-386.
221 Goodbye.
Connection closed by foreign host.


Additionally, main repository contains only one version to install:

polishcode@U-10043-386:~$ sudo apt-cache showpkg wu-ftpd
Package: wu-ftpd
Versions:
2.6.2-31ubuntu1 (/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_lucid_universe_binary-i386_Packages)
 Description Language:
                 File: /var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_lucid_universe_binary-i386_Packages
                  MD5: fb738037171b2322d34372f59596ac2e

For learning purposes one needs to substitute 2.6.2 with 2.6.0 version of this ftp daemon. In order to do that, first download appropriate package (you may use icm.edu.pl). Please do not remove 2.6.2 version, as it sets up environment required by 2.6.0 version and it is easier that way.
After retrieving, unpack it to a known location. Read README and INSTALL files bundled with package to get general idea how to prepare and install package. Lets start building the devil (logs partially removed for clarity):

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ ./build lnx

[...]

Making ftpd.
gcc -O3 -fomit-frame-pointer -fno-strength-reduce -pipe -I.. -I../support  -L../support -s  -DSHADOW_PASSWORD   -c -o COPYRIGHT.o COPYRIGHT.c
sh newvers.sh
gcc -O3 -fomit-frame-pointer -fno-strength-reduce -pipe -I.. -I../support  -L../support -s  -DSHADOW_PASSWORD   -c -o vers.o vers.c
gcc -O3 -fomit-frame-pointer -fno-strength-reduce -pipe -I.. -I../support  -L../support -s  -DSHADOW_PASSWORD   -c -o ftpd.o ftpd.c
ftpd.c: In function ‘socket_flush_wait’:
ftpd.c:538: warning: ignoring return value of ‘read’, declared with attribute warn_unused_result
ftpd.c: In function ‘main’:
ftpd.c:761: warning: ignoring return value of ‘freopen’, declared with attribute warn_unused_result
ftpd.c: In function ‘randomsig’:
ftpd.c:1341: warning: ignoring return value of ‘chdir’, declared with attribute warn_unused_result
ftpd.c: In function ‘pass’:
ftpd.c:2795: error: invalid application of ‘sizeof’ to incomplete type ‘struct dqblk’ 
ftpd.c: In function ‘retrieve’:
ftpd.c:3968: warning: cast from pointer to integer of different size
ftpd.c:4172: warning: format ‘%d’ expects type ‘int’, but argument 6 has type ‘off_t’
ftpd.c:4189: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result
ftpd.c: In function ‘store’:
ftpd.c:4519: warning: format ‘%d’ expects type ‘int’, but argument 6 has type ‘off_t’
ftpd.c:4536: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result
ftpd.c: In function ‘dataconn’:
ftpd.c:4640: warning: format ‘%d’ expects type ‘int’, but argument 3 has type ‘off_t’
ftpd.c: In function ‘passive’:
ftpd.c:6145: warning: format not a string literal and no format arguments
ftpd.c:6160: warning: format not a string literal and no format arguments
ftpd.c: In function ‘do_daemon’:
ftpd.c:6965: warning: ignoring return value of ‘freopen’, declared with attribute warn_unused_result
make: *** [ftpd.o] Error 1

[...]

Executables are in bin directory:
size: 'bin/ftpd': No such file
   text     data      bss      dec      hex  filename
   8590      892       32     9514     252a  bin/ftpcount
size: 'bin/ftpshut': No such file
   7783      836    16416    25035     61cb  bin/ftprestart
   8590      892       32     9514     252a  bin/ftpwho
   6932      740     8256    15928     3e38  bin/ckconfig
   9859      868      384    11111     2b67  bin/privatepw
Done

As first encountered error suggests:

ftpd.c:2795: error: invalid application of ‘sizeof’ to incomplete type ‘struct dqblk’

navigate to line 2795 in file src/ftpd.c:

#ifdef QUOTA
    memset(&quota, 0, sizeof(quota));
    get_quota(pw->pw_dir, pw->pw_uid);
#endif

Lets search for QUOTA declaration:

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ grep -Rns QUOTA .

There are a few solutions to that problem. Remember that a fully functional ftp server is not what we need here. What we need is a working server with a known flaw for learning purposes. Achieving this can be done in a numerous ways, one of which is commenting out line:

./src/config/config.lnx:92:#define QUOTA

which was pointed out by search done by previous command. Let us rebuild sources. Remember to perform

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ ./build clean

between builds to get rid of intermediate files.
In my box case error in next build is as follows:

ftpd.c:6965: warning: ignoring return value of ‘freopen’, declared with attribute warn_unused_result
yacc  ftpcmd.y
make: yacc: Command not found
make: *** [ftpcmd.c] Error 127

yacc tool needs to be installed:

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ sudo apt-get install byacc

Building again gives

access.c: In function ‘parsetime’:
access.c:105: warning: assignment makes pointer from integer without a cast
access.c:106: error: dereferencing pointer to incomplete type
access.c:133: error: dereferencing pointer to incomplete type
access.c:133: error: dereferencing pointer to incomplete type

what means there is a problem with time struct:

struct tm *curtime;

The thing is that tm struct is correctly defined in /usr/include/time.h, whereas conditional compilation dereferences to other libraries:

#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#include <sys/time.h>
#elif defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#else
#include <time.h>
#endif

The easiest solution is to undefine both flags: TIME_WITH_SYS_TIME and HAVE_SYS_TIME_H. This will cause usage of #include
First removal is done in file configuration at line 1373, second in file src/config/config.lnx@53.

Getting rid of previous errors gives us following:

extensions.c:1005: error: ‘RE_SYNTAX_POSIX_EXTENDED’ undeclared (first use in this function)
extensions.c:1005: error: (Each undeclared identifier is reported only once
extensions.c:1005: error: for each function it appears in.)

Just comment out section

#ifdef LINUX
    re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
#endif

Right now you should get beautifully shaped "Done":

Executables are in bin directory:
   text     data      bss      dec      hex  filename
 164302    10652    97280   272234    4276a  bin/ftpd
   7660      492       16     8168     1fe8  bin/ftpcount
   9482      500    10308    20290     4f42  bin/ftpshut
   7025      464    12320    19809     4d61  bin/ftprestart
   7660      492       16     8168     1fe8  bin/ftpwho
   6378      416     8256    15050     3aca  bin/ckconfig
   8721      480      384     9585     2571  bin/privatepw
Done

Almost there - code compiles now. Before installing it, turn off previously started 2.6.2 daemon ($ wu-ftpd stop). Perform (step not necessary):

polishcode@U-10043-386:~/wu-ftpd-2.6.0$ sudo ./build install
[sudo] password for polishcode:
make args are :
make opts are :
installing binaries.
install -c -o bin -g bin -m 110 bin/ftpd           /usr/sbin/in.ftpd
install -c -o bin  -g bin  -m 111 bin/ftpshut        /usr/bin/ftpshut
install -c -o bin  -g bin  -m 111 bin/ftprestart     /usr/bin/ftprestart
install -c -o bin  -g bin  -m 111 bin/ftpcount       /usr/bin/ftpcount
install -c -o bin  -g bin  -m 111 bin/ftpwho         /usr/bin/ftpwho
install -c -o bin  -g bin  -m 111 bin/privatepw      /usr/bin/privatepw
installing manpages.
install -c -o bin -g bin -m 444 doc/ftpcount.1       /usr/man/man1/ftpcount.1
install -c -o bin -g bin -m 444 doc/ftpwho.1         /usr/man/man1/ftpwho.1
install -c -o bin -g bin -m 444 doc/ftpaccess.5      /usr/man/man5/ftpaccess.5
install -c -o bin -g bin -m 444 doc/ftpconversions.5 /usr/man/man5/ftpconversions.5
install -c -o bin -g bin -m 444 doc/ftphosts.5       /usr/man/man5/ftphosts.5
install -c -o bin -g bin -m 444 doc/xferlog.5        /usr/man/man5/xferlog.5
install -c -o bin -g bin -m 444 doc/ftpd.8           /usr/man/man8/ftpd.8
install -c -o bin -g bin -m 444 doc/ftpshut.8        /usr/man/man8/ftpshut.8
install -c -o bin -g bin -m 444 doc/ftprestart.8     /usr/man/man8/ftprestart.8
install -c -o bin -g bin -m 444 util/privatepw/privatepw.8     /usr/man/man8/privatepw

Open for edit (as root) file /etc/init.d/wu-ftpd. The difference between versions is that previously, there were several binaries, each performing dedicated task (daemon itself, stopping daemon, restarting etc. - see above log). In 2.6.2, there is only one binary (at the begging of /etc/init.d/wu-ftpd): DAEMON=/usr/sbin/wu-ftpd. Fastest, but least elegant solution is to substitute paths from newer to older in that file:

#DAEMON=/usr/sbin/wu-ftpd
DAEMON=/home/polishcode/wu-ftpd-2.6.0/bin/ftpd

We are practically there. Ladies and gentleman, may I introduce the format string vulnerability itself:

polishcode@U-10043-386:~$ telnet localhost 21
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 U-10043-386 FTP server (Version wu-2.6.0(1) Sun Sep 18 14:02:50 CEST 2011) ready.
USER polishcode
331 Password required for polishcode.
PASS dupa
230 User polishcode logged in.
site exec %x %x %x %x %x %x %x %x
200-0 806b431 806d2cd 8354ea0 0 0 0 0
200  (end of '%x %x %x %x %x %x %x %x')
site index %x %x %x %x %x %x %x %x
200-index 0 806b431 806d2cd bf8b85ac 0 0 0 0
200  (end of 'index %x %x %x %x %x %x %x %x')
quit
221-You have transferred 0 bytes in 0 files.
221-Total traffic for this session was 466 bytes in 0 transfers.
221-Thank you for using the FTP service on U-10043-386.
221 Goodbye.
Connection closed by foreign host.

Of course, performing simple path substitution in /etc/init.d/wu-ftpd introduces inability to stop/restart the daemon, but hope you may figure out this one by yourselves.

Happy learning everyone!

1 comment:

水育文 said...

Thank you for your sharing. I was in the same situation when i installed wu-ftpd-2.6.0, and your blog helped me resolve most of the compiling errors. But now my trouble is that after i substitute the paths from newer to older, wu-ftpd-2.6.0 could not run normally, the situation is: when "ftp localhost", after i input the username, the program does not respond. Do you have any advice? Looking forward your reply sincerely.