From b386b2ba5122ac4d51f586f15a1793621fa1a5ad Mon Sep 17 00:00:00 2001 From: Yun Zheng Hu Date: Thu, 28 May 2015 12:11:06 +0200 Subject: [PATCH] BIT-1314: Add detection for Quantum Insert attacks TCP_Reassembler can now keep a history of old TCP segments using the `tcp_max_old_segments` option. A value of zero will disable it. An overlapping segment with different data can indicate a possible TCP injection attack. The rexmit_inconsistency event will fire if this is the case. --- src/NetVar.cc | 2 + src/NetVar.h | 1 + src/Reassem.cc | 70 +++++++++++++++++++- src/Reassem.h | 12 ++++ src/analyzer/protocol/tcp/TCP_Reassembler.cc | 3 + 5 files changed, 87 insertions(+), 1 deletion(-) diff --git a/src/NetVar.cc b/src/NetVar.cc index 123e947701..dc049005bb 100644 --- a/src/NetVar.cc +++ b/src/NetVar.cc @@ -49,6 +49,7 @@ double tcp_partial_close_delay; int tcp_max_initial_window; int tcp_max_above_hole_without_any_acks; int tcp_excessive_data_without_further_acks; +int tcp_max_old_segments; RecordType* socks_address; @@ -354,6 +355,7 @@ void init_net_var() opt_internal_int("tcp_max_above_hole_without_any_acks"); tcp_excessive_data_without_further_acks = opt_internal_int("tcp_excessive_data_without_further_acks"); + tcp_max_old_segments = opt_internal_int("tcp_max_old_segments"); socks_address = internal_type("SOCKS::Address")->AsRecordType(); diff --git a/src/NetVar.h b/src/NetVar.h index bf2d9a5712..efadaaad6d 100644 --- a/src/NetVar.h +++ b/src/NetVar.h @@ -52,6 +52,7 @@ extern double tcp_reset_delay; extern int tcp_max_initial_window; extern int tcp_max_above_hole_without_any_acks; extern int tcp_excessive_data_without_further_acks; +extern int tcp_max_old_segments; extern RecordType* socks_address; diff --git a/src/Reassem.cc b/src/Reassem.cc index 8bf965427b..bf2ed5df02 100644 --- a/src/Reassem.cc +++ b/src/Reassem.cc @@ -34,12 +34,38 @@ uint64 Reassembler::total_size = 0; Reassembler::Reassembler(uint64 init_seq) { blocks = last_block = 0; + old_blocks = last_old_block = 0; + total_old_blocks = max_old_blocks = 0; trim_seq = last_reassem_seq = init_seq; } Reassembler::~Reassembler() { ClearBlocks(); + ClearOldBlocks(); + } + +void Reassembler::CheckOverlap(DataBlock *head, DataBlock *tail, + uint64 seq, uint64 len, const u_char* data) + { + if ( ! head || ! tail ) + return; + + if ( seq_between(seq, head->seq, tail->upper) ) + { + for ( DataBlock* b = head; b; b = b->next ) + { + if ( seq_between(seq, b->seq, b->upper) ) + { + uint64 overlap_start = seq; + uint64 overlap_offset = overlap_start - b->seq; + uint64 new_b_len = len; + uint64 b_len = b->upper - overlap_start; + uint64 overlap_len = min(new_b_len, b_len); + Overlap(&b->block[overlap_offset], data, overlap_len); + } + } + } } void Reassembler::NewBlock(double t, uint64 seq, uint64 len, const u_char* data) @@ -49,6 +75,9 @@ void Reassembler::NewBlock(double t, uint64 seq, uint64 len, const u_char* data) uint64 upper_seq = seq + len; + CheckOverlap( blocks, last_block, seq, len, data ); + CheckOverlap( old_blocks, last_old_block, seq, len, data ); + if ( upper_seq <= trim_seq ) // Old data, don't do any work for it. return; @@ -119,7 +148,33 @@ uint64 Reassembler::TrimToSeq(uint64 seq) num_missing += seq - blocks->upper; } - delete blocks; + if (max_old_blocks) + { + blocks->next = 0; + if (last_old_block) + { + blocks->prev = last_old_block; + last_old_block->next = blocks; + } + else + { + blocks->prev = 0; + old_blocks = blocks; + } + + last_old_block = blocks; + total_old_blocks++; + + while (old_blocks && total_old_blocks > max_old_blocks) + { + DataBlock* next = old_blocks->next; + delete old_blocks; + old_blocks = next; + total_old_blocks--; + } + } + else + delete blocks; blocks = b; } @@ -156,6 +211,19 @@ void Reassembler::ClearBlocks() last_block = 0; } +void Reassembler::ClearOldBlocks() + { + while ( old_blocks ) + { + DataBlock* b = old_blocks->next; + delete old_blocks; + old_blocks = b; + } + + last_old_block = 0; + } + + uint64 Reassembler::TotalSize() const { uint64 size = 0; diff --git a/src/Reassem.h b/src/Reassem.h index 39617f7816..e55c809990 100644 --- a/src/Reassem.h +++ b/src/Reassem.h @@ -36,6 +36,7 @@ public: // Delete all held blocks. void ClearBlocks(); + void ClearOldBlocks(); int HasBlocks() const { return blocks != 0; } uint64 LastReassemSeq() const { return last_reassem_seq; } @@ -50,6 +51,8 @@ public: // Sum over all data buffered in some reassembler. static uint64 TotalMemoryAllocation() { return total_size; } + void SetMaxOldBlocks(uint32 count) { max_old_blocks = count; } + protected: Reassembler() { } @@ -65,10 +68,19 @@ protected: DataBlock* AddAndCheck(DataBlock* b, uint64 seq, uint64 upper, const u_char* data); + void CheckOverlap(DataBlock *head, DataBlock *tail, + uint64 seq, uint64 len, const u_char* data); + DataBlock* blocks; DataBlock* last_block; + + DataBlock* old_blocks; + DataBlock* last_old_block; + uint64 last_reassem_seq; uint64 trim_seq; // how far we've trimmed + uint32 max_old_blocks; + uint32 total_old_blocks; static uint64 total_size; }; diff --git a/src/analyzer/protocol/tcp/TCP_Reassembler.cc b/src/analyzer/protocol/tcp/TCP_Reassembler.cc index 16bb9cc56d..bbcd9cb43a 100644 --- a/src/analyzer/protocol/tcp/TCP_Reassembler.cc +++ b/src/analyzer/protocol/tcp/TCP_Reassembler.cc @@ -42,6 +42,9 @@ TCP_Reassembler::TCP_Reassembler(analyzer::Analyzer* arg_dst_analyzer, seq_to_skip = 0; in_delivery = false; + if ( tcp_max_old_segments ) + SetMaxOldBlocks(tcp_max_old_segments); + if ( tcp_contents ) { // Val dst_port_val(ntohs(Conn()->RespPort()), TYPE_PORT);