- -------------------------------------------------------------------------- De_aap security advisory 1 December 20th, 2004 - -------------------------------------------------------------------------- Package : rftpd 2 and rpf 1.2.2 Vulnerability : buffer overflows, race conditions, integer overflows, logic bugs and much much more Problem-Type : local and remote CVE ID : No CVE ID's are assigned Bugs found by : most by de_aap, some found by Slotto Corleone and percy, vlad902 Previous remote root vulnerabilities in software from Rosiello.org "security research team" (http://lists.netsys.com/pipermail/full-disclosure/2004-April/020690.html) 1.) Gaping security holes in rftpd 2: some background info from their site (rave.swehack.se): ******************************************************* "Do you know that many ftp daemons are released with NULL support? rFTPD is not like that at all, your experience will build the software!" Except that some of the bugs presented in this advisory have been disclosed to the rftpd development team months ago, still no fix !! ... rFTPD offers: * Download at high speed // yea right * Fantastic logging // hellyea, and it also offers 'pre-auth' remote root * Unique signal handling // can you say signal race condition ? oh, and you can't catch SIGKILL you dummy * Restore your download after the client disconnection * Very easy configuration * Multiple languages are available such as English, dutch, norwegian, swedish, italian. // all with crappy spelling * EPRT/EPASV full working ... "But why?, well we are already better faster and more secure then a lot other comercial products that are around a long time now." more secure ? excuse me ? we found more security holes in rftpd then there are in wu-ftpd for fucks sake ! It's not fast either, ever heard of a syscall named sendfile() ? ... Current release (rftpd-secprec2-linux.tar.gz): * Some features may not be included yet. // like actually authenticating someone :) * Waiting for tkn people to rate the software. ... "If you plan to use the ftpd for hosting purposes, remind to contact us and tell about your experience with the daemon, please. This could be crucial for the existance of the server daemon because your knowledge is our concern." please contact us aswell, we'd love to own you. ... If you think you can do anything for this project such as: ... * Reporting bugs // will do ... Contact us by: irc.rosiello.net #rosiello rftpd@rosiello.org from their forum (on source forge) : ************************************ "By: Nobody/Anonymous - nobody RE: Compliments 2004-10-29 13:33 it's a great ftp and i use it for all my customers. they all like it and we havent had any remote root exploits since then! very secure and fast" verry funny, and almost at leet time :) The gaping security holes: ************************** 1.) injection terminal escape codes into the terminal. rFTPd is ran by root, and by default doesn't fork in the background. This is done so that root can see all sorts of debug messages. When processing the following commands (there are probably more) it doesn't filter any characters, allowing a potential attacker to inject any terminal escape codes. Because this also affects the USER command this can be seen as a pre-auth remote root bug. commands: USER, STOR, NLST, LIST, APPE, PASV, MLST, MLSD, MKD, RMD, DELE, RNFR, RNTO 2.) Authentication screwup. The authentication routine is close to empty. Only validating usernames. This means that if a correct username is given an attacker can just log in. No matter what password he enters. There is some default protection. the root user is not allowed to login by default. This check is not sufficiant however. as it checks for the user 'root'. and not for uid 0. If another uid 0 account is present on the box (let's say toor) then one is allowed to login with that user. Therefor we can say that this is a pre-auth remote root bug. and that everything else described below can be considered semi pre-auth remote root because all an attacker needs is a valid login. 3.) Info leak Due to the way arguments are treated when the command itself get's translated in all uppercase characters there exists an info leak. This was tested with the MKD and USER command. If the second parameter is big enough (> 128) it is possible to leak memory from. In some cases it is even possible to crash the current session with this. We haven't look at this any further but is is likely that an attacker can get a rootshell from this. (The crash was triggered only with the USER command) 4.) buffer overflow in debug routines Most commands log debug messages by default. There exists a bufferoverflow in the debug routine: #define MAX_LOG 225 void Log_debug ( int preority, char *fmt, ...) { va_list args; char buffer[MAX_LOG]; char *ptr; fs_zero(buffer,MAX_LOG); va_start ( args,fmt); ptr = buffer; vsprintf(ptr,fmt,args); ... } This speaks louder then words. Exploitation is trivial. We made a proof of concept exploit at our lab to prove exploitability. We are however not going to release our exploit. This can be considered a semi pre-auth remote root bug. 5.) NULL pointer dereference There exists a NULL pointer dereference in the NLST command, when used with no argument. This can be used to crash the current session. 6.) Buffer overflow in filter_port() Filter port is a procedure called from do_port() which is the procedure that gets called when the port command is given. The most funny part about this overflow is that there is a comment stating that one should watch out for bufferoverflows: int filter_port(const char *portcommand,const char *ip_addr, const void *prt) { int pos[10] = { 0 , 0 , 0 ,0 ,0 }; int inc; int i; char tmp[128]; int left; int right; char port[1]; long port_c; /* Always fear buffer underruns/overflows and Check length */ if ((strlen(portcommand)) <= 3) return -1; if ( !portcommand[0] && ((portcommand[strlen(portcommand)-2]) != 0xd) && ((portcommand[strlen(portcommand)-2]) != 0xd)) return -1; strcpy(tmp,portcommand); <--- THIS IS WHERE IT ALL HAPPENS ! Exploitation is trivial and this can be considered a semi pre-auth remote root bug. 7.) Another bufferoverflow in filter_port If the argument given to filter_port isn't long enough to trigger the first bufferoverflow, and contains more then 10 ','s then one can overflow the pos array: ... for (i= 0; i < strlen(portcommand); ++i) if (((char)portcommand[i]) == ',') { pos[inc] = i; inc++ ; } ... Exploitation is not so easy. We at de_aap security research believe that this can be exploited, given an attacker that is clever enough. 8.) Third bufferoverflow in filter_port When filter_port is called at do_port it's second argument is a local character array of size 128. This can be overflowed later in filter_port: int do_port ( void ) { char ip[128]; ... filter_port(u_com,ip,&con->usr_rel.req_port); ... } int filter_port(const char *portcommand,const char *ip_addr, const void *prt) { ... char tmp[128]; ... strcpy((char *)ip_addr,tmp); ... } Exploitation is trivial and is also considered a semi pre-auth remote root. Also note the wrong usage of 'const' in filter_port. 9.) Bufferoverflow in motd parsing When parsing the motd file a bufferoverflow can happen if there are more then MAX_READ + 3 characters on a single line: #define MAX_READ 255 void do_welcome ( int socket ) { char message[MAX_READ+3]; ... FILE *fp; ... if ( config ->motdfile ) { fp = fopen(config->motdfile, "r"); if ( !fp) { send_data(MSG_WELCOME,socket); return ;} while((feof(fp)) == 0){ fgets(message,500,fp); ... } ... } ... } Exploitation is only usefull when an attacker controls the motd file. When this is the case exploitation is trivial. 10.) do_list info leak. There exists a potential information leak in do_list due to not 0terminating a string after strncpy(): void do_list ( void ) { char dir[MAX_PATH+3]; ... if (!dir[0]) strncpy(dir,con->usr_rel.PWD,MAX_PATH); ... } 11.) do_nlst info leak. An info leak very simular as the one described in 10 is present in do_nlst: void do_nlst ( void ) { char dir[MAX_PATH+3]; ... if (!dir[0]) strncpy(dir,con->usr_rel.PWD,MAX_PATH); ... } 12.) bufferoverflow in PAD There is a potential overflow in the PAD procedure: char padstring[512]; static char *PAD(char *string,size_t size) { fs_zero (padstring,sizeof(padstring)); if ((size >= sizeof(padstring)) || (sizeof(string) >= size) ) return (char *)string; memset (padstring,0x20,size); memcpy(padstring,string,strlen(string)); return (char *)padstring; } The problem occurs because sizeof() was used on a pointer. Exploitation is somewhat difficult, depending on the compiler used. This is because an attacker can only overflow a buffer on the .bss. 13.) Potential buffer overflow in do_ascii void do_ascii ( char *msg,const char *target,size_t max) { int o; char *ptr; ptr = (char *)target; o=0; while ( o <= (size_t) strnlen(msg,max) ) { if (msg[o] == '\n' ) *ptr ++ = '\r'; *ptr ++= msg[o]; o++; } It is obious that if msg < target and target contains enough '\n' characters that an overflow occurs. The bug in question can however not be exploited due to the programmer not calling his own procedure the way he intended to. 14.) Funny logic bug in s_rename There exists a logic bug in s_rename. A very strange condition in an if statement that can never be true: int s_rename (const char *src,const char *org_name,char *newname) { int i; if ( (strlen( src) < 5) && (strlen (src) > 128) ) return -1; ... } because something cannot be smaller then 5 AND bigger then 128 this is a void statement. Later bufferoverflows are possible. 15.) Off-by-one underflow in do_append: There exists an off-by-one underflow in the appending of files if an unexpected error is returned by read(): do_append (char *filename) { ... do { numbytes +=strlen((char *)strip_nl(buffer)); buffer[inc]='\0'; write (fin,buffer,inc); memset (buffer,'\0',MAX_READ); } while ( (inc = read((int)con->usr_rel.req_sock,buffer,MAX_READ)) !=0 ); ... } 16.) Endless loop in get_data char buffer[MAX_READ+3]; char *get_data(struct connection *conn){ int numbytes=0; fs_zero(buffer,MAX_READ); while ( numbytes=read (conn->fd,buffer,MAX_READ) <= 0 ) buffer[numbytes]='\0'; return (char *)buffer; } This while loop here can cause an endless loop when disconnecting while reading. 17.) Deleting and creating any file or directory on the system. The DELE, XMKD and XRMD commands do not check wether a certain user is allowed to delete or create a file or directory. This unfortunate bug caused one of our researcher to loose some of his research data. 18.) Potential information leak in do_eprt int do_eprt ( void ){ char ip[20]; F_EPRT(u_com,ip,&port,&protocol,20); ... } int F_EPRT(const char *src, char *ip, long *port, int *protocol,int MAX_IP) { char **ok; int pos; if ((strlen(src)) <= 5) return -1; src += 5; ... ok = splitcmd ( src, '|' ); if (calc_argc (ok) !=2) return -1; ... strncpy(ip,ok[1],MAX_IP); } since strncpy() might not 0terminate it is possible to cause an information leak in do_eprt. 19.) Logic bug in do_mlst void do_mlst ( char *filename, int soc, int type) { ... if ( (filename[0]=='\0') && (strlen(filename) > 128) ) { send_data ( MSG_NO_SUCH_FD,soc); return ; } ... } The logic bug is located in the if-statement. If the first byte is a 0byte then strlen() cannot return anything else BUT 0. Hence this is a void statement. 20.) Buffer overflow in do_mlst due to a logic bug void do_mlst ( char *filename, int soc, int type) { struct stat status; struct utsname buf; int ret; char complete[512]; ... sprintf( complete, "type=%s;sized=%d;modify=%s;%s.mode=%o;%s.uid=%d;%s.gid=%d %s \r\n", S_ISDIR( status.st_mode ) ? "dir" : "file", status.st_size, mytime ( time((time_t *)status.st_mtime) , FORMAT_MDTM ), me, status.st_mode, me, status.st_uid, me, status.st_gid, filename); ... } Due do the logic bug described earlier the length of filename is allowed to be longer then 128. This might cause a bufferoverflow. 21.) Integer overflows in dirlist dirlist is a procedure inside ls.c it is indeed used to list whatever is in directories. The procedure itself has a few integer values used to hold information about the directory it's supposed to list: int bytes=0; int files=0; int folders=0; int onefile=0; int total=0; Due to no checks whatsoover on any operations on these they can overflow, or become negative (since they are signed). This can cause more mischief later on. 22.) Error in priviledge dropping After logging in the rftpd daemon is supposed to drop all priviledges. This is done with seteuid() instead of setuid(). Hence only the effective userid gets dropped and can be regained with some of the vulnerabilities described earlier. some more funny things about rftpd and it's programmer: ******************************************************* ftpd_core.c:322: time_t start, finish;; dumbass, you dont need 2 semi-colons for 2 variables if you don't use free() then you get memory starvation. you have to admit, rftpd is more secure then sphiro ?? you got that shiz right ! sphiro has even more bugs (see sphiro advisory from slotto) when is the next release of rftpd comming btw ? not soon and you know the current one has major bugs ? yes even though people are using it right now ? just check the source forge forum all those ppl are told about it so they know ?? this is the same guy that stated his ftpd is more secure then others and who is in a security group that releases exploits for sudo ! (that particular exploit also had a few buffer overruns, but we'll save that for another advisory :pPpPpPpPPp) I dont even care what you think of my code but you will see when i release it or parts of it you will not find as mutch or only minior bugs then every project of mine before. ?? rave about his next 'secure' project (an ircd) my reply "We'll see :)" neeh im a trained security engeneer ?? note his misspelling of engineer rftpd is total crap 2.0 is for sure i told you a 10000000 times ?? there you have it folks, on one side it's 'more secure then most other ftpd's out there' and on the other it's total crap. this doesn't make sense, does it ? rftpd is more secure then bulletproof ftpd and raiden ftpd it's not as secure as ftpd's that have been there for a long time, like wu-ftpd .. dustcloth [~dustcloth@mail.bookfeel.com] has joined #rosiello ... Topic (#rosiello): Rosiello Security http://www.rosiello.org | Rosiello Security won the InfoSecWriters contest of July | atm rftpd is being ported to win32 + mysql and PHP support will be added to the project ... Topic (#rosiello): set by rave at Mon Aug 30 14:43:51 2004 ... [Users(#rosiello:14)] [ dustcloth ] [ giles_ ] [ jdag ] [@rave ] [ Anix ] [ anthill ] [ giles__ ] [ zeusfaber_] [ Guest ] [ akriel ] [ nocturnal ] [@angelo ] [ anto^eof ] [ chris ] ... Channel #rosiello was created at Wed Aug 25 09:34:47 2004 ... BitchX: Join to #rosiello was synched in 0.414 secs!! hello hello rave & angelo hellow dust what do you mean php support? php in ftp? we have a user nammager you can use on apache servers as a website for admins like webmin ohhhhhhs nice secure too? yes sure hey ppl I'm waiting for punix to be ready she's getting sme phard :> hi dustcloth dustcloth: hows xar these days i use rftpd on my webhost box to test ... SignOff giles__: #rosiello (Ping timeout) chris, he is ok rftpd will r0x i heard he had sex with a man at defcon though its quite funny if you look who is logged into xar's box theres one username which sticks out what name? dustcloth do u find rftpd ok? angelo, yes it's nice and secure haha you are being bullshitted to hell good, I'm happy to hear it rave did u release the sec-pre 3 ? no that will take a long time i have to code getpwent wrappers and set/get uid wrappers and the sql code and win32 user api`s i need more coders MORE MORE MORE !!! heh bro I suggest you to fix the sec-pre 2 and to release a stable one do u guys run rftpd? then you add new stuffs for the next releases every one sayes yes i want to help out but no one does shit sec-pre 2 is not stable? it hasnt crashed yet dustcloth it is but I mean on sf sf? dustcloth ofcource we dont run our own stuff sorceforge? only for testing why not run your own stuff? rave: release a stable version on sf secpre 2 has a DOS when u use a non existing user ohhh you can crash the auth system but no remote exploits right? no dust this conversation will be on FD all over again why do you let it happen again well the auth thing is known hehe [msg(chris)] u not like me? [chris(~chris@crew.tkn.us)] i hate liars [msg(chris)] liars? [msg(chris)] i not lie [chris(~chris@crew.tkn.us)] k [msg(chris)] am from france ok english not so good [chris(~chris@crew.tkn.us)] it doesnt matter to me, its their loss [chris(~chris@crew.tkn.us)] they are the ones being bullshitted [msg(chris)] loldongs !!!!!!! hahah yes sure we have milions of remote exploits ready for the server and in time we release a bug for stupid ppl like sloth to laugh at and feel good so we entertain the dogs from the drain you ssee sloth is a nice guy :( yeah when he dreams and keeps away from here he is you should just ban him he is oh i wish i knew how to program c so i could help you but only i know scheme if you buy a book you can learn C i can't read books i mean bad eyes then read a Ebook 2.) Gaping security holes in rpf 1.2.2 Some background: **************** rpf stands for rmp finder. According to rave (who thinks he coded it, see log below) It's in the mandrake packages. Our research pointed out that it is not! Funny comments in the code: *************************** // gets(command_line); Just to kill any bad idea The gaping security holes: ************************** 1.) remotely exploitable bufferoverflow When rpf connects to a webserver it uses a procedure web() to do the actual communicatio with the webserver. This procedure however contains a bufferoverflow: char *web(char *url, char *ip) { char ch; struct sockaddr_in server_addr; int PORT, sd, error, buffer, fd, begin; int register i, j; char get[256], memory[1024], cleaner[1024], old[1024], file[64]; char capo = '\n'; ... while(buffer != 0) { buffer = recv(sd, &ch, 1, 0); if(ch == '\n' || ch == ' ') { .... } else if(ch != '\n' && ch != ' ') { memory[i] = ch; i++; } } ... } Exploitation is trivial ! 2.) Local file race condition. in that same web() procedure there is a file made to store some needed data: ... sprintf(file, "/tmp/.rpf-%d", getuid()); fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0644); ... There is however no check done if the file already exists or not. A symlink attack is possible allowing a potential attacker to truncate any file owned by the person running rpf. Afthermath: *********** There are various other overflows in rpf that are not exploitable due to the programmer (angelo) not free()'ing ANY memory as shown by our reseach: de_aap@monkeytown:~/rpf-1.2.2%grep "free *(" * -n -r | wc 0 0 0 de_aap@monkeytown:~/rpf-1.2.2% According to our calculations there are about one gazillion memory leaks in rpf. It also appears that the programmer (angelo) has never heard of memset and insists on clearing memory with ugly for loops. (note to angelo, in case you don't know most memset() implementations are optimized for this kind of stuff and it is advised that you use those.) Some nice ircd logs about this: mine got on mandrake what is in mandrake ? percy the rpm tool to find new updated rpm files THATS ANGELOS TOOL YOU FAGGET stop claiming other peoples achievements greets: - boobys.org - toeters security labs - gobbles (personal hero !) - sorbo (italian maffia) - kokanin (woke up naked hugging his computer) - LOLDONGS.com - GeS - percy (doing it for the puppies since 2004) super-secret hacker greetz: MRX, vect0rx, HexPhile, wormwood, t12 / tk421, MC m00, dyldo, DJ Tino, eating dead baby animals, chickens, kawanza, DJ Jackalope _______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.netsys.com/full-disclosure-charter.html