Skip to content

Commit

Permalink
Improved IPSec/ISAKMP detection. (#1600)
Browse files Browse the repository at this point in the history
Signed-off-by: lns <[email protected]>
  • Loading branch information
utoni committed Jun 16, 2022
1 parent c43360f commit 20a29c3
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/include/ndpi_protocol_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ typedef enum {
NDPI_PROTOCOL_WORLDOFWARCRAFT = 76,
NDPI_PROTOCOL_TELNET = 77,
NDPI_PROTOCOL_STUN = 78,
NDPI_PROTOCOL_IP_IPSEC = 79,
NDPI_PROTOCOL_IPSEC = 79,
NDPI_PROTOCOL_IP_GRE = 80,
NDPI_PROTOCOL_IP_ICMP = 81,
NDPI_PROTOCOL_IP_IGMP = 82,
Expand Down
1 change: 1 addition & 0 deletions src/include/ndpi_protocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ void init_raknet_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_i
void init_xiaomi_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask);
void init_mpegdash_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask);
void init_rsh_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask);
void init_ipsec_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask);

/* ndpi_main.c */
extern u_int32_t ndpi_ip_port_hash_funct(u_int32_t ip, u_int16_t port);
Expand Down
9 changes: 6 additions & 3 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1283,8 +1283,8 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp
"STUN", NDPI_PROTOCOL_CATEGORY_NETWORK,
ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */,
ndpi_build_default_ports(ports_b, 3478, 0, 0, 0, 0) /* UDP */);
ndpi_set_proto_defaults(ndpi_str, 0 /* encrypted */, 0 /* nw proto */, NDPI_PROTOCOL_SAFE, NDPI_PROTOCOL_IP_IPSEC,
"IPsec", NDPI_PROTOCOL_CATEGORY_VPN,
ndpi_set_proto_defaults(ndpi_str, 0 /* encrypted */, 0 /* nw proto */, NDPI_PROTOCOL_SAFE, NDPI_PROTOCOL_IPSEC,
"IPSec", NDPI_PROTOCOL_CATEGORY_VPN,
ndpi_build_default_ports(ports_a, 500, 0, 0, 0, 0) /* TCP */,
ndpi_build_default_ports(ports_b, 500, 4500, 0, 0, 0) /* UDP */);
ndpi_set_proto_defaults(ndpi_str, 1 /* cleartext */, 0 /* nw proto */, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_IP_GRE,
Expand Down Expand Up @@ -3140,7 +3140,7 @@ u_int16_t ndpi_guess_protocol_id(struct ndpi_detection_module_struct *ndpi_str,
switch(proto) {
case NDPI_IPSEC_PROTOCOL_ESP:
case NDPI_IPSEC_PROTOCOL_AH:
return(NDPI_PROTOCOL_IP_IPSEC);
return(NDPI_PROTOCOL_IPSEC);
break;
case NDPI_GRE_PROTOCOL_TYPE:
return(NDPI_PROTOCOL_IP_GRE);
Expand Down Expand Up @@ -4421,6 +4421,9 @@ static int ndpi_callback_init(struct ndpi_detection_module_struct *ndpi_str) {
/* RSH */
init_rsh_dissector(ndpi_str, &a, detection_bitmask);

/* IPsec */
init_ipsec_dissector(ndpi_str, &a, detection_bitmask);

#ifdef CUSTOM_NDPI_PROTOCOLS
#include "../../../nDPI-custom/custom_ndpi_main_init.c"
#endif
Expand Down
198 changes: 198 additions & 0 deletions src/lib/protocols/ipsec.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* ipsec.c
*
* Copyright (C) 2022 - ntop.org
*
* nDPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nDPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with nDPI. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "ndpi_protocol_ids.h"

#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_IPSEC

#include "ndpi_api.h"

enum isakmp_type {
ISAKMP_INVALID = 0,
ISAKMP_MALFORMED,
ISAKMP_V1,
ISAKMP_V2,
};

static void ndpi_int_ipsec_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
struct ndpi_flow_struct * const flow,
enum isakmp_type isakmp_type)
{
switch (isakmp_type)
{
case ISAKMP_INVALID:
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
case ISAKMP_MALFORMED:
NDPI_LOG_INFO(ndpi_struct, "found malformed ISAKMP (UDP)\n");
ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid IPSec/ISAKMP Header");
break;
case ISAKMP_V1:
NDPI_LOG_INFO(ndpi_struct, "found ISAKMPv1 (UDP)\n");
break;
case ISAKMP_V2:
NDPI_LOG_INFO(ndpi_struct, "found ISAKMPv2 (UDP)\n");
break;
}

ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_IPSEC,
NDPI_PROTOCOL_UNKNOWN,
NDPI_CONFIDENCE_DPI);
}

static enum isakmp_type ndpi_int_check_ports(struct ndpi_packet_struct const * const packet)
{
u_int16_t sport = ntohs(packet->udp->source);
u_int16_t dport = ntohs(packet->udp->dest);

/*
* If packet matches default IPSec/ISAKMP ports, it is most likely malformed,
* not IPSec/ISAKMP otherwise.
*/
if (sport == 500 || dport == 500 ||
sport == 4500 || dport == 4500)
{
return ISAKMP_MALFORMED;
}

return ISAKMP_INVALID;
}

static enum isakmp_type ndpi_int_check_isakmp_v1(struct ndpi_packet_struct const * const packet,
u_int16_t isakmp_offset, enum isakmp_type isakmp_type)
{
/* Next payload type */
if (packet->payload[isakmp_offset + 16] >= 14 && packet->payload[isakmp_offset + 16] <= 127)
{
return ndpi_int_check_ports(packet);
}

/* Exchange Type */
if (packet->payload[isakmp_offset + 18] >= 6 && packet->payload[isakmp_offset + 18] < 31)
{
return ndpi_int_check_ports(packet);
}

/* Flags */
if (packet->payload[isakmp_offset + 19] >= 8)
{
return ndpi_int_check_ports(packet);
}

return isakmp_type;
}

static enum isakmp_type ndpi_int_check_isakmp_v2(struct ndpi_packet_struct const * const packet,
u_int16_t isakmp_offset, enum isakmp_type isakmp_type)
{
/* Next payload type */
if ((packet->payload[isakmp_offset + 16] > 0 && packet->payload[isakmp_offset + 16] <= 32) ||
(packet->payload[isakmp_offset + 16] >= 49 && packet->payload[isakmp_offset + 16] <= 127))
{
return ndpi_int_check_ports(packet);
}

/* Exchange Type */
if ((packet->payload[isakmp_offset + 18] <= 33) ||
(packet->payload[isakmp_offset + 18] >= 38 && packet->payload[isakmp_offset + 18] <= 239))
{
return ndpi_int_check_ports(packet);
}

/* Flags */
if ((packet->payload[isakmp_offset + 19] & 0xC7) != 0)
{
return ndpi_int_check_ports(packet);
}

return isakmp_type;
}

void ndpi_search_ipsec(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
struct ndpi_packet_struct * const packet = &ndpi_struct->packet;
u_int16_t isakmp_offset = 0;
enum isakmp_type isakmp_type = ISAKMP_INVALID;

NDPI_LOG_DBG(ndpi_struct, "search IPSEC (UDP)\n");

if (packet->payload_packet_len < 28)
{
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

/* check for non-ESP marker required for ISAKMP over UDP */
if (get_u_int32_t(packet->payload, 0) == 0x00000000)
{
isakmp_offset = 4;
if (packet->payload_packet_len < 32)
{
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}
}

if (packet->payload[isakmp_offset + 17] != 0x20 /* Major Version 2 */)
{
if (packet->payload[isakmp_offset + 17] != 0x10 /* Major Version 1 */)
{
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
} else {
/* Version 1 is obsolete, but still used by some embedded devices. */
isakmp_type = ISAKMP_V1;
}
} else {
isakmp_type = ISAKMP_V2;
}

if (ntohl(get_u_int32_t(packet->payload, isakmp_offset + 24)) != (u_int32_t)packet->payload_packet_len - isakmp_offset)
{
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (isakmp_type == ISAKMP_V1)
{
isakmp_type = ndpi_int_check_isakmp_v1(packet, isakmp_offset, isakmp_type);
} else {
isakmp_type = ndpi_int_check_isakmp_v2(packet, isakmp_offset, isakmp_type);
}

ndpi_int_ipsec_add_connection(ndpi_struct, flow, isakmp_type);
}

void init_ipsec_dissector(struct ndpi_detection_module_struct *ndpi_struct,
u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask)
{
ndpi_set_bitmask_protocol_detection("IPSec", ndpi_struct, detection_bitmask, *id,
NDPI_PROTOCOL_IPSEC,
ndpi_search_ipsec,
NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD,
SAVE_DETECTION_BITMASK_AS_UNKNOWN,
ADD_TO_DETECTION_BITMASK
);

*id += 1;
}

6 changes: 3 additions & 3 deletions src/lib/protocols/non_tcp_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void ndpi_search_in_non_tcp_udp(struct ndpi_detection_module_struct
switch (flow->l4_proto) {
case NDPI_IPSEC_PROTOCOL_ESP:
case NDPI_IPSEC_PROTOCOL_AH:
set_protocol_and_bmask(NDPI_PROTOCOL_IP_IPSEC);
set_protocol_and_bmask(NDPI_PROTOCOL_IPSEC);
break;

case NDPI_GRE_PROTOCOL_TYPE:
Expand Down Expand Up @@ -106,8 +106,8 @@ void init_non_tcp_udp_dissector(struct ndpi_detection_module_struct *ndpi_struct
/* always add non tcp/udp if one protocol is compiled in */
NDPI_SAVE_AS_BITMASK(ndpi_struct->callback_buffer[*id].detection_bitmask, NDPI_PROTOCOL_UNKNOWN);

ndpi_set_bitmask_protocol_detection("IP_IPSEC", ndpi_struct, detection_bitmask, *id,
NDPI_PROTOCOL_IP_IPSEC,
ndpi_set_bitmask_protocol_detection("IPSec", ndpi_struct, detection_bitmask, *id,
NDPI_PROTOCOL_IPSEC,
ndpi_search_in_non_tcp_udp,
NDPI_SELECTION_BITMASK_PROTOCOL_IPV4_OR_IPV6,
NO_SAVE_DETECTION_BITMASK_AS_UNKNOWN,
Expand Down
Binary file added tests/pcap/ipsec_isakmp_esp.pcap
Binary file not shown.
13 changes: 6 additions & 7 deletions tests/result/ah.pcapng.out
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
Guessed flow protos: 1
Guessed flow protos: 0

DPI Packets (UDP): 4 (4.00 pkts/flow)
DPI Packets (UDP): 1 (1.00 pkts/flow)
DPI Packets (other): 1 (1.00 pkts/flow)
Confidence Match by port : 1 (flows)
Confidence DPI : 1 (flows)
Confidence DPI : 2 (flows)

IPsec 6 1768 2
IPSec 6 1768 2

1 UDP 10.2.3.2:500 <-> 10.3.4.4:500 [proto: 79/IPsec][Encrypted][Confidence: Match by port][cat: VPN/2][2 pkts/770 bytes <-> 2 pkts/722 bytes][Goodput ratio: 89/88][0.02 sec][PLAIN TEXT (DELETE)][Plen Bins: 0,0,0,0,0,0,0,0,25,0,25,50,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]
2 51 10.2.3.2:0 <-> 10.3.4.4:0 [proto: 79/IPsec][Encrypted][Confidence: DPI][cat: VPN/2][1 pkts/138 bytes <-> 1 pkts/138 bytes][Goodput ratio: 0/0][< 1 sec][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,0]
1 UDP 10.2.3.2:500 <-> 10.3.4.4:500 [proto: 79/IPSec][Encrypted][Confidence: DPI][cat: VPN/2][2 pkts/770 bytes <-> 2 pkts/722 bytes][Goodput ratio: 89/88][0.02 sec][PLAIN TEXT (DELETE)][Plen Bins: 0,0,0,0,0,0,0,0,25,0,25,50,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]
2 51 10.2.3.2:0 <-> 10.3.4.4:0 [proto: 79/IPSec][Encrypted][Confidence: DPI][cat: VPN/2][1 pkts/138 bytes <-> 1 pkts/138 bytes][Goodput ratio: 0/0][< 1 sec][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,0]
13 changes: 6 additions & 7 deletions tests/result/esp.pcapng.out
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
Guessed flow protos: 1
Guessed flow protos: 0

DPI Packets (UDP): 4 (4.00 pkts/flow)
DPI Packets (UDP): 1 (1.00 pkts/flow)
DPI Packets (other): 1 (1.00 pkts/flow)
Confidence Match by port : 1 (flows)
Confidence DPI : 1 (flows)
Confidence DPI : 2 (flows)

IPsec 6 1856 2
IPSec 6 1856 2

1 UDP 10.2.3.2:500 <-> 10.3.4.4:500 [proto: 79/IPsec][Encrypted][Confidence: Match by port][cat: VPN/2][2 pkts/786 bytes <-> 2 pkts/738 bytes][Goodput ratio: 89/88][0.02 sec][PLAIN TEXT (DELETE)][Plen Bins: 0,0,0,0,0,0,0,0,0,25,25,50,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]
2 50 10.2.3.2:0 <-> 10.3.4.4:0 [proto: 79/IPsec][Encrypted][Confidence: DPI][cat: VPN/2][1 pkts/166 bytes <-> 1 pkts/166 bytes][Goodput ratio: 0/0][0.00 sec][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,0]
1 UDP 10.2.3.2:500 <-> 10.3.4.4:500 [proto: 79/IPSec][Encrypted][Confidence: DPI][cat: VPN/2][2 pkts/786 bytes <-> 2 pkts/738 bytes][Goodput ratio: 89/88][0.02 sec][PLAIN TEXT (DELETE)][Plen Bins: 0,0,0,0,0,0,0,0,0,25,25,50,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]
2 50 10.2.3.2:0 <-> 10.3.4.4:0 [proto: 79/IPSec][Encrypted][Confidence: DPI][cat: VPN/2][1 pkts/166 bytes <-> 1 pkts/166 bytes][Goodput ratio: 0/0][0.00 sec][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,0]
Loading

0 comments on commit 20a29c3

Please sign in to comment.