Skip to content

Commit

Permalink
Improved ASN.1 parsing for Keberos. Fixes ntop#1492. (ntop#1497)
Browse files Browse the repository at this point in the history
* This is a quick fix, the Kerberos protocol dissector requires some refactoring effort.

Signed-off-by: Toni Uhlig <[email protected]>
Signed-off-by: lns <[email protected]>
  • Loading branch information
utoni authored Apr 10, 2022
1 parent 713cdd0 commit e3faecf
Show file tree
Hide file tree
Showing 3 changed files with 317 additions and 40 deletions.
336 changes: 296 additions & 40 deletions src/lib/protocols/kerberos.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,292 @@ static int ndpi_search_kerberos_extra(struct ndpi_detection_module_struct *ndpi_
struct ndpi_flow_struct *flow);


/* Reference: https://en.wikipedia.org/wiki/X.690#Length_octets */
static int krb_decode_asn1_length(struct ndpi_detection_module_struct *ndpi_struct,
size_t * const kasn1_offset)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;
unsigned char length_octet;
int length;

length_octet = packet->payload[*kasn1_offset];

if (length_octet == 0xFF)
{
/* Malformed Packet */
return -1;
}

if ((length_octet & 0x80) == 0)
{
/* Definite, short */
length = length_octet & 0x7F;
(*kasn1_offset)++;
} else {
/* Definite, long or indefinite (not support by this implementation) */
if ((length_octet & 0x7F) == 0)
{
/* indefinite, unsupported */
return -1;
}

length_octet &= 0x7F;
if (length_octet > 4 /* We support only 4 additional length octets. */ ||
packet->payload_packet_len <= *kasn1_offset + length_octet + 1)
{
return -1;
}

int i = 1;
length = 0;
for (; i <= length_octet; ++i)
{
length |= packet->payload[*kasn1_offset + i] << (length_octet - i) * 8;
}
*kasn1_offset += i;
}

if (packet->payload_packet_len < *kasn1_offset + length)
{
return -1;
}

return length;
}

/* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */
static int krb_decode_asn1_sequence_type(struct ndpi_detection_module_struct *ndpi_struct,
size_t * const kasn1_offset)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;

if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ ||
packet->payload[*kasn1_offset] != 0x30 /* Universal Constructed Tag Type: Sequence */)
{
return -1;
}

(*kasn1_offset)++;

return krb_decode_asn1_length(ndpi_struct, kasn1_offset);
}

/* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */
static int krb_decode_asn1_string_type(struct ndpi_detection_module_struct *ndpi_struct,
size_t * const kasn1_offset,
char const ** const out)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;
int length;

if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ ||
(packet->payload[*kasn1_offset] != 0xA3 /* Context-specific Constructed Tag Type: Bit String */ &&
packet->payload[*kasn1_offset] != 0xA4 /* Context-specific Constructed Tag Type: Octect String */ &&
packet->payload[*kasn1_offset] != 0x30 /* Sequence Of */))
{
return -1;
}

(*kasn1_offset)++;

length = krb_decode_asn1_length(ndpi_struct, kasn1_offset);
if (length <= 0)
{
return -1;
}

if (out != NULL)
{
*out = (char *)&packet->payload[*kasn1_offset];
}

return length;
}

/* Reference: https://en.wikipedia.org/wiki/X.690#Identifier_octets */
static int krb_decode_asn1_int_type(struct ndpi_detection_module_struct *ndpi_struct,
size_t * const kasn1_offset,
int * const out)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;
int length;

if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ ||
packet->payload[*kasn1_offset] != 0x02)
{
return -1;
}

(*kasn1_offset)++;

length = krb_decode_asn1_length(ndpi_struct, kasn1_offset);
if (length <= 0 || length > 4)
{
return -1;
}

if (out != NULL)
{
int i = 0;
*out = 0;
for (; i < length; ++i)
{
*out |= packet->payload[*kasn1_offset + i] << (length - i - 1) * 8;
}
*kasn1_offset += i;
}

return length;
}

/* Tags in which we are not interested. */
static int krb_decode_asn1_blocks_skip(struct ndpi_detection_module_struct *ndpi_struct,
size_t * const kasn1_offset)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;
int length;

if (packet->payload_packet_len <= *kasn1_offset + 1 /* length octet */ ||
(packet->payload[*kasn1_offset] != 0xA0 /* Constructed Context-specific NULL */ &&
packet->payload[*kasn1_offset] != 0xA1 /* Constructed Context-specific BOOLEAN */ &&
packet->payload[*kasn1_offset] != 0xA2 /* Constructed Context-specific INTEGER */))
{
return -1;
}

(*kasn1_offset)++;

length = krb_decode_asn1_length(ndpi_struct, kasn1_offset);
if (length < 0)
{
return -1;
}

return length;
}

static void strncpy_lower(char * const dst, size_t dst_siz,
char const * const src, size_t src_siz)
{
int dst_len = ndpi_min(src_siz, dst_siz - 1);

strncpy(dst, src, dst_len);
dst[dst_len] = '\0';

for(int i = 0; i < dst_len; ++i)
{
dst[i] = tolower(dst[i]);
}
}

/* Reference: https://datatracker.ietf.org/doc/html/rfc4120 */
static int krb_parse(struct ndpi_detection_module_struct * const ndpi_struct,
struct ndpi_flow_struct * const flow,
size_t payload_offset)
{
size_t kasn1_offset = payload_offset;
int length, krb_version, msg_type;
char const * text;

length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_int_type(ndpi_struct, &kasn1_offset, &krb_version); /* pvno */
if (length != 1 || krb_version != 5)
{
return -1;
}

length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_int_type(ndpi_struct, &kasn1_offset, &msg_type); /* msg-type */
if (length != 1 || msg_type != 0x0d /* TGS-REP */)
{
return -1;
}

krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset);

length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset); /* Optional PADATA */
if (length > 0)
{
kasn1_offset += length;
}

length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, &text);
if (length < 0)
{
return -1;
}

kasn1_offset += length;
text += 2;
length -= 2;
if (flow->protos.kerberos.domain[0] == '\0')
{
strncpy_lower(flow->protos.kerberos.domain, sizeof(flow->protos.kerberos.domain),
text, length);
}

length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, NULL);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_sequence_type(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}
kasn1_offset += length;

length = krb_decode_asn1_blocks_skip(ndpi_struct, &kasn1_offset);
if (length < 0)
{
return -1;
}

length = krb_decode_asn1_string_type(ndpi_struct, &kasn1_offset, &text);
if (length < 0)
{
return -1;
}

kasn1_offset += length;
text += 2;
length -= 2;
if (flow->protos.kerberos.hostname[0] == '\0' && text[length - 1] != '$')
{
strncpy_lower(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname),
text, length);
} else if (flow->protos.kerberos.username[0] == '\0') {
strncpy_lower(flow->protos.kerberos.username, sizeof(flow->protos.kerberos.username),
text, length - 1);
}

return 0;
}

static void ndpi_int_kerberos_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_KERBEROS, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
Expand Down Expand Up @@ -395,48 +681,18 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct,

return;
} else if(msg_type == 0x0d) /* TGS-REP */ {
u_int16_t pad_data_len, cname_offset;

#ifdef KERBEROS_DEBUG
printf("[Kerberos] Processing TGS-REP\n");
#endif

koffsetp = koffset + 4;
pad_data_len = packet->payload[koffsetp];
/* Skip realm already filled in request */
cname_offset = pad_data_len + koffsetp + 15;
NDPI_LOG_DBG(ndpi_struct, "[Kerberos] Processing TGS-REP\n");

if(cname_offset < packet->payload_packet_len) {
u_int8_t cname_len = packet->payload[cname_offset];

if((cname_offset+cname_offset) < packet->payload_packet_len) {
char cname_str[48];

if(cname_len > sizeof(cname_str)-1)
cname_len = sizeof(cname_str)-1;

strncpy(cname_str, (char*)&packet->payload[cname_offset+1], cname_len);
cname_str[cname_len] = '\0';
for(i=0; i<cname_len; i++) cname_str[i] = tolower(cname_str[i]);

#ifdef KERBEROS_DEBUG
printf("[TGS-REP][s/dport: %u/%u][Kerberos Cname][len: %u][%s]\n",
sport, dport, cname_len, cname_str);
#endif

if(cname_len && cname_str[cname_len-1] == '$') {
cname_str[cname_len-1] = '\0';
snprintf(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname), "%s", cname_str);
} else {
snprintf(flow->protos.kerberos.username, sizeof(flow->protos.kerberos.username), "%s", cname_str);
}

#ifdef KERBEROS_DEBUG
printf("[TGS-REP] Found everything. disabling extra func\n");
#endif
flow->extra_packets_func = NULL;
}
if (krb_parse(ndpi_struct, flow, 8) != 0)
{
NDPI_LOG_ERR(ndpi_struct, "[TGS-REP] Invalid packet received\n");
return;
}
NDPI_LOG_DBG(ndpi_struct,
"[TGS-REP][s/dport: %u/%u][Kerberos Hostname,Domain,Username][%s,%s,%s]\n",
sport, dport, flow->protos.kerberos.hostname, flow->protos.kerberos.domain,
flow->protos.kerberos.username);
flow->extra_packets_func = NULL;
}

return;
Expand Down
Binary file added tests/pcap/kerberos-login.pcap
Binary file not shown.
21 changes: 21 additions & 0 deletions tests/result/kerberos-login.pcap.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Guessed flow protos: 0

DPI Packets (TCP): 11 (11.00 pkts/flow)
DPI Packets (UDP): 12 (1.00 pkts/flow)
Confidence DPI : 13 (flows)

Kerberos 39 37272 13

1 TCP 192.168.10.12:44256 <-> 192.168.10.3:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][9 pkts/3720 bytes <-> 6 pkts/3520 bytes][Goodput ratio: 84/88][0.00 sec][testbed1.ca\ubuntu64a][bytes ratio: 0.028 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 0/0 0/0 0/0][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 413/587 1621/1620 646/731][PLAIN TEXT (TESTBED)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100]
2 UDP 10.1.12.2:1074 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1275 bytes <-> 1 pkts/1279 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0]
3 UDP 10.1.12.2:1092 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1277 bytes <-> 1 pkts/1270 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0]
4 UDP 10.1.12.2:1067 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1261 bytes <-> 1 pkts/1247 bytes][Goodput ratio: 97/97][0.04 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0]
5 UDP 10.1.12.2:1076 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1261 bytes <-> 1 pkts/1247 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0]
6 UDP 10.1.12.2:1089 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1263 bytes <-> 1 pkts/1244 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0]
7 UDP 10.1.12.2:1096 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1263 bytes <-> 1 pkts/1244 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0]
8 UDP 10.1.12.2:1065 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1265 bytes <-> 1 pkts/1234 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0]
9 UDP 10.1.12.2:1061 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1253 bytes <-> 1 pkts/1231 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0]
10 UDP 10.1.12.2:1084 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1255 bytes <-> 1 pkts/1228 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0]
11 UDP 10.1.12.2:1068 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1251 bytes <-> 1 pkts/1229 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0]
12 UDP 10.1.12.2:1069 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1250 bytes <-> 1 pkts/1228 bytes][Goodput ratio: 97/97][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0]
13 UDP 10.1.12.2:1090 <-> 10.5.3.1:88 [proto: 111/Kerberos][ClearText][Confidence: DPI][cat: Network/14][1 pkts/1253 bytes <-> 1 pkts/1224 bytes][Goodput ratio: 97/96][< 1 sec][denydc.com][PLAIN TEXT (DENYDC.COM)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,0,0,0,0,0,0,0,0,0,0]

0 comments on commit e3faecf

Please sign in to comment.