/* * ftp_vulnerable.c — Intentionally Vulnerable FTP Server * ======================================================== * FOR EDUCATIONAL USE ONLY. Run inside a VM or Docker container. */ #include #include #include #include #include #define PORT 2121 #define BUFFER_SIZE 512 #define USERNAME "admin" #define PASSWORD "password" /* * __attribute__((noinline)) forces GCC to NEVER inline this function. * Without it, the compiler may merge it into handle_client() at higher * optimization levels, pushing saved RIP out of reach again. */ __attribute__((noinline)) void parse_stor_filename(char *buffer) { char filename[32]; /* only local — RIP ~40 bytes away */ sscanf(buffer, "STOR %s", filename); /* no bounds check → overflow */ /* Intentionally crash BEFORE sending any response so the crash is obvious */ } void handle_client(int client_sock) { char buffer[BUFFER_SIZE]; char username[32]; char password[32]; send(client_sock, "220 Welcome to Vulnerable FTP Server\r\n", 37, 0); recv(client_sock, buffer, BUFFER_SIZE, 0); if (strncmp(buffer, "USER ", 5) == 0) { sscanf(buffer, "USER %s", username); send(client_sock, "331 Please specify the password.\r\n", 34, 0); } else { send(client_sock, "530 Login incorrect.\r\n", 22, 0); close(client_sock); return; } recv(client_sock, buffer, BUFFER_SIZE, 0); if (strncmp(buffer, "PASS ", 5) == 0) { sscanf(buffer, "PASS %s", password); if (strcmp(username, USERNAME) == 0 && strcmp(password, PASSWORD) == 0) { send(client_sock, "230 Login successful.\r\n", 23, 0); } else { send(client_sock, "530 Login incorrect.\r\n", 22, 0); close(client_sock); return; } } else { send(client_sock, "530 Login incorrect.\r\n", 22, 0); close(client_sock); return; } while (1) { memset(buffer, 0, BUFFER_SIZE); int bytes_received = recv(client_sock, buffer, BUFFER_SIZE, 0); if (bytes_received <= 0) break; if (strncmp(buffer, "STOR ", 5) == 0) { /* Acknowledge receipt, THEN trigger the overflow */ send(client_sock, "125 Accepted, processing filename...\r\n", 38, 0); /* ===== BUFFER OVERFLOW HERE — server crashes on return ===== */ parse_stor_filename(buffer); /* Code below is NEVER reached when overflow hits saved RIP */ send(client_sock, "226 Transfer complete.\r\n", 24, 0); } else if (strncmp(buffer, "QUIT", 4) == 0) { send(client_sock, "221 Goodbye.\r\n", 14, 0); break; } else { send(client_sock, "502 Command not implemented.\r\n", 30, 0); } } close(client_sock); } int main() { int server_sock, client_sock; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_len = sizeof(client_addr); server_sock = socket(AF_INET, SOCK_STREAM, 0); if (server_sock < 0) { perror("socket"); exit(1); } int opt = 1; setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind"); exit(1); } listen(server_sock, 5); printf("Vulnerable FTP Server on port %d\n", PORT); printf("WARNING: intentionally vulnerable — lab use only\n"); while (1) { client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len); if (client_sock < 0) { perror("accept"); continue; } handle_client(client_sock); } close(server_sock); return 0; }