mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
More DCE_RPC improvements.
- The logic for fragment handling has been rewritten and should be correct now. - There are now tunables for fragment handling overflow situations. - DCE_RPC::max_cmd_reassembly and DCE_RPC::max_frag_data - They result in weirds and analyzer removal. - Memory leak fixed by unique_ptr auto cleanup. - DCE_RPC is now intolerate of content gaps and will stop analyzing traffic if content gaps happen (like most other analyzers currently).
This commit is contained in:
parent
bd0a374c87
commit
e4b620673b
7 changed files with 76 additions and 13 deletions
|
@ -2,6 +2,16 @@
|
|||
module DCE_RPC;
|
||||
|
||||
export {
|
||||
## The maximum number of simultaneous fragmented commands that
|
||||
## the analyzer will tolerate before the analyzer will generate
|
||||
## a weird and remove itself from the connection.
|
||||
const max_cmd_reassembly = 20 &redef;
|
||||
|
||||
## The maximum number of fragmented bytes that will be tolerated
|
||||
## on a command before the analyzer will generate a weird and
|
||||
## remove itself from the connection.
|
||||
const max_frag_data = 30000 &redef;
|
||||
|
||||
const uuid_endpoint_map: table[string] of string = {
|
||||
["367abb81-9844-35f1-ad32-98f038001003"] = "svcctl",
|
||||
["86d35949-83c9-4044-b424-db363231fd0c"] = "ITaskSchedulerService",
|
||||
|
|
|
@ -5,7 +5,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
|
|||
|
||||
bro_plugin_begin(Bro DCE_RPC)
|
||||
bro_plugin_cc(DCE_RPC.cc Plugin.cc)
|
||||
bro_plugin_bif(types.bif events.bif)
|
||||
bro_plugin_bif(consts.bif types.bif events.bif)
|
||||
bro_plugin_pac(
|
||||
dce_rpc.pac
|
||||
dce_rpc-protocol.pac
|
||||
|
|
|
@ -16,6 +16,7 @@ using namespace analyzer::dce_rpc;
|
|||
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection *conn)
|
||||
: tcp::TCP_ApplicationAnalyzer("DCE_RPC", conn)
|
||||
{
|
||||
had_gap = false;
|
||||
interp = new binpac::DCE_RPC::DCE_RPC_Conn(this);
|
||||
}
|
||||
|
||||
|
@ -41,6 +42,7 @@ void DCE_RPC_Analyzer::EndpointEOF(bool is_orig)
|
|||
void DCE_RPC_Analyzer::Undelivered(uint64 seq, int len, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
had_gap = true;
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
|
@ -49,6 +51,12 @@ void DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
|||
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
|
||||
assert(TCP());
|
||||
|
||||
if ( had_gap )
|
||||
// If only one side had a content gap, we could still try to
|
||||
// deliver data to the other side if the script layer can handle this.
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
interp->NewData(orig, data, data + len);
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
{ return new DCE_RPC_Analyzer(conn); }
|
||||
|
||||
protected:
|
||||
bool had_gap;
|
||||
binpac::DCE_RPC::DCE_RPC_Conn* interp;
|
||||
};
|
||||
|
||||
|
|
2
src/analyzer/protocol/dce-rpc/consts.bif
Normal file
2
src/analyzer/protocol/dce-rpc/consts.bif
Normal file
|
@ -0,0 +1,2 @@
|
|||
const DCE_RPC::max_cmd_reassembly: count;
|
||||
const DCE_RPC::max_frag_data: count;
|
|
@ -37,7 +37,7 @@ type DCE_RPC_PDU(is_orig: bool) = record {
|
|||
# Subtract an extra 8 when there is an auth section because we have some "auth header" fields in that structure.
|
||||
body_length : int = header.frag_length - sizeof(header) - header.auth_length - (header.auth_length > 0 ? 8 : 0);
|
||||
frag_reassembled : bool = $context.flow.reassemble_fragment(header, frag);
|
||||
body : DCE_RPC_Body(header) withinput $context.flow.reassembled_body(header, frag) &if(header.lastfrag);
|
||||
body : DCE_RPC_Body(header) withinput $context.flow.reassembled_body(header, frag) &if(frag_reassembled);
|
||||
} &byteorder = header.byteorder, &length = header.frag_length;
|
||||
|
||||
type NDR_Format = record {
|
||||
|
@ -174,23 +174,65 @@ flow DCE_RPC_Flow(is_orig: bool) {
|
|||
flowunit = DCE_RPC_PDU(is_orig) withcontext(connection, this);
|
||||
|
||||
%member{
|
||||
std::map<uint32, FlowBuffer*> fb;
|
||||
std::map<uint32, std::unique_ptr<FlowBuffer>> fb;
|
||||
%}
|
||||
|
||||
# Fragment reassembly.
|
||||
function reassemble_fragment(header: DCE_RPC_Header, frag: bytestring): bool
|
||||
%{
|
||||
if ( ${header.firstfrag} && !${header.lastfrag} &&
|
||||
fb.count(${header.call_id}) == 0 )
|
||||
fb[${header.call_id}] = new FlowBuffer();
|
||||
if ( ${header.firstfrag} )
|
||||
{
|
||||
if ( ${header.lastfrag} )
|
||||
{
|
||||
// all-in-one packet
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// first frag, but not last so we start a flowbuffer
|
||||
fb[${header.call_id}] = std::unique_ptr<FlowBuffer>(new FlowBuffer());
|
||||
fb[${header.call_id}]->NewFrame(0, true);
|
||||
fb[${header.call_id}]->BufferData(frag.begin(), frag.end());
|
||||
|
||||
if ( fb.count(${header.call_id}) == 0 )
|
||||
if ( fb.size() > BifConst::DCE_RPC::max_cmd_reassembly )
|
||||
{
|
||||
reporter->Weird(connection()->bro_analyzer()->Conn(),
|
||||
"too_many_dce_rpc_msgs_in_reassembly");
|
||||
connection()->bro_analyzer()->Remove();
|
||||
}
|
||||
|
||||
if ( fb[${header.call_id}]->data_length() > BifConst::DCE_RPC::max_frag_data )
|
||||
{
|
||||
reporter->Weird(connection()->bro_analyzer()->Conn(),
|
||||
"too_much_dce_rpc_fragment_data");
|
||||
connection()->bro_analyzer()->Remove();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( fb.count(${header.call_id}) > 0 )
|
||||
{
|
||||
// not the first frag, but we have a flow buffer so add to it
|
||||
fb[${header.call_id}]->BufferData(frag.begin(), frag.end());
|
||||
|
||||
if ( fb[${header.call_id}]->data_length() > BifConst::DCE_RPC::max_frag_data )
|
||||
{
|
||||
reporter->Weird(connection()->bro_analyzer()->Conn(),
|
||||
"too_much_dce_rpc_fragment_data");
|
||||
connection()->bro_analyzer()->Remove();
|
||||
}
|
||||
|
||||
return ${header.lastfrag};
|
||||
}
|
||||
else
|
||||
{
|
||||
// no flow buffer and not a first frag, ignore it.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto frag_reassembler_ = fb[${header.call_id}];
|
||||
frag_reassembler_->BufferData(frag.begin(), frag.end());
|
||||
|
||||
return (!${header.firstfrag} && ${header.lastfrag});
|
||||
// can't reach here.
|
||||
return false;
|
||||
%}
|
||||
|
||||
function reassembled_body(h: DCE_RPC_Header, body: bytestring): const_bytestring
|
||||
|
@ -200,7 +242,6 @@ flow DCE_RPC_Flow(is_orig: bool) {
|
|||
if ( fb.count(${h.call_id}) > 0 )
|
||||
{
|
||||
bd = const_bytestring(fb[${h.call_id}]->begin(), fb[${h.call_id}]->end());
|
||||
delete fb[${h.call_id}];
|
||||
fb.erase(${h.call_id});
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
%include bro.pac
|
||||
|
||||
%extern{
|
||||
#include "consts.bif.h"
|
||||
#include "types.bif.h"
|
||||
#include "events.bif.h"
|
||||
%}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue