Skip to content

Commit 1d7b0d9

Browse files
inashivbvictorjulien
authored andcommitted
tls/subjectaltname: use byte arr instead of string
TLS parsers use x509-parser crate which parses X.509 certificates that use ASN.1 DER encoding that can allow arbitrary byte sequences. An attacker could inject null byte in a certificate anywhere to stump the common language parsers terminating the string at a null byte leading to a bypass of a possibly malicious certificate. So far, the rust TLS parser for "subjectaltname" used a pattern that involved: -> Get ASN.1 DER encoded raw data from the x509-parser crate -> Convert this raw data to a decoded string (Rust) -> Convert the Rust string to CString -- The problem lies here. CString only accepts proper strings/byte buffers and converts it into an owned C-compatible, null-terminated string. However, if any null byte occurs in the string passed to the CString then it panics. In the rust TLS parser, this panic is handled by returning NULL. This means that the parser will error out during the decoding of the certificate. However, Suricata must be able to detect the null byte injection attack being an IDS/IPS. Hence, replace all such string patterns w.r.t. TLS SAN with a byte array. Bug 7887
1 parent 9c3ed7d commit 1d7b0d9

6 files changed

Lines changed: 30 additions & 23 deletions

File tree

rust/src/x509/mod.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919
2020
// written by Pierre Chifflier <chifflier@wzdftpd.net>
2121

22-
use crate::common::rust_string_to_c;
2322
use nom7::Err;
2423
use std;
25-
use std::os::raw::c_char;
2624
use std::fmt;
2725
use x509_parser::prelude::*;
2826
use crate::x509::GeneralName;
@@ -115,18 +113,21 @@ pub unsafe extern "C" fn SCX509GetSubjectAltNameLen(ptr: *const X509) -> u16 {
115113
}
116114

117115
#[no_mangle]
118-
pub unsafe extern "C" fn SCX509GetSubjectAltNameAt(ptr: *const X509, idx: u16) -> *mut c_char {
116+
pub unsafe extern "C" fn SCX509GetSubjectAltNameAt(ptr: *const X509, idx: u16, san: *mut *mut u8, san_len: *mut u32) {
119117
if ptr.is_null() {
120-
return std::ptr::null_mut();
118+
*san_len = 0;
119+
*san = std::ptr::null_mut();
120+
return;
121121
}
122122
let x509 = cast_pointer! {ptr, X509};
123123
let san_list = x509.0.tbs_certificate.subject_alternative_name();
124124
if let Ok(Some(sans)) = san_list {
125125
let general_name = &sans.value.general_names[idx as usize];
126126
let dns_name = SCGeneralName(general_name);
127-
return rust_string_to_c(dns_name.to_string());
127+
let dn = dns_name.to_string().into_bytes();
128+
*san_len = dn.len() as u32;
129+
*san = Box::into_raw(dn.into_boxed_slice()) as *mut u8;
128130
}
129-
return std::ptr::null_mut();
130131
}
131132

132133
#[no_mangle]

src/app-layer-ssl.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -502,15 +502,15 @@ static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
502502
goto error;
503503
}
504504

505-
connp->cert0_sans_len = SCX509GetSubjectAltNameLen(x509);
506-
char **sans = SCCalloc(connp->cert0_sans_len, sizeof(char *));
507-
if (sans == NULL) {
505+
connp->cert0_sans_num = SCX509GetSubjectAltNameLen(x509);
506+
connp->cert0_sans = SCCalloc(connp->cert0_sans_num, sizeof(SSLSubjectAltName));
507+
if (connp->cert0_sans == NULL) {
508508
goto error;
509509
}
510-
for (uint16_t i = 0; i < connp->cert0_sans_len; i++) {
511-
sans[i] = SCX509GetSubjectAltNameAt(x509, i);
510+
for (uint16_t i = 0; i < connp->cert0_sans_num; i++) {
511+
SCX509GetSubjectAltNameAt(
512+
x509, i, &connp->cert0_sans[i].san, &connp->cert0_sans[i].san_len);
512513
}
513-
connp->cert0_sans = sans;
514514

515515
SCX509GetSerial(x509, &connp->cert0_serial, &connp->cert0_serial_len);
516516
if (connp->cert0_serial == NULL) {
@@ -2834,8 +2834,8 @@ static void *SSLStateAlloc(void *orig_state, AppProto proto_orig)
28342834
static void SSLStateCertSANFree(SSLStateConnp *connp)
28352835
{
28362836
if (connp->cert0_sans) {
2837-
for (uint16_t i = 0; i < connp->cert0_sans_len; i++) {
2838-
SCRustCStringFree(connp->cert0_sans[i]);
2837+
for (uint16_t i = 0; i < connp->cert0_sans_num; i++) {
2838+
SCX509ArrayFree(connp->cert0_sans[i].san, connp->cert0_sans[i].san_len);
28392839
}
28402840
SCFree(connp->cert0_sans);
28412841
}

src/app-layer-ssl.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ typedef struct SSLCertsChain_ {
164164
TAILQ_ENTRY(SSLCertsChain_) next;
165165
} SSLCertsChain;
166166

167+
typedef struct SSLSubjectAltName_ {
168+
uint8_t *san;
169+
uint32_t san_len;
170+
} SSLSubjectAltName;
171+
167172
typedef struct SSLStateConnp_ {
168173
/* record length */
169174
uint32_t record_length;
@@ -194,8 +199,8 @@ typedef struct SSLStateConnp_ {
194199
int64_t cert0_not_after;
195200
char *cert0_fingerprint;
196201

197-
char **cert0_sans;
198-
uint16_t cert0_sans_len;
202+
SSLSubjectAltName *cert0_sans;
203+
uint16_t cert0_sans_num;
199204
/* ssl server name indication extension */
200205
uint8_t *sni;
201206
uint16_t sni_len;

src/detect-tls-subjectaltname.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ static bool TlsSubjectAltNameGetData(DetectEngineThreadCtx *det_ctx, const void
6363

6464
connp = &ssl_state->server_connp;
6565

66-
if (idx >= connp->cert0_sans_len) {
66+
if (idx >= connp->cert0_sans_num) {
6767
return false;
6868
}
6969

70-
*buf = (const uint8_t *)connp->cert0_sans[idx];
70+
*buf = (const uint8_t *)connp->cert0_sans[idx].san;
7171
if (*buf) {
72-
*buf_len = (uint32_t)strlen(connp->cert0_sans[idx]);
72+
*buf_len = connp->cert0_sans[idx].san_len;
7373
} else {
7474
// happens if the altname had a zero character in it
7575
*buf_len = 0;

src/detect-tls.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (C) 2011-2012 ANSSI
3-
* Copyright (C) 2022 Open Information Security Foundation
3+
* Copyright (C) 2022-2025 Open Information Security Foundation
44
* All rights reserved.
55
*
66
* Redistribution and use in source and binary forms, with or without

src/output-json-tls.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,11 @@ static void JsonTlsLogIssuer(SCJsonBuilder *js, SSLState *ssl_state)
155155

156156
static void JsonTlsLogSAN(SCJsonBuilder *js, SSLState *ssl_state)
157157
{
158-
if (ssl_state->server_connp.cert0_sans_len > 0) {
158+
if (ssl_state->server_connp.cert0_sans_num > 0) {
159159
SCJbOpenArray(js, "subjectaltname");
160-
for (uint16_t i = 0; i < ssl_state->server_connp.cert0_sans_len; i++) {
161-
SCJbAppendString(js, ssl_state->server_connp.cert0_sans[i]);
160+
for (uint16_t i = 0; i < ssl_state->server_connp.cert0_sans_num; i++) {
161+
SCJbAppendStringFromBytes(js, ssl_state->server_connp.cert0_sans[i].san,
162+
ssl_state->server_connp.cert0_sans[i].san_len);
162163
}
163164
SCJbClose(js);
164165
}

0 commit comments

Comments
 (0)