From b386b2ba5122ac4d51f586f15a1793621fa1a5ad Mon Sep 17 00:00:00 2001 From: Yun Zheng Hu Date: Thu, 28 May 2015 12:11:06 +0200 Subject: [PATCH 1/2] 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); From 2aa214d8358fe583bd56920545e2057565ab6500 Mon Sep 17 00:00:00 2001 From: Yun Zheng Hu Date: Thu, 28 May 2015 12:12:22 +0200 Subject: [PATCH 2/2] BIT-1314: Added QI test for rexmit_inconsistency --- .../Baseline/core.tcp.quantum-insert/.stdout | 4 ++++ .../tcp/qi_internet_SYNACK_curl_jsonip.pcap | Bin 0 -> 1718 bytes testing/btest/core/tcp/quantum-insert.bro | 12 ++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 testing/btest/Baseline/core.tcp.quantum-insert/.stdout create mode 100644 testing/btest/Traces/tcp/qi_internet_SYNACK_curl_jsonip.pcap create mode 100644 testing/btest/core/tcp/quantum-insert.bro diff --git a/testing/btest/Baseline/core.tcp.quantum-insert/.stdout b/testing/btest/Baseline/core.tcp.quantum-insert/.stdout new file mode 100644 index 0000000000..3f2c5fad1b --- /dev/null +++ b/testing/btest/Baseline/core.tcp.quantum-insert/.stdout @@ -0,0 +1,4 @@ +----- rexmit_inconsistency ----- +1429652006.683290 c: [orig_h=178.200.100.200, orig_p=39976/tcp, resp_h=96.126.98.124, resp_p=80/tcp] +1429652006.683290 t1: HTTP/1.1 200 OK\x0d\x0aContent-Length: 5\x0d\x0a\x0d\x0aBANG! +1429652006.683290 t2: HTTP/1.1 200 OK\x0d\x0aServer: nginx/1.4.4\x0d\x0aDate: diff --git a/testing/btest/Traces/tcp/qi_internet_SYNACK_curl_jsonip.pcap b/testing/btest/Traces/tcp/qi_internet_SYNACK_curl_jsonip.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d906d9cac9dbccaface027bfe7feeeff22f9c742 GIT binary patch literal 1718 zcmah}O>7fK6rOc{u%}%JsA{7Us=-1KPZKP1q&)yPi}s2Xf`pI|qLq3Jszh&Xih7B9=mGkkS9a_*q$@pn)^GNE z-+SLT^Xr4Viy_iU>d&i-gb47mdwF>F$3lc0gEa=}S$P!;lg)46sfNR3jF9xw_i2*u zzH#O5qx_>!FU-wf>nE8#sdFp1xf)87WrL7VXV*`H&>0dsI*1^I>5JsR1m+R_=J^OY z0&5H(*RN4SKL7>DaQDeLB4Y)Txz>N_>!nLj;n~e?pRulYeDuyxnC`lvI*1HaHX-sq z-$uwNfMT@qR;OI#z3pLu_cK7hywH;-96qXZMPx+$?rDTzW3}djlX#Lb5OGWLwTQiG z^2w7{#PP8#Q`kf{n^EF&T%@xucchU5H$9Ez%1%j1$_Z7Dv3?_w8kWlDDbqS*N|w#d zIX6Ef5A9_oW3GAQG^A!`jsjXt9@NWc``p;e54EE8k~y^-p^^-DmO%P32xmyxG`5K?c94aQ<@dQzl^ zEYpMLq@&y{ctwp3VXe`TW8*uq;QPIF1GRp>jkEpcYU^z0%b-?uVQZS?_qWa#5%(T! zLaaHq$yw^!c63%{M|%tN<%J%y`a{M?1nsY*s}OPdcoSk!f7Od1$G?;}alAL#;CL9P zTI>2WCD&n?7=bYp;p$o+a`cv7%dwZNI(@Ai+eSXaoeFm}W)=+dbC6$^Rgr$Adt77L zG9O@xIGfTPP$)jc;;NQNYU(f>pUSR_l|5_2X5F?+Mo#w(%T&-f`&q82J1+M=EPE%U zlt{CVZn`JHzhh?3${S_@EL;T3@_Y@-2xH z=oZH+Nh76_bw-+Y3}^uy`iA@z_lj2D)fgNqw3V6^>A9$3N401?nUcYRavTDFqXSWW z&MJG5QT)X~G~-x1;l1d2wx%cr?yVn==y{RG>WAXtmz2PjRExG7rGIuD^`|d``WG)e z-<^T0b@B?Tjt?sD-f8kuyE}K(+_}9~`TNiJq~+W3nZ6kJv{zZ0u zlL2;>2D`6(Za%v|U()qEKgce<_Cbx^<^a3jkzEze&aUGPc8`JGVL|AkkSk*zdw=)6 Rw>Fq#bQGB$?D&7C{{eG_