LightFTP icon indicating copy to clipboard operation
LightFTP copied to clipboard

LightFTP: Blocking active data connect with stale PORT params leads to DoS

Open yuyz-cyber opened this issue 1 month ago • 0 comments

Affected Versions: All tagged releases confirmed: v1.0, v1.1, v2.0, v2.1, v2.2, v2.3, v2.3.1 (latest). Deprecated Windows branch shares the same logic.

Vulnerability Type Denial of Service / Resource Exhaustion

Description: ftpPORT() returns error on address mismatch but leaves previous data_ipv4/data_port intact. create_datasocket() performs blocking connect() without timeout in active mode. If the stale address/port is unreachable, the worker thread blocks until OS-level timeout (≈75s+). While blocked, WorkerThreadValid remains 0; subsequent LIST/RETR/STOR commands for that session receive 550 “Another action is in progress”. Multiple concurrent sessions can exhaust MaxUsers, degrading service availability.

Code analysis:

  1. PORT command verification fails and does not reset the status(367:405:LightFTP/src/ftpserv.c) ssize_t ftpPORT(pftp_context context, const char *params) { int c; in_addr_t data_ipv4 = 0, data_port = 0; char *p = (char *)params; // ... if ( data_ipv4 != context->client_ipv4 ) return sendstring(context, error501);// Authentication fails but does not reset data_ipv4/data_port

    context->data_ipv4 = data_ipv4;// Only update after verification context->data_port = (in_port_t)data_port; context->mode = MODE_NORMAL;

    return sendstring(context, success200); }

Problem: When the PORT command verification fails (such as IP mismatch), the function returns an error, but data_ipv4 and data_port will not be updated or reset. These values ​​​​may retain the previous state (may be a valid value or an invalid value)

  1. connect() call blocks and has no timeout.(113:155:LightFTP/src/ftpserv.c) SOCKET create_datasocket(pftp_context context) { SOCKET client_socket = INVALID_SOCKET; struct sockaddr_in laddr; socklen_t asz;

    memset(&laddr, 0, sizeof(laddr));

    switch ( context->mode ) { case MODE_NORMAL: client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); context->data_socket = client_socket; if ( client_socket == INVALID_SOCKET ) return INVALID_SOCKET;

     laddr.sin_family = AF_INET;
     laddr.sin_port = context->data_port;
     laddr.sin_addr.s_addr = context->data_ipv4;
     // blocking connect, no timeout setting
     if ( connect(client_socket, (const struct sockaddr *)&laddr, sizeof(laddr)) == -1 ) {
         close(client_socket);
         return INVALID_SOCKET;
     }
     break;
    

    // ... } default: return INVALID_SOCKET; } return client_socket; }

Problem: The connect() call is blocking and the socket timeout option is not set; if data_ipv4 and data_port point to an unreachable address, connect will block until the system times out. The system default connect timeout is usually 75 seconds or longer.

  1. Worker thread blocking causes function rejection(601:630:LightFTP/src/ftpserv.c) ssize_t ftpLIST(pftp_context context, const char *params) { //... if ((context->worker_thread_valid == 0) || (context->file_fd != -1)) return sendstring(context, error550_t);// "Another action is in progress" //... }

Problem: If the worker thread is blocked in create_datasocket() and WorkerThreadValid remains 0, the new LIST/RETR/STOR command will be rejected (returning 550 error) and the client cannot use the data connection function until the worker thread times out.

POC:

  1. Login.
  2. Send valid PORT to an unreachable port on client IP (e.g., 127.0.0.1:65535).
  3. Send invalid PORT with mismatched IP (gets 501, but stale DataIP/Port kept).
  4. Send LIST/RETR/STOR → server blocks on connect(), data commands denied for duration.

Impact

  • Single session: data-channel commands unusable for the OS timeout window.
  • Multi-session: attackers can occupy user slots/threads, leading to service degradation or DoS.

Fix

  • Add connect timeouts (non-blocking connect + select/poll, or SO_SNDTIMEO/SO_RCVTIMEO).
  • On PORT validation failure, zero data_ipv4/data_port before returning an error.
  • Optional: cap retries/idle time for active connects.

yuyz-cyber avatar Dec 10 '25 02:12 yuyz-cyber