ipsec: Improve ESP NULL autodetection

Improve the ESP NULL autodetection, and get it closer to the
heuristics in RFC 5879:

Detect multiple ICV lengths - 12, 16, 24, and 32
Check padding length validity
Check padding values
Reject if the subdissector rejects the packet

Still does not attempt to properly detect ENCR_NULL_AUTH_AES_GMAC,
which has a nonzero IV.

Fix #13730.
This commit is contained in:
John Thacker 2022-08-04 20:41:54 -04:00 committed by A Wireshark GitLab Utility
parent d24d27ebc1
commit 0aed38cf97
1 changed files with 102 additions and 36 deletions

View File

@ -1207,6 +1207,98 @@ export_ipsec_pdu(dissector_handle_t dissector_handle, packet_info *pinfo, tvbuff
}
}
/**
* Implements much of RFC 5879, "Heuristics for Detecting ESP-NULL Packets"
*
* Does NOT attempt to properly detect ENCR_NULL_AUTH_AES_GMAC.
*/
static int
esp_null_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *esp_tree)
{
int esp_packet_len, esp_pad_len, esp_icv_len, offset;
guint encapsulated_protocol;
guint32 saved_match_uint;
tvbuff_t *next_tvb;
dissector_handle_t dissector_handle;
/* Possible ICV lengths to try. Per RFC 5879, smallest to largest.
*/
static const int icv_lengths[] = {
12,
16,
24,
32,
-1
};
esp_packet_len = tvb_reported_length(tvb);
for (int i = 0; (esp_icv_len = icv_lengths[i]) != -1; i++) {
/* Make sure the packet is not truncated before the fields
* we need to read to determine the encapsulated protocol.
*/
if (tvb_bytes_exist(tvb, -(esp_icv_len + 2), 2))
{
offset = esp_packet_len - (esp_icv_len + 2);
esp_pad_len = tvb_get_guint8(tvb, offset);
encapsulated_protocol = tvb_get_guint8(tvb, offset + 1);
dissector_handle = dissector_get_uint_handle(ip_dissector_table, encapsulated_protocol);
if (dissector_handle == NULL) {
continue;
}
if (ESP_HEADER_LEN + esp_pad_len > offset) {
continue;
}
for (int j=0; j < esp_pad_len; j++) {
if (tvb_get_guint8(tvb, offset - (j + 1)) != (esp_pad_len - j)) {
continue;
}
}
saved_match_uint = pinfo->match_uint;
pinfo->match_uint = encapsulated_protocol;
next_tvb = tvb_new_subset_length(tvb, ESP_HEADER_LEN, offset - ESP_HEADER_LEN - esp_pad_len);
/* If the matching dissector has been disabled or rejects the packet,
* consider the heuristic failed.
* XXX: Should we also catch exceptions and consider those failures too?
*
* Note that the case of ENCR_NULL_AUTH_AES_GMAC will find the correct
* padding and encapsulated protocol using a 16 byte ICV, but needs to
* skip over the 8 bytes of IV.
*/
if (call_dissector_only(dissector_handle, next_tvb, pinfo, proto_tree_get_parent_tree(esp_tree), NULL) == 0) {
pinfo->match_uint = saved_match_uint;
continue;
}
export_ipsec_pdu(dissector_handle, pinfo, next_tvb);
pinfo->match_uint = saved_match_uint;
if (esp_tree) {
if (esp_pad_len !=0) {
proto_tree_add_item(esp_tree, hf_esp_pad,
tvb, offset - esp_pad_len,
esp_pad_len, ENC_NA);
}
proto_tree_add_uint(esp_tree, hf_esp_pad_len, tvb,
offset, 1,
esp_pad_len);
proto_tree_add_uint_format(esp_tree, hf_esp_protocol, tvb,
offset + 1, 1,
encapsulated_protocol,
"Next header: %s (0x%02x)",
ipprotostr(encapsulated_protocol), encapsulated_protocol);
}
return esp_icv_len;
}
}
return esp_icv_len;
}
static gboolean
capture_ah(const guchar *pd, int offset, int len, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header)
{
@ -2179,51 +2271,24 @@ dissect_esp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
if(g_esp_enable_null_encryption_decode_heuristic)
{
/* Make sure the packet is not truncated before the fields
* we need to read to determine the encapsulated protocol.
* (Note: we assume an ICV of 12 byte length.)
*/
if(tvb_bytes_exist(tvb, esp_packet_len - 14, 2))
{
esp_pad_len = tvb_get_guint8(tvb, esp_packet_len - 14);
encapsulated_protocol = tvb_get_guint8(tvb, esp_packet_len - 13);
dissector_handle = dissector_get_uint_handle(ip_dissector_table, encapsulated_protocol);
if (dissector_handle && (ESP_HEADER_LEN + 14 + esp_pad_len) <= esp_packet_len) {
saved_match_uint = pinfo->match_uint;
pinfo->match_uint = encapsulated_protocol;
next_tvb = tvb_new_subset_length(tvb, ESP_HEADER_LEN, esp_packet_len - ESP_HEADER_LEN - 14 - esp_pad_len);
export_ipsec_pdu(dissector_handle, pinfo, next_tvb);
call_dissector(dissector_handle, next_tvb, pinfo, tree);
pinfo->match_uint = saved_match_uint;
decrypt_dissect_ok = TRUE;
}
}
esp_icv_len = esp_null_heur(tvb, pinfo, esp_tree);
}
if(decrypt_dissect_ok)
if(esp_icv_len != -1)
{
offset = esp_packet_len - esp_icv_len;
if(esp_tree)
{
proto_tree_add_uint(esp_tree, hf_esp_pad_len, tvb,
esp_packet_len - 14, 1,
esp_pad_len);
proto_tree_add_uint_format(esp_tree, hf_esp_protocol, tvb,
esp_packet_len - 13, 1,
encapsulated_protocol,
"Next header: %s (0x%02x)",
ipprotostr(encapsulated_protocol), encapsulated_protocol);
/* Make sure we have the auth trailer data */
if(tvb_bytes_exist(tvb, esp_packet_len - 12, 12))
if(tvb_bytes_exist(tvb, offset, esp_icv_len))
{
proto_tree_add_item(esp_tree, hf_esp_icv, tvb, esp_packet_len - 12, 12, ENC_NA);
icv_item = proto_tree_add_item(esp_tree, hf_esp_icv, tvb, offset, esp_icv_len, ENC_NA);
}
else
{
/* Truncated so just display what we have */
proto_tree_add_bytes_format(esp_tree, hf_esp_icv, tvb, esp_packet_len - 12,
12 - (esp_packet_len - tvb_captured_length(tvb)),
icv_item = proto_tree_add_bytes_format(esp_tree, hf_esp_icv, tvb, offset,
esp_icv_len - (esp_packet_len - tvb_captured_length(tvb)),
NULL, "Integrity Check Value (truncated)");
}
}
@ -2482,8 +2547,9 @@ proto_register_ipsec(void)
prefs_register_bool_preference(esp_module, "enable_null_encryption_decode_heuristic",
"Attempt to detect/decode NULL encrypted ESP payloads",
"This is done only if the Decoding is not SET or the packet does not belong to a SA. "
"Assumes a 12 byte auth (HMAC-SHA1-96/HMAC-MD5-96/AES-XCBC-MAC-96) "
"and attempts decode based on the ethertype 13 bytes from packet end",
"Tries ICV lengths of 12, 16, 24, and 32 bytes, checks for valid padding, "
"and attempts to decode based on the derived Next Header field. "
"Does not detect ENCR_NULL_AUTH_AES_GMAC (i.e. assumes 0 length IV)",
&g_esp_enable_null_encryption_decode_heuristic);
prefs_register_bool_preference(esp_module, "do_esp_sequence_analysis",