mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Lots of cleanup and improvement to DCE/RPC analyzer.
- It works with DCE/RPC over SMB1+2 now. - Using named pipes in 1+2 and the transaction cmd in SMB1. - Base scripts based on work by Josh Liburdi. - New dce_rpc.log. Feedback on how to make this log more compact and useful would be appreciated.
This commit is contained in:
parent
d249b76390
commit
5721db4be7
31 changed files with 1890 additions and 1819 deletions
|
@ -43,6 +43,7 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@load base/protocols/conn
|
@load base/protocols/conn
|
||||||
|
@load base/protocols/dce-rpc
|
||||||
@load base/protocols/dhcp
|
@load base/protocols/dhcp
|
||||||
@load base/protocols/dnp3
|
@load base/protocols/dnp3
|
||||||
@load base/protocols/dns
|
@load base/protocols/dns
|
||||||
|
|
2
scripts/base/protocols/dce-rpc/__load__.bro
Normal file
2
scripts/base/protocols/dce-rpc/__load__.bro
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
@load ./consts
|
||||||
|
@load ./main
|
1374
scripts/base/protocols/dce-rpc/consts.bro
Normal file
1374
scripts/base/protocols/dce-rpc/consts.bro
Normal file
File diff suppressed because it is too large
Load diff
6
scripts/base/protocols/dce-rpc/dpd.sig
Normal file
6
scripts/base/protocols/dce-rpc/dpd.sig
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
signature dpd_dce_rpc {
|
||||||
|
ip-proto == tcp
|
||||||
|
payload /^\x05[\x00\x01][\x00-\x13]\x03/
|
||||||
|
enable "DCE_RPC"
|
||||||
|
}
|
109
scripts/base/protocols/dce-rpc/main.bro
Normal file
109
scripts/base/protocols/dce-rpc/main.bro
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
@load ./consts
|
||||||
|
|
||||||
|
module DCE_RPC;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef enum Log::ID += { LOG };
|
||||||
|
|
||||||
|
type Info: record {
|
||||||
|
## Timestamp for when the event happened.
|
||||||
|
ts : time &log;
|
||||||
|
## Unique ID for the connection.
|
||||||
|
uid : string &log;
|
||||||
|
## The connection's 4-tuple of endpoint addresses/ports.
|
||||||
|
id : conn_id &log;
|
||||||
|
## Round trip time from the request to the response.
|
||||||
|
## If either the request or response wasn't seen,
|
||||||
|
## this will be null.
|
||||||
|
rtt : interval &log &optional;
|
||||||
|
|
||||||
|
## Remote pipe name.
|
||||||
|
named_pipe : string &log &optional;
|
||||||
|
## Endpoint name looked up from the uuid.
|
||||||
|
endpoint : string &log &optional;
|
||||||
|
## Operation seen in the call.
|
||||||
|
operation : string &log &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Set of interface UUID values to ignore.
|
||||||
|
const ignored_uuids = set("e1af8308-5d1f-11c9-91a4-08002b14a0fa") &redef;
|
||||||
|
}
|
||||||
|
|
||||||
|
redef record Info += {
|
||||||
|
uuid: string &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
redef record connection += {
|
||||||
|
dce_rpc: Info &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ports = { 135/tcp };
|
||||||
|
redef likely_server_ports += { ports };
|
||||||
|
|
||||||
|
event bro_init() &priority=5
|
||||||
|
{
|
||||||
|
Log::create_stream(DCE_RPC::LOG, [$columns=Info, $path="dce_rpc"]);
|
||||||
|
Analyzer::register_for_ports(Analyzer::ANALYZER_DCE_RPC, ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_session(c: connection)
|
||||||
|
{
|
||||||
|
if ( ! c?$dce_rpc )
|
||||||
|
{
|
||||||
|
c$dce_rpc = [$ts=network_time(),
|
||||||
|
$id=c$id,
|
||||||
|
$uid=c$uid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event dce_rpc_bind(c: connection, uuid: string, version: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
local uuid_str = uuid_to_string(uuid);
|
||||||
|
if ( uuid_str in ignored_uuids )
|
||||||
|
return;
|
||||||
|
|
||||||
|
c$dce_rpc$uuid = uuid_str;
|
||||||
|
c$dce_rpc$endpoint = uuid_endpoint_map[uuid_str];
|
||||||
|
}
|
||||||
|
|
||||||
|
event dce_rpc_bind_ack(c: connection, sec_addr: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
if ( sec_addr != "" )
|
||||||
|
c$dce_rpc$named_pipe = sec_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
event dce_rpc_request(c: connection, opnum: count, stub: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
if ( c?$dce_rpc && c$dce_rpc?$endpoint )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event dce_rpc_response(c: connection, opnum: count, stub: string) &priority=5
|
||||||
|
{
|
||||||
|
set_session(c);
|
||||||
|
|
||||||
|
if ( c?$dce_rpc && c$dce_rpc?$endpoint )
|
||||||
|
{
|
||||||
|
c$dce_rpc$operation = operations[c$dce_rpc$uuid, opnum];
|
||||||
|
if ( c$dce_rpc$ts != network_time() )
|
||||||
|
c$dce_rpc$rtt = network_time() - c$dce_rpc$ts;
|
||||||
|
|
||||||
|
Log::write(LOG, c$dce_rpc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event connection_state_remove(c: connection)
|
||||||
|
{
|
||||||
|
if ( ! c?$dce_rpc )
|
||||||
|
return;
|
||||||
|
|
||||||
|
# TODO: Go through any remaining dce_rpc requests that haven't been processed with replies.
|
||||||
|
}
|
|
@ -5,8 +5,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
|
||||||
|
|
||||||
bro_plugin_begin(Bro DCE_RPC)
|
bro_plugin_begin(Bro DCE_RPC)
|
||||||
bro_plugin_cc(DCE_RPC.cc Plugin.cc)
|
bro_plugin_cc(DCE_RPC.cc Plugin.cc)
|
||||||
bro_plugin_bif(events.bif)
|
bro_plugin_bif(types.bif events.bif)
|
||||||
bro_plugin_pac(dce_rpc.pac dce_rpc-protocol.pac dce_rpc-analyzer.pac epmapper.pac)
|
bro_plugin_pac(dce_rpc.pac dce_rpc-protocol.pac dce_rpc-analyzer.pac epmapper.pac)
|
||||||
bro_plugin_pac(dce_rpc_simple.pac dce_rpc-protocol.pac epmapper.pac)
|
|
||||||
bro_plugin_end()
|
bro_plugin_end()
|
||||||
|
|
||||||
|
|
|
@ -9,580 +9,52 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include "DCE_RPC.h"
|
#include "DCE_RPC.h"
|
||||||
#include "Sessions.h"
|
|
||||||
|
|
||||||
#include "analyzer/Manager.h"
|
|
||||||
|
|
||||||
#include "events.bif.h"
|
|
||||||
|
|
||||||
using namespace analyzer::dce_rpc;
|
using namespace analyzer::dce_rpc;
|
||||||
|
|
||||||
#define xbyte(b, n) (((const u_char*) (b))[n])
|
|
||||||
|
|
||||||
#define extract_uint16(little_endian, bytes) \
|
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection *conn)
|
||||||
((little_endian) ? \
|
|
||||||
uint16(xbyte(bytes, 0)) | ((uint16(xbyte(bytes, 1))) << 8) : \
|
|
||||||
uint16(xbyte(bytes, 1)) | ((uint16(xbyte(bytes, 0))) << 8))
|
|
||||||
|
|
||||||
static int uuid_index[] = {
|
|
||||||
3, 2, 1, 0,
|
|
||||||
5, 4, 7, 6,
|
|
||||||
8, 9, 10, 11,
|
|
||||||
12, 13, 14, 15
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* analyzer::dce_rpc::uuid_to_string(const u_char* uuid_data)
|
|
||||||
{
|
|
||||||
static char s[1024];
|
|
||||||
char* sp = s;
|
|
||||||
|
|
||||||
for ( int i = 0; i < 16; ++i )
|
|
||||||
{
|
|
||||||
if ( i == 4 || i == 6 || i == 8 || i == 10 )
|
|
||||||
sp += snprintf(sp, s + sizeof(s) - sp, "-");
|
|
||||||
|
|
||||||
int j = uuid_index[i];
|
|
||||||
sp += snprintf(sp, s + sizeof(s) - sp, "%02x", uuid_data[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID()
|
|
||||||
{
|
|
||||||
memset(data, 0, 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const u_char d[16])
|
|
||||||
{
|
|
||||||
memcpy(data, d, 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const binpac::bytestring& uuid)
|
|
||||||
{
|
|
||||||
if ( uuid.length() != 16 )
|
|
||||||
reporter->InternalError("UUID length error");
|
|
||||||
memcpy(data, uuid.begin(), 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const char* str)
|
|
||||||
{
|
|
||||||
s = string(str);
|
|
||||||
const char* sp = str;
|
|
||||||
int i;
|
|
||||||
for ( i = 0; i < 16; ++i )
|
|
||||||
{
|
|
||||||
if ( *sp == '-' )
|
|
||||||
++sp;
|
|
||||||
if ( ! *sp || ! *(sp+1) )
|
|
||||||
break;
|
|
||||||
|
|
||||||
data[uuid_index[i]] =
|
|
||||||
(u_char) (decode_hex(*sp) * 16 + decode_hex(*(sp+1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( i != 16 )
|
|
||||||
reporter->InternalError("invalid UUID string: %s", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef map<UUID, BifEnum::dce_rpc_if_id> uuid_map_t;
|
|
||||||
|
|
||||||
static uuid_map_t& well_known_uuid_map()
|
|
||||||
{
|
|
||||||
static uuid_map_t the_map;
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
if ( initialized )
|
|
||||||
return the_map;
|
|
||||||
|
|
||||||
using namespace BifEnum;
|
|
||||||
|
|
||||||
the_map[UUID("e1af8308-5d1f-11c9-91a4-08002b14a0fa")] = DCE_RPC_epmapper;
|
|
||||||
|
|
||||||
the_map[UUID("afa8bd80-7d8a-11c9-bef4-08002b102989")] = DCE_RPC_mgmt;
|
|
||||||
|
|
||||||
// It's said that the following interfaces are merely aliases.
|
|
||||||
the_map[UUID("12345778-1234-abcd-ef00-0123456789ab")] = DCE_RPC_lsarpc;
|
|
||||||
the_map[UUID("12345678-1234-abcd-ef00-01234567cffb")] = DCE_RPC_netlogon;
|
|
||||||
the_map[UUID("12345778-1234-abcd-ef00-0123456789ac")] = DCE_RPC_samr;
|
|
||||||
|
|
||||||
// The next group of aliases.
|
|
||||||
the_map[UUID("4b324fc8-1670-01d3-1278-5a47bf6ee188")] = DCE_RPC_srvsvc;
|
|
||||||
the_map[UUID("12345678-1234-abcd-ef00-0123456789ab")] = DCE_RPC_spoolss;
|
|
||||||
the_map[UUID("45f52c28-7f9f-101a-b52b-08002b2efabe")] = DCE_RPC_winspipe;
|
|
||||||
the_map[UUID("6bffd098-a112-3610-9833-46c3f87e345a")] = DCE_RPC_wkssvc;
|
|
||||||
|
|
||||||
// DRS - NT directory replication service.
|
|
||||||
the_map[UUID("e3514235-4b06-11d1-ab04-00c04fc2dcd2")] = DCE_RPC_drs;
|
|
||||||
|
|
||||||
// "The IOXIDResolver RPC interface (formerly known as
|
|
||||||
// IObjectExporter) is remotely used to reach the local object
|
|
||||||
// resolver (OR)."
|
|
||||||
the_map[UUID("99fcfec4-5260-101b-bbcb-00aa0021347a")] = DCE_RPC_oxid;
|
|
||||||
|
|
||||||
the_map[UUID("3919286a-b10c-11d0-9ba8-00c04fd92ef5")] = DCE_RPC_lsa_ds;
|
|
||||||
|
|
||||||
the_map[UUID("000001a0-0000-0000-c000-000000000046")] = DCE_RPC_ISCMActivator;
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
return the_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to remember mapped DCE/RPC endpoints and parse the follow-up
|
|
||||||
// connections as DCE/RPC sessions.
|
|
||||||
map<dce_rpc_endpoint_addr, UUID> dce_rpc_endpoints;
|
|
||||||
|
|
||||||
static bool is_mapped_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr)
|
|
||||||
{
|
|
||||||
return dce_rpc_endpoints.find(addr) != dce_rpc_endpoints.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_mapped_dce_rpc_endpoint(const ConnID* id, TransportProto proto)
|
|
||||||
{
|
|
||||||
if ( id->dst_addr.GetFamily() == IPv6 )
|
|
||||||
// TODO: Does the protocol support v6 addresses? #773
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dce_rpc_endpoint_addr addr;
|
|
||||||
addr.addr = id->dst_addr;
|
|
||||||
addr.port = ntohs(id->dst_port);
|
|
||||||
addr.proto = proto;
|
|
||||||
|
|
||||||
return is_mapped_dce_rpc_endpoint(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr,
|
|
||||||
const UUID& uuid)
|
|
||||||
{
|
|
||||||
DEBUG_MSG("Adding endpoint %s @ %s\n",
|
|
||||||
uuid.to_string(), addr.to_string().c_str());
|
|
||||||
dce_rpc_endpoints[addr] = uuid;
|
|
||||||
|
|
||||||
// FIXME: Once we can pass the cookie to the analyzer, we can get rid
|
|
||||||
// of the dce_rpc_endpoints table.
|
|
||||||
// FIXME: Don't hard-code the timeout.
|
|
||||||
|
|
||||||
analyzer_mgr->ScheduleAnalyzer(IPAddr(), addr.addr, addr.port, addr.proto,
|
|
||||||
"DCE_RPC", 5 * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Header::DCE_RPC_Header(analyzer::Analyzer* a, const u_char* b)
|
|
||||||
{
|
|
||||||
analyzer = a;
|
|
||||||
bytes = b;
|
|
||||||
|
|
||||||
// This checks whether it's both the first fragment *and*
|
|
||||||
// the last fragment.
|
|
||||||
if ( (bytes[3] & 0x3) != 0x3 )
|
|
||||||
{
|
|
||||||
fragmented = 1;
|
|
||||||
Weird("Fragmented DCE/RPC message");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fragmented = 0;
|
|
||||||
|
|
||||||
ptype = (BifEnum::dce_rpc_ptype) bytes[2];
|
|
||||||
frag_len = extract_uint16(LittleEndian(), bytes + 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Session::DCE_RPC_Session(analyzer::Analyzer* a)
|
|
||||||
: analyzer(a),
|
|
||||||
uuid("00000000-0000-0000-0000-000000000000"),
|
|
||||||
if_id(BifEnum::DCE_RPC_unknown_if)
|
|
||||||
{
|
|
||||||
opnum = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DCE_RPC_Session::LooksLikeRPC(int len, const u_char* msg)
|
|
||||||
{
|
|
||||||
// if ( ! is_IPC )
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::DCE_RPC_Header h;
|
|
||||||
h.Parse(msg, msg + len);
|
|
||||||
if ( h.rpc_vers() == 5 && h.rpc_vers_minor() == 0 )
|
|
||||||
{
|
|
||||||
if ( h.frag_length() == len )
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DEBUG_MSG("length mismatch: %d != %d\n",
|
|
||||||
h.frag_length(), len);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& )
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverPDU(int is_orig, int len, const u_char* data)
|
|
||||||
{
|
|
||||||
if ( dce_rpc_message )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(is_orig, TYPE_BOOL));
|
|
||||||
vl->append(new EnumVal(data[2], BifType::Enum::dce_rpc_ptype));
|
|
||||||
vl->append(new StringVal(len, (const char*) data));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_message, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO: handle incremental input
|
|
||||||
binpac::DCE_RPC::DCE_RPC_PDU pdu;
|
|
||||||
pdu.Parse(data, data + len);
|
|
||||||
|
|
||||||
switch ( pdu.header()->PTYPE() ) {
|
|
||||||
case binpac::DCE_RPC::DCE_RPC_BIND:
|
|
||||||
case binpac::DCE_RPC::DCE_RPC_ALTER_CONTEXT:
|
|
||||||
DeliverBind(&pdu);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC::DCE_RPC_REQUEST:
|
|
||||||
DeliverRequest(&pdu);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC::DCE_RPC_RESPONSE:
|
|
||||||
DeliverResponse(&pdu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& e )
|
|
||||||
{
|
|
||||||
analyzer->Weird(e.msg().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverBind(const binpac::DCE_RPC::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::DCE_RPC_Bind* bind = pdu->body()->bind();
|
|
||||||
|
|
||||||
for ( int i = 0; i < bind->context_list()->num_contexts(); ++i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::ContextRequest* elem =
|
|
||||||
(*bind->context_list()->request_contexts())[i];
|
|
||||||
|
|
||||||
uuid = UUID(elem->abstract_syntax()->uuid().begin());
|
|
||||||
uuid_map_t::const_iterator uuid_it =
|
|
||||||
well_known_uuid_map().find(uuid);
|
|
||||||
|
|
||||||
if ( uuid_it == well_known_uuid_map().end() )
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
// conn->Weird(fmt("Unknown DCE_RPC interface %s",
|
|
||||||
// uuid.to_string()));
|
|
||||||
#endif
|
|
||||||
if_id = BifEnum::DCE_RPC_unknown_if;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if_id = uuid_it->second;
|
|
||||||
|
|
||||||
if ( dce_rpc_bind )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new StringVal(uuid.to_string()));
|
|
||||||
// vl->append(new EnumVal(if_id, BifType::Enum::dce_rpc_if_id));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_bind, vl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverRequest(const binpac::DCE_RPC::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::DCE_RPC_Request* req = pdu->body()->request();
|
|
||||||
|
|
||||||
opnum = req->opnum();
|
|
||||||
|
|
||||||
if ( dce_rpc_request )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(opnum, TYPE_COUNT));
|
|
||||||
vl->append(new StringVal(req->stub().length(),
|
|
||||||
(const char*) req->stub().begin()));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_request, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( if_id ) {
|
|
||||||
case BifEnum::DCE_RPC_epmapper:
|
|
||||||
DeliverEpmapperRequest(pdu, req);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverResponse(const binpac::DCE_RPC::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::DCE_RPC_Response* resp = pdu->body()->response();
|
|
||||||
|
|
||||||
if ( dce_rpc_response )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(opnum, TYPE_COUNT));
|
|
||||||
vl->append(new StringVal(resp->stub().length(),
|
|
||||||
(const char*) resp->stub().begin()));
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_response, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( if_id ) {
|
|
||||||
case BifEnum::DCE_RPC_epmapper:
|
|
||||||
DeliverEpmapperResponse(pdu, resp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperRequest(
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_PDU* /* pdu */,
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_Request* /* req */)
|
|
||||||
{
|
|
||||||
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
|
||||||
// ### TODO(rpang): generate an event on epmapper request
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperResponse(
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_Response* resp)
|
|
||||||
{
|
|
||||||
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
|
||||||
switch ( opnum ) {
|
|
||||||
case 3: // Map
|
|
||||||
DeliverEpmapperMapResponse(pdu, resp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperMapResponse(
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC::DCE_RPC_Response* resp)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::epmapper_map_resp epm_resp;
|
|
||||||
|
|
||||||
epm_resp.Parse(resp->stub().begin(), resp->stub().end(),
|
|
||||||
pdu->byteorder());
|
|
||||||
|
|
||||||
for ( unsigned int twr_i = 0;
|
|
||||||
twr_i < epm_resp.towers()->actual_count(); ++twr_i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::epm_tower* twr =
|
|
||||||
(*epm_resp.towers()->towers())[twr_i]->tower();
|
|
||||||
|
|
||||||
mapped.addr = dce_rpc_endpoint_addr();
|
|
||||||
mapped.uuid = UUID();
|
|
||||||
|
|
||||||
for ( int floor_i = 0; floor_i < twr->num_floors();
|
|
||||||
++floor_i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC::epm_floor* floor =
|
|
||||||
(*twr->floors())[floor_i];
|
|
||||||
|
|
||||||
switch ( floor->protocol() ) {
|
|
||||||
case binpac::DCE_RPC::EPM_PROTOCOL_UUID:
|
|
||||||
if ( floor_i == 0 )
|
|
||||||
mapped.uuid = UUID(floor->lhs()->data()->uuid()->if_uuid());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC::EPM_PROTOCOL_TCP:
|
|
||||||
mapped.addr.port =
|
|
||||||
floor->rhs()->data()->tcp();
|
|
||||||
mapped.addr.proto = TRANSPORT_TCP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC::EPM_PROTOCOL_UDP:
|
|
||||||
mapped.addr.port =
|
|
||||||
floor->rhs()->data()->udp();
|
|
||||||
mapped.addr.proto = TRANSPORT_UDP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC::EPM_PROTOCOL_IP:
|
|
||||||
uint32 hostip = floor->rhs()->data()->ip();
|
|
||||||
mapped.addr.addr = IPAddr(IPv4, &hostip, IPAddr::Host);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( mapped.addr.is_valid_addr() )
|
|
||||||
add_dce_rpc_endpoint(mapped.addr, mapped.uuid);
|
|
||||||
|
|
||||||
if ( epm_map_response )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new StringVal(mapped.uuid.to_string()));
|
|
||||||
vl->append(new PortVal(mapped.addr.port, mapped.addr.proto));
|
|
||||||
vl->append(new AddrVal(mapped.addr.addr));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(epm_map_response, vl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& e )
|
|
||||||
{
|
|
||||||
analyzer->Weird(e.msg().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Contents_DCE_RPC_Analyzer::Contents_DCE_RPC_Analyzer(Connection* conn,
|
|
||||||
bool orig, DCE_RPC_Session* arg_session, bool speculative)
|
|
||||||
: tcp::TCP_SupportAnalyzer("CONTENTS_DCE_RPC", conn, orig)
|
|
||||||
{
|
|
||||||
session = arg_session;
|
|
||||||
msg_buf = 0;
|
|
||||||
buf_len = 0;
|
|
||||||
speculation = speculative ? 0 : 1;
|
|
||||||
|
|
||||||
InitState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::InitState()
|
|
||||||
{
|
|
||||||
// Allocate space for header.
|
|
||||||
if ( ! msg_buf )
|
|
||||||
{
|
|
||||||
buf_len = DCE_RPC_HEADER_LENGTH;
|
|
||||||
msg_buf = new u_char[buf_len];
|
|
||||||
}
|
|
||||||
|
|
||||||
buf_n = 0;
|
|
||||||
msg_len = 0;
|
|
||||||
hdr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Contents_DCE_RPC_Analyzer::~Contents_DCE_RPC_Analyzer()
|
|
||||||
{
|
|
||||||
delete [] msg_buf;
|
|
||||||
delete hdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
|
||||||
{
|
|
||||||
tcp::TCP_SupportAnalyzer::DeliverStream(len, data, orig);
|
|
||||||
|
|
||||||
tcp::TCP_Analyzer* tcp =
|
|
||||||
static_cast<tcp::TCP_ApplicationAnalyzer*>(Parent())->TCP();
|
|
||||||
|
|
||||||
if ( tcp->HadGap(orig) || tcp->IsPartial() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( speculation == 0 ) // undecided
|
|
||||||
{
|
|
||||||
if ( ! DCE_RPC_Session::LooksLikeRPC(len, data) )
|
|
||||||
speculation = -1;
|
|
||||||
else
|
|
||||||
speculation = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( speculation < 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
ASSERT(buf_len >= DCE_RPC_HEADER_LENGTH);
|
|
||||||
while ( len > 0 )
|
|
||||||
{
|
|
||||||
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
|
||||||
{
|
|
||||||
while ( buf_n < DCE_RPC_HEADER_LENGTH && len > 0 )
|
|
||||||
{
|
|
||||||
msg_buf[buf_n] = *data;
|
|
||||||
++buf_n; ++data; --len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( ! ParseHeader() )
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( buf_n < msg_len && len > 0 )
|
|
||||||
{
|
|
||||||
msg_buf[buf_n] = *data;
|
|
||||||
++buf_n; ++data; --len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( buf_n < msg_len )
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( msg_len > 0 )
|
|
||||||
DeliverPDU(msg_len, msg_buf);
|
|
||||||
// Reset for next message
|
|
||||||
InitState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::DeliverPDU(int len, const u_char* data)
|
|
||||||
{
|
|
||||||
session->DeliverPDU(IsOrig(), len, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Contents_DCE_RPC_Analyzer::ParseHeader()
|
|
||||||
{
|
|
||||||
delete hdr;
|
|
||||||
hdr = 0;
|
|
||||||
|
|
||||||
if ( msg_buf[0] != 5 ) // DCE/RPC version
|
|
||||||
{
|
|
||||||
Conn()->Weird("DCE/RPC_version_error (non-DCE/RPC?)");
|
|
||||||
Conn()->SetSkip(1);
|
|
||||||
msg_len = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr = new DCE_RPC_Header(this, msg_buf);
|
|
||||||
|
|
||||||
msg_len = hdr->FragLen();
|
|
||||||
if ( msg_len > buf_len )
|
|
||||||
{
|
|
||||||
u_char* new_msg_buf = new u_char[msg_len];
|
|
||||||
memcpy(new_msg_buf, msg_buf, buf_n);
|
|
||||||
delete [] msg_buf;
|
|
||||||
buf_len = msg_len;
|
|
||||||
msg_buf = new_msg_buf;
|
|
||||||
hdr->SetBytes(new_msg_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection* conn, bool arg_speculative)
|
|
||||||
: tcp::TCP_ApplicationAnalyzer("DCE_RPC", conn)
|
: tcp::TCP_ApplicationAnalyzer("DCE_RPC", conn)
|
||||||
{
|
{
|
||||||
session = new DCE_RPC_Session(this);
|
interp = new binpac::DCE_RPC::DCE_RPC_Conn(this);
|
||||||
speculative = arg_speculative;
|
|
||||||
|
|
||||||
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, true, session,
|
|
||||||
speculative));
|
|
||||||
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, false, session,
|
|
||||||
speculative));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DCE_RPC_Analyzer::~DCE_RPC_Analyzer()
|
DCE_RPC_Analyzer::~DCE_RPC_Analyzer()
|
||||||
{
|
{
|
||||||
delete session;
|
delete interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Analyzer::Done()
|
||||||
|
{
|
||||||
|
TCP_ApplicationAnalyzer::Done();
|
||||||
|
|
||||||
|
interp->FlowEOF(true);
|
||||||
|
interp->FlowEOF(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Analyzer::EndpointEOF(bool is_orig)
|
||||||
|
{
|
||||||
|
TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
||||||
|
interp->FlowEOF(is_orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Analyzer::Undelivered(uint64 seq, int len, bool orig)
|
||||||
|
{
|
||||||
|
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||||
|
interp->NewGap(orig, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||||
|
{
|
||||||
|
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||||
|
|
||||||
|
assert(TCP());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
interp->NewData(orig, data, data + len);
|
||||||
|
}
|
||||||
|
catch ( const binpac::Exception& e )
|
||||||
|
{
|
||||||
|
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ protected:
|
||||||
string s;
|
string s;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* uuid_to_string(const u_char* uuid_data);
|
//const char* uuid_to_string(const u_char* uuid_data);
|
||||||
|
|
||||||
struct dce_rpc_endpoint_addr {
|
struct dce_rpc_endpoint_addr {
|
||||||
// All fields are in host byteorder.
|
// All fields are in host byteorder.
|
||||||
|
@ -88,6 +88,7 @@ enum DCE_RPC_PTYPE {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
#define DCE_RPC_HEADER_LENGTH 16
|
#define DCE_RPC_HEADER_LENGTH 16
|
||||||
|
|
||||||
class DCE_RPC_Header {
|
class DCE_RPC_Header {
|
||||||
|
@ -172,18 +173,23 @@ protected:
|
||||||
|
|
||||||
DCE_RPC_Session* session;
|
DCE_RPC_Session* session;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
||||||
public:
|
public:
|
||||||
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
|
DCE_RPC_Analyzer(Connection* conn);
|
||||||
~DCE_RPC_Analyzer();
|
~DCE_RPC_Analyzer();
|
||||||
|
|
||||||
|
virtual void Done();
|
||||||
|
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||||
|
virtual void Undelivered(uint64 seq, int len, bool orig);
|
||||||
|
virtual void EndpointEOF(bool is_orig);
|
||||||
|
|
||||||
static analyzer::Analyzer* Instantiate(Connection* conn)
|
static analyzer::Analyzer* Instantiate(Connection* conn)
|
||||||
{ return new DCE_RPC_Analyzer(conn); }
|
{ return new DCE_RPC_Analyzer(conn); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DCE_RPC_Session* session;
|
binpac::DCE_RPC::DCE_RPC_Conn* interp;
|
||||||
bool speculative;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // namespace analyzer::*
|
} } // namespace analyzer::*
|
||||||
|
|
|
@ -13,7 +13,7 @@ public:
|
||||||
plugin::Configuration Configure()
|
plugin::Configuration Configure()
|
||||||
{
|
{
|
||||||
AddComponent(new ::analyzer::Component("DCE_RPC", ::analyzer::dce_rpc::DCE_RPC_Analyzer::Instantiate));
|
AddComponent(new ::analyzer::Component("DCE_RPC", ::analyzer::dce_rpc::DCE_RPC_Analyzer::Instantiate));
|
||||||
AddComponent(new ::analyzer::Component("Contents_DCE_RPC", 0));
|
//AddComponent(new ::analyzer::Component("Contents_DCE_RPC", 0));
|
||||||
|
|
||||||
plugin::Configuration config;
|
plugin::Configuration config;
|
||||||
config.name = "Bro::DCE_RPC";
|
config.name = "Bro::DCE_RPC";
|
||||||
|
|
|
@ -1,135 +1,163 @@
|
||||||
# DCE/RPC protocol data unit.
|
# DCE/RPC protocol data unit.
|
||||||
|
|
||||||
type DCE_RPC_PDU = record {
|
refine connection DCE_RPC_Conn += {
|
||||||
# Set header's byteorder to little-endian (or big-endian) to
|
%member{
|
||||||
# avoid cyclic dependency.
|
map<uint16, uint16> cont_id_opnum_map;
|
||||||
header : DCE_RPC_Header;
|
%}
|
||||||
# TODO: bring back reassembly. It was having trouble.
|
|
||||||
#frag : bytestring &length = body_length;
|
function get_cont_id_opnum_map(cont_id: uint16): uint16
|
||||||
body : DCE_RPC_Body(header);
|
%{
|
||||||
auth : DCE_RPC_Auth(header);
|
return cont_id_opnum_map[cont_id];
|
||||||
} &let {
|
%}
|
||||||
#body_length : int = header.frag_length - sizeof(header) - header.auth_length;
|
|
||||||
#frag_reassembled : bool = $context.flow.reassemble_fragment(frag, header.lastfrag);
|
function set_cont_id_opnum_map(cont_id: uint16, opnum: uint16): bool
|
||||||
#body : DCE_RPC_Body(header)
|
%{
|
||||||
# withinput $context.flow.reassembled_body()
|
cont_id_opnum_map[cont_id] = opnum;
|
||||||
# &if frag_reassembled;
|
return true;
|
||||||
} &byteorder = header.byteorder,
|
%}
|
||||||
&length = header.frag_length;
|
|
||||||
|
function proc_dce_rpc_pdu(pdu: DCE_RPC_PDU): bool
|
||||||
|
%{
|
||||||
|
// If a whole pdu message parsed ok, let's confirm the protocol
|
||||||
|
bro_analyzer()->ProtocolConfirmation();
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_dce_rpc_message(header: DCE_RPC_Header): bool
|
||||||
|
%{
|
||||||
|
if ( dce_rpc_message )
|
||||||
|
{
|
||||||
|
BifEvent::generate_dce_rpc_message(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
${header.is_orig},
|
||||||
|
${header.PTYPE},
|
||||||
|
new EnumVal(${header.PTYPE}, BifType::Enum::DCE_RPC::PType));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function process_dce_rpc_bind(bind: DCE_RPC_Bind): bool
|
||||||
|
%{
|
||||||
|
|
||||||
|
if ( dce_rpc_bind )
|
||||||
|
{
|
||||||
|
// Go over the elements, each having a UUID
|
||||||
|
$const_def{bind_elems = bind.context_list};
|
||||||
|
for ( int i = 0; i < ${bind_elems.num_contexts}; ++i )
|
||||||
|
{
|
||||||
|
$const_def{uuid = bind_elems.request_contexts[i].abstract_syntax.uuid};
|
||||||
|
$const_def{version = bind_elems.request_contexts[i].abstract_syntax.version};
|
||||||
|
|
||||||
|
// Queue the event
|
||||||
|
BifEvent::generate_dce_rpc_bind(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
bytestring_to_val(${uuid}),
|
||||||
|
new StringVal(fmt("%d.0", ${version})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function process_dce_rpc_bind_ack(bind: DCE_RPC_Bind_Ack): bool
|
||||||
|
%{
|
||||||
|
if ( dce_rpc_bind_ack )
|
||||||
|
{
|
||||||
|
StringVal *sec_addr;
|
||||||
|
// Remove the null from the end of the string if it's there.
|
||||||
|
if ( *(${bind.sec_addr}.begin() + ${bind.sec_addr}.length()) == 0 )
|
||||||
|
sec_addr = new StringVal(${bind.sec_addr}.length()-1, (const char*) ${bind.sec_addr}.begin());
|
||||||
|
else
|
||||||
|
sec_addr = new StringVal(${bind.sec_addr}.length(), (const char*) ${bind.sec_addr}.begin());
|
||||||
|
|
||||||
|
BifEvent::generate_dce_rpc_bind_ack(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
sec_addr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function process_dce_rpc_request(req: DCE_RPC_Request): bool
|
||||||
|
%{
|
||||||
|
if ( dce_rpc_request )
|
||||||
|
{
|
||||||
|
BifEvent::generate_dce_rpc_request(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
${req.opnum},
|
||||||
|
bytestring_to_val(${req.stub}));
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cont_id_opnum_map(${req.context_id},
|
||||||
|
${req.opnum});
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function process_dce_rpc_response(resp: DCE_RPC_Response): bool
|
||||||
|
%{
|
||||||
|
if ( dce_rpc_response )
|
||||||
|
{
|
||||||
|
BifEvent::generate_dce_rpc_response(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
get_cont_id_opnum_map(${resp.context_id}),
|
||||||
|
bytestring_to_val(${resp.stub}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
refine flow DCE_RPC_Flow += {
|
||||||
|
#%member{
|
||||||
|
#FlowBuffer frag_reassembler_;
|
||||||
|
#%}
|
||||||
|
|
||||||
|
# Fragment reassembly.
|
||||||
|
#function reassemble_fragment(frag: bytestring, lastfrag: bool): bool
|
||||||
|
# %{
|
||||||
|
# int orig_data_length = frag_reassembler_.data_length();
|
||||||
|
#
|
||||||
|
# frag_reassembler_.NewData(frag.begin(), frag.end());
|
||||||
|
#
|
||||||
|
# int new_frame_length = orig_data_length + frag.length();
|
||||||
|
# if ( orig_data_length == 0 )
|
||||||
|
# frag_reassembler_.NewFrame(new_frame_length, false);
|
||||||
|
# else
|
||||||
|
# frag_reassembler_.GrowFrame(new_frame_length);
|
||||||
|
#
|
||||||
|
# return lastfrag;
|
||||||
|
# %}
|
||||||
|
|
||||||
|
#function reassembled_body(): const_bytestring
|
||||||
|
# %{
|
||||||
|
# return const_bytestring(
|
||||||
|
# frag_reassembler_.begin(),
|
||||||
|
# frag_reassembler_.end());
|
||||||
|
# %}
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_PDU += &let {
|
||||||
|
proc = $context.connection.proc_dce_rpc_pdu(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_Header += &let {
|
||||||
|
proc = $context.connection.proc_dce_rpc_message(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_Bind += &let {
|
||||||
|
proc = $context.connection.process_dce_rpc_bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_Bind_Ack += &let {
|
||||||
|
proc = $context.connection.process_dce_rpc_bind_ack(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_Request += &let {
|
||||||
|
proc = $context.connection.process_dce_rpc_request(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
refine typeattr DCE_RPC_Response += &let {
|
||||||
|
proc = $context.connection.process_dce_rpc_response(this);
|
||||||
|
};
|
||||||
|
|
||||||
#connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
|
|
||||||
# upflow = DCE_RPC_Flow(true);
|
|
||||||
# downflow = DCE_RPC_Flow(false);
|
|
||||||
#
|
|
||||||
# %member{
|
|
||||||
# map<uint16, uint16> cont_id_opnum_map;
|
|
||||||
# %}
|
|
||||||
#
|
|
||||||
# function get_cont_id_opnum_map(cont_id: uint16): uint16
|
|
||||||
# %{
|
|
||||||
# return cont_id_opnum_map[cont_id];
|
|
||||||
# %}
|
|
||||||
#
|
|
||||||
# function set_cont_id_opnum_map(cont_id: uint16, opnum: uint16): bool
|
|
||||||
# %{
|
|
||||||
# cont_id_opnum_map[cont_id] = opnum;
|
|
||||||
# return true;
|
|
||||||
# %}
|
|
||||||
#};
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#flow DCE_RPC_Flow(is_orig: bool) {
|
|
||||||
# flowunit = DCE_RPC_PDU withcontext (connection, this);
|
|
||||||
#
|
|
||||||
# #%member{
|
|
||||||
# #FlowBuffer frag_reassembler_;
|
|
||||||
# #%}
|
|
||||||
#
|
|
||||||
# # Fragment reassembly.
|
|
||||||
# #function reassemble_fragment(frag: bytestring, lastfrag: bool): bool
|
|
||||||
# # %{
|
|
||||||
# # int orig_data_length = frag_reassembler_.data_length();
|
|
||||||
# #
|
|
||||||
# # frag_reassembler_.NewData(frag.begin(), frag.end());
|
|
||||||
# #
|
|
||||||
# # int new_frame_length = orig_data_length + frag.length();
|
|
||||||
# # if ( orig_data_length == 0 )
|
|
||||||
# # frag_reassembler_.NewFrame(new_frame_length, false);
|
|
||||||
# # else
|
|
||||||
# # frag_reassembler_.GrowFrame(new_frame_length);
|
|
||||||
# #
|
|
||||||
# # return lastfrag;
|
|
||||||
# # %}
|
|
||||||
#
|
|
||||||
# #function reassembled_body(): const_bytestring
|
|
||||||
# # %{
|
|
||||||
# # return const_bytestring(
|
|
||||||
# # frag_reassembler_.begin(),
|
|
||||||
# # frag_reassembler_.end());
|
|
||||||
# # %}
|
|
||||||
#
|
|
||||||
# # Bind.
|
|
||||||
# function process_dce_rpc_bind(bind: DCE_RPC_Bind): bool
|
|
||||||
# %{
|
|
||||||
# $const_def{bind_elems = bind.context_list};
|
|
||||||
#
|
|
||||||
# if ( ${bind_elems.num_contexts} > 1 )
|
|
||||||
# {
|
|
||||||
# ${connection.bro_analyzer}->Weird("DCE_RPC_bind_to_multiple_interfaces");
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# if ( dce_rpc_bind )
|
|
||||||
# {
|
|
||||||
# // Go over the elements, each having a UUID
|
|
||||||
# for ( int i = 0; i < ${bind_elems.num_contexts}; ++i )
|
|
||||||
# {
|
|
||||||
# $const_def{uuid =
|
|
||||||
# bind_elems.request_contexts[i].abstract_syntax.uuid};
|
|
||||||
#
|
|
||||||
# // Queue the event
|
|
||||||
# BifEvent::generate_dce_rpc_bind(
|
|
||||||
# ${connection.bro_analyzer},
|
|
||||||
# ${connection.bro_analyzer}->Conn(),
|
|
||||||
# bytestring_to_val(${uuid}));
|
|
||||||
#
|
|
||||||
# // Set the connection's UUID
|
|
||||||
# // ${connection}->set_uuid(${uuid});
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# return ${bind_elems.num_contexts} > 0;
|
|
||||||
# %}
|
|
||||||
#
|
|
||||||
# # Request.
|
|
||||||
# function process_dce_rpc_request(req: DCE_RPC_Request): bool
|
|
||||||
# %{
|
|
||||||
# if ( dce_rpc_request )
|
|
||||||
# {
|
|
||||||
# BifEvent::generate_dce_rpc_request(
|
|
||||||
# ${connection.bro_analyzer},
|
|
||||||
# ${connection.bro_analyzer}->Conn(),
|
|
||||||
# ${req.opnum},
|
|
||||||
# bytestring_to_val(${req.stub}));
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# ${connection}->set_cont_id_opnum_map(${req.context_id},
|
|
||||||
# ${req.opnum});
|
|
||||||
#
|
|
||||||
# return true;
|
|
||||||
# %}
|
|
||||||
#
|
|
||||||
# # Response.
|
|
||||||
# function process_dce_rpc_response(resp: DCE_RPC_Response): bool
|
|
||||||
# %{
|
|
||||||
# if ( dce_rpc_response )
|
|
||||||
# {
|
|
||||||
# BifEvent::generate_dce_rpc_response(
|
|
||||||
# ${connection.bro_analyzer},
|
|
||||||
# ${connection.bro_analyzer}->Conn(),
|
|
||||||
# ${connection}->get_cont_id_opnum_map(${resp.context_id}),
|
|
||||||
# bytestring_to_val(${resp.stub}));
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# return true;
|
|
||||||
# %}
|
|
||||||
#};
|
|
||||||
|
|
|
@ -29,6 +29,23 @@ type context_handle = record {
|
||||||
uuid : bytestring &length = 16;
|
uuid : bytestring &length = 16;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DCE_RPC_PDU(is_orig: bool) = record {
|
||||||
|
# Set header's byteorder to little-endian (or big-endian) to
|
||||||
|
# avoid cyclic dependency.
|
||||||
|
header : DCE_RPC_Header(is_orig);
|
||||||
|
# TODO: bring back reassembly. It was having trouble.
|
||||||
|
#frag : bytestring &length = body_length;
|
||||||
|
body : DCE_RPC_Body(header);
|
||||||
|
auth : DCE_RPC_Auth(header);
|
||||||
|
} &let {
|
||||||
|
#body_length : int = header.frag_length - sizeof(header) - header.auth_length;
|
||||||
|
#frag_reassembled : bool = $context.flow.reassemble_fragment(frag, header.lastfrag);
|
||||||
|
#body : DCE_RPC_Body(header)
|
||||||
|
# withinput $context.flow.reassembled_body()
|
||||||
|
# &if frag_reassembled;
|
||||||
|
} &byteorder = header.byteorder, &length = header.frag_length;
|
||||||
|
|
||||||
|
|
||||||
#type rpc_if_id_t = record {
|
#type rpc_if_id_t = record {
|
||||||
# if_uuid : bytestring &length = 16;
|
# if_uuid : bytestring &length = 16;
|
||||||
# vers_major : uint16;
|
# vers_major : uint16;
|
||||||
|
@ -46,7 +63,7 @@ type NDR_Format = record {
|
||||||
#### There might be a endianness problem here: the frag_length
|
#### There might be a endianness problem here: the frag_length
|
||||||
# causes problems despite the NDR_Format having a byteorder set.
|
# causes problems despite the NDR_Format having a byteorder set.
|
||||||
|
|
||||||
type DCE_RPC_Header = record {
|
type DCE_RPC_Header(is_orig: bool) = record {
|
||||||
rpc_vers : uint8 &check(rpc_vers == 5);
|
rpc_vers : uint8 &check(rpc_vers == 5);
|
||||||
rpc_vers_minor : uint8;
|
rpc_vers_minor : uint8;
|
||||||
PTYPE : uint8;
|
PTYPE : uint8;
|
||||||
|
|
|
@ -2,16 +2,26 @@
|
||||||
%include bro.pac
|
%include bro.pac
|
||||||
|
|
||||||
%extern{
|
%extern{
|
||||||
#include "events.bif.h"
|
#include "types.bif.h"
|
||||||
|
#include "events.bif.h"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
analyzer DCE_RPC withcontext {};
|
analyzer DCE_RPC withcontext {
|
||||||
|
connection : DCE_RPC_Conn;
|
||||||
|
flow : DCE_RPC_Flow;
|
||||||
|
};
|
||||||
|
|
||||||
#analyzer DCE_RPC withcontext {
|
connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
|
||||||
# connection : DCE_RPC_Conn;
|
upflow = DCE_RPC_Flow(true);
|
||||||
# flow : DCE_RPC_Flow;
|
downflow = DCE_RPC_Flow(false);
|
||||||
#};
|
};
|
||||||
|
|
||||||
%include dce_rpc-protocol.pac
|
%include dce_rpc-protocol.pac
|
||||||
|
|
||||||
|
# Now we define the flow:
|
||||||
|
flow DCE_RPC_Flow(is_orig: bool) {
|
||||||
|
flowunit = DCE_RPC_PDU(is_orig) withcontext(connection, this);
|
||||||
|
};
|
||||||
|
|
||||||
%include epmapper.pac
|
%include epmapper.pac
|
||||||
%include dce_rpc-analyzer.pac
|
%include dce_rpc-analyzer.pac
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
%include bro.pac
|
|
||||||
|
|
||||||
%extern{
|
|
||||||
#include "events.bif.h"
|
|
||||||
%}
|
|
||||||
|
|
||||||
analyzer DCE_RPC_Simple withcontext {};
|
|
||||||
|
|
||||||
%include dce_rpc-protocol.pac
|
|
||||||
|
|
||||||
type DCE_RPC_PDU = record {
|
|
||||||
# Set header's byteorder to little-endian (or big-endian) to
|
|
||||||
# avoid cyclic dependency.
|
|
||||||
header : DCE_RPC_Header;
|
|
||||||
body : DCE_RPC_Body(header)
|
|
||||||
&length = header.frag_length - sizeof(header) -
|
|
||||||
header.auth_length;
|
|
||||||
auth : DCE_RPC_Auth(header);
|
|
||||||
} &byteorder = header.byteorder,
|
|
||||||
&length = header.frag_length;
|
|
|
@ -2,54 +2,31 @@
|
||||||
##
|
##
|
||||||
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_request
|
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_request
|
||||||
## dce_rpc_response rpc_timeout
|
## dce_rpc_response rpc_timeout
|
||||||
##
|
event dce_rpc_message%(c: connection, is_orig: bool, ptype_id: count, ptype: DCE_RPC::PType%);
|
||||||
## .. todo:: Bro's current default configuration does not activate the protocol
|
|
||||||
## analyzer that generates this event; the corresponding script has not yet
|
|
||||||
## been ported to Bro 2.x. To still enable this event, one needs to
|
|
||||||
## register a port for it or add a DPD payload signature.
|
|
||||||
event dce_rpc_message%(c: connection, is_orig: bool, ptype: dce_rpc_ptype, msg: string%);
|
|
||||||
|
|
||||||
## TODO.
|
## TODO.
|
||||||
##
|
##
|
||||||
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_message dce_rpc_request
|
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_message dce_rpc_request
|
||||||
## dce_rpc_response rpc_timeout
|
## dce_rpc_response rpc_timeout
|
||||||
##
|
event dce_rpc_bind%(c: connection, uuid: string, version: string%);
|
||||||
## .. todo:: Bro's current default configuration does not activate the protocol
|
|
||||||
## analyzer that generates this event; the corresponding script has not yet
|
event dce_rpc_bind_ack%(c: connection, sec_addr: string%);
|
||||||
## been ported to Bro 2.x. To still enable this event, one needs to
|
|
||||||
## register a port for it or add a DPD payload signature.
|
|
||||||
event dce_rpc_bind%(c: connection, uuid: string%);
|
|
||||||
|
|
||||||
## TODO.
|
## TODO.
|
||||||
##
|
##
|
||||||
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
||||||
## dce_rpc_response rpc_timeout
|
## dce_rpc_response rpc_timeout
|
||||||
##
|
|
||||||
## .. todo:: Bro's current default configuration does not activate the protocol
|
|
||||||
## analyzer that generates this event; the corresponding script has not yet
|
|
||||||
## been ported to Bro 2.x. To still enable this event, one needs to
|
|
||||||
## register a port for it or add a DPD payload signature.
|
|
||||||
event dce_rpc_request%(c: connection, opnum: count, stub: string%);
|
event dce_rpc_request%(c: connection, opnum: count, stub: string%);
|
||||||
|
|
||||||
## TODO.
|
## TODO.
|
||||||
##
|
##
|
||||||
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
||||||
## dce_rpc_request rpc_timeout
|
## dce_rpc_request rpc_timeout
|
||||||
##
|
|
||||||
## .. todo:: Bro's current default configuration does not activate the protocol
|
|
||||||
## analyzer that generates this event; the corresponding script has not yet
|
|
||||||
## been ported to Bro 2.x. To still enable this event, one needs to
|
|
||||||
## register a port for it or add a DPD payload signature.
|
|
||||||
event dce_rpc_response%(c: connection, opnum: count, stub: string%);
|
event dce_rpc_response%(c: connection, opnum: count, stub: string%);
|
||||||
|
|
||||||
## TODO.
|
## TODO.
|
||||||
##
|
##
|
||||||
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
|
||||||
## dce_rpc_request dce_rpc_response rpc_timeout
|
## dce_rpc_request dce_rpc_response rpc_timeout
|
||||||
##
|
|
||||||
## .. todo:: Bro's current default configuration does not activate the protocol
|
|
||||||
## analyzer that generates this event; the corresponding script has not yet
|
|
||||||
## been ported to Bro 2.x. To still enable this event, one needs to
|
|
||||||
## register a port for it or add a DPD payload signature.
|
|
||||||
event epm_map_response%(c: connection, uuid: string, p: port, h: addr%);
|
event epm_map_response%(c: connection, uuid: string, p: port, h: addr%);
|
||||||
|
|
||||||
|
|
41
src/analyzer/protocol/dce-rpc/types.bif
Normal file
41
src/analyzer/protocol/dce-rpc/types.bif
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
module DCE_RPC;
|
||||||
|
|
||||||
|
enum PType %{
|
||||||
|
REQUEST,
|
||||||
|
PING,
|
||||||
|
RESPONSE,
|
||||||
|
FAULT,
|
||||||
|
WORKING,
|
||||||
|
NOCALL,
|
||||||
|
REJECT,
|
||||||
|
ACK,
|
||||||
|
CL_CANCEL,
|
||||||
|
FACK,
|
||||||
|
CANCEL_ACK,
|
||||||
|
BIND,
|
||||||
|
BIND_ACK,
|
||||||
|
BIND_NAK,
|
||||||
|
ALTER_CONTEXT,
|
||||||
|
ALTER_CONTEXT_RESP,
|
||||||
|
SHUTDOWN,
|
||||||
|
CO_CANCEL,
|
||||||
|
ORPHANED,
|
||||||
|
%}
|
||||||
|
|
||||||
|
enum IfID %{
|
||||||
|
unknown_if,
|
||||||
|
epmapper,
|
||||||
|
lsarpc,
|
||||||
|
lsa_ds,
|
||||||
|
mgmt,
|
||||||
|
netlogon,
|
||||||
|
samr,
|
||||||
|
srvsvc,
|
||||||
|
spoolss,
|
||||||
|
drs,
|
||||||
|
winspipe,
|
||||||
|
wkssvc,
|
||||||
|
oxid,
|
||||||
|
ISCMActivator,
|
||||||
|
%}
|
|
@ -47,7 +47,6 @@ bro_plugin_pac(
|
||||||
smb-pipe.pac
|
smb-pipe.pac
|
||||||
smb-mailslot.pac
|
smb-mailslot.pac
|
||||||
smb-ntlmssp.pac
|
smb-ntlmssp.pac
|
||||||
dce_rpc-protocol.pac
|
|
||||||
|
|
||||||
smb1-protocol.pac
|
smb1-protocol.pac
|
||||||
smb1-com-check-directory.pac
|
smb1-com-check-directory.pac
|
||||||
|
|
|
@ -1,588 +0,0 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include "DCE_RPC.h"
|
|
||||||
#include "Sessions.h"
|
|
||||||
|
|
||||||
#include "analyzer/Manager.h"
|
|
||||||
|
|
||||||
#include "events.bif.h"
|
|
||||||
|
|
||||||
using namespace analyzer::dce_rpc;
|
|
||||||
|
|
||||||
#define xbyte(b, n) (((const u_char*) (b))[n])
|
|
||||||
|
|
||||||
#define extract_uint16(little_endian, bytes) \
|
|
||||||
((little_endian) ? \
|
|
||||||
uint16(xbyte(bytes, 0)) | ((uint16(xbyte(bytes, 1))) << 8) : \
|
|
||||||
uint16(xbyte(bytes, 1)) | ((uint16(xbyte(bytes, 0))) << 8))
|
|
||||||
|
|
||||||
static int uuid_index[] = {
|
|
||||||
3, 2, 1, 0,
|
|
||||||
5, 4, 7, 6,
|
|
||||||
8, 9, 10, 11,
|
|
||||||
12, 13, 14, 15
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* analyzer::dce_rpc::uuid_to_string(const u_char* uuid_data)
|
|
||||||
{
|
|
||||||
static char s[1024];
|
|
||||||
char* sp = s;
|
|
||||||
|
|
||||||
for ( int i = 0; i < 16; ++i )
|
|
||||||
{
|
|
||||||
if ( i == 4 || i == 6 || i == 8 || i == 10 )
|
|
||||||
sp += snprintf(sp, s + sizeof(s) - sp, "-");
|
|
||||||
|
|
||||||
int j = uuid_index[i];
|
|
||||||
sp += snprintf(sp, s + sizeof(s) - sp, "%02x", uuid_data[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID()
|
|
||||||
{
|
|
||||||
memset(data, 0, 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const u_char d[16])
|
|
||||||
{
|
|
||||||
memcpy(data, d, 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const binpac::bytestring& uuid)
|
|
||||||
{
|
|
||||||
if ( uuid.length() != 16 )
|
|
||||||
reporter->InternalError("UUID length error");
|
|
||||||
memcpy(data, uuid.begin(), 16);
|
|
||||||
s = uuid_to_string(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID::UUID(const char* str)
|
|
||||||
{
|
|
||||||
s = string(str);
|
|
||||||
const char* sp = str;
|
|
||||||
int i;
|
|
||||||
for ( i = 0; i < 16; ++i )
|
|
||||||
{
|
|
||||||
if ( *sp == '-' )
|
|
||||||
++sp;
|
|
||||||
if ( ! *sp || ! *(sp+1) )
|
|
||||||
break;
|
|
||||||
|
|
||||||
data[uuid_index[i]] =
|
|
||||||
(u_char) (decode_hex(*sp) * 16 + decode_hex(*(sp+1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( i != 16 )
|
|
||||||
reporter->InternalError("invalid UUID string: %s", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef map<UUID, BifEnum::dce_rpc_if_id> uuid_map_t;
|
|
||||||
|
|
||||||
static uuid_map_t& well_known_uuid_map()
|
|
||||||
{
|
|
||||||
static uuid_map_t the_map;
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
if ( initialized )
|
|
||||||
return the_map;
|
|
||||||
|
|
||||||
using namespace BifEnum;
|
|
||||||
|
|
||||||
the_map[UUID("e1af8308-5d1f-11c9-91a4-08002b14a0fa")] = DCE_RPC_epmapper;
|
|
||||||
|
|
||||||
the_map[UUID("afa8bd80-7d8a-11c9-bef4-08002b102989")] = DCE_RPC_mgmt;
|
|
||||||
|
|
||||||
// It's said that the following interfaces are merely aliases.
|
|
||||||
the_map[UUID("12345778-1234-abcd-ef00-0123456789ab")] = DCE_RPC_lsarpc;
|
|
||||||
the_map[UUID("12345678-1234-abcd-ef00-01234567cffb")] = DCE_RPC_netlogon;
|
|
||||||
the_map[UUID("12345778-1234-abcd-ef00-0123456789ac")] = DCE_RPC_samr;
|
|
||||||
|
|
||||||
// The next group of aliases.
|
|
||||||
the_map[UUID("4b324fc8-1670-01d3-1278-5a47bf6ee188")] = DCE_RPC_srvsvc;
|
|
||||||
the_map[UUID("12345678-1234-abcd-ef00-0123456789ab")] = DCE_RPC_spoolss;
|
|
||||||
the_map[UUID("45f52c28-7f9f-101a-b52b-08002b2efabe")] = DCE_RPC_winspipe;
|
|
||||||
the_map[UUID("6bffd098-a112-3610-9833-46c3f87e345a")] = DCE_RPC_wkssvc;
|
|
||||||
|
|
||||||
// DRS - NT directory replication service.
|
|
||||||
the_map[UUID("e3514235-4b06-11d1-ab04-00c04fc2dcd2")] = DCE_RPC_drs;
|
|
||||||
|
|
||||||
// "The IOXIDResolver RPC interface (formerly known as
|
|
||||||
// IObjectExporter) is remotely used to reach the local object
|
|
||||||
// resolver (OR)."
|
|
||||||
the_map[UUID("99fcfec4-5260-101b-bbcb-00aa0021347a")] = DCE_RPC_oxid;
|
|
||||||
|
|
||||||
the_map[UUID("3919286a-b10c-11d0-9ba8-00c04fd92ef5")] = DCE_RPC_lsa_ds;
|
|
||||||
|
|
||||||
the_map[UUID("000001a0-0000-0000-c000-000000000046")] = DCE_RPC_ISCMActivator;
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
return the_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to remember mapped DCE/RPC endpoints and parse the follow-up
|
|
||||||
// connections as DCE/RPC sessions.
|
|
||||||
map<dce_rpc_endpoint_addr, UUID> dce_rpc_endpoints;
|
|
||||||
|
|
||||||
static bool is_mapped_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr)
|
|
||||||
{
|
|
||||||
return dce_rpc_endpoints.find(addr) != dce_rpc_endpoints.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_mapped_dce_rpc_endpoint(const ConnID* id, TransportProto proto)
|
|
||||||
{
|
|
||||||
if ( id->dst_addr.GetFamily() == IPv6 )
|
|
||||||
// TODO: Does the protocol support v6 addresses? #773
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dce_rpc_endpoint_addr addr;
|
|
||||||
addr.addr = id->dst_addr;
|
|
||||||
addr.port = ntohs(id->dst_port);
|
|
||||||
addr.proto = proto;
|
|
||||||
|
|
||||||
return is_mapped_dce_rpc_endpoint(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr,
|
|
||||||
const UUID& uuid)
|
|
||||||
{
|
|
||||||
DEBUG_MSG("Adding endpoint %s @ %s\n",
|
|
||||||
uuid.to_string(), addr.to_string().c_str());
|
|
||||||
dce_rpc_endpoints[addr] = uuid;
|
|
||||||
|
|
||||||
// FIXME: Once we can pass the cookie to the analyzer, we can get rid
|
|
||||||
// of the dce_rpc_endpoints table.
|
|
||||||
// FIXME: Don't hard-code the timeout.
|
|
||||||
|
|
||||||
analyzer_mgr->ScheduleAnalyzer(IPAddr(), addr.addr, addr.port, addr.proto,
|
|
||||||
"DCE_RPC", 5 * 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Header::DCE_RPC_Header(analyzer::Analyzer* a, const u_char* b)
|
|
||||||
{
|
|
||||||
analyzer = a;
|
|
||||||
bytes = b;
|
|
||||||
|
|
||||||
// This checks whether it's both the first fragment *and*
|
|
||||||
// the last fragment.
|
|
||||||
if ( (bytes[3] & 0x3) != 0x3 )
|
|
||||||
{
|
|
||||||
fragmented = 1;
|
|
||||||
Weird("Fragmented DCE/RPC message");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fragmented = 0;
|
|
||||||
|
|
||||||
ptype = (BifEnum::dce_rpc_ptype) bytes[2];
|
|
||||||
frag_len = extract_uint16(LittleEndian(), bytes + 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Session::DCE_RPC_Session(analyzer::Analyzer* a)
|
|
||||||
: analyzer(a),
|
|
||||||
if_uuid("00000000-0000-0000-0000-000000000000"),
|
|
||||||
if_id(BifEnum::DCE_RPC_unknown_if)
|
|
||||||
{
|
|
||||||
opnum = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DCE_RPC_Session::LooksLikeRPC(int len, const u_char* msg)
|
|
||||||
{
|
|
||||||
// if ( ! is_IPC )
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::DCE_RPC_Header h;
|
|
||||||
h.Parse(msg, msg + len);
|
|
||||||
if ( h.rpc_vers() == 5 && h.rpc_vers_minor() == 0 )
|
|
||||||
{
|
|
||||||
if ( h.frag_length() == len )
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DEBUG_MSG("length mismatch: %d != %d\n",
|
|
||||||
h.frag_length(), len);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& )
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverPDU(int is_orig, int len, const u_char* data)
|
|
||||||
{
|
|
||||||
if ( dce_rpc_message )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(is_orig, TYPE_BOOL));
|
|
||||||
vl->append(new EnumVal(data[2], BifType::Enum::dce_rpc_ptype));
|
|
||||||
vl->append(new StringVal(len, (const char*) data));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_message, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO: handle incremental input
|
|
||||||
binpac::DCE_RPC_Simple::DCE_RPC_PDU pdu;
|
|
||||||
pdu.Parse(data, data + len);
|
|
||||||
|
|
||||||
switch ( pdu.header()->PTYPE() ) {
|
|
||||||
case binpac::DCE_RPC_Simple::DCE_RPC_BIND:
|
|
||||||
case binpac::DCE_RPC_Simple::DCE_RPC_ALTER_CONTEXT:
|
|
||||||
DeliverBind(&pdu);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC_Simple::DCE_RPC_REQUEST:
|
|
||||||
DeliverRequest(&pdu);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC_Simple::DCE_RPC_RESPONSE:
|
|
||||||
DeliverResponse(&pdu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& e )
|
|
||||||
{
|
|
||||||
analyzer->Weird(e.msg().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverBind(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::DCE_RPC_Bind* bind = pdu->body()->bind();
|
|
||||||
|
|
||||||
for ( int i = 0; i < bind->p_context_elem()->n_context_elem(); ++i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::p_cont_elem_t* elem =
|
|
||||||
(*bind->p_context_elem()->p_cont_elem())[i];
|
|
||||||
|
|
||||||
if_uuid = UUID(elem->abstract_syntax()->if_uuid().begin());
|
|
||||||
uuid_map_t::const_iterator uuid_it =
|
|
||||||
well_known_uuid_map().find(if_uuid);
|
|
||||||
|
|
||||||
if ( uuid_it == well_known_uuid_map().end() )
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
// conn->Weird(fmt("Unknown DCE_RPC interface %s",
|
|
||||||
// if_uuid.to_string()));
|
|
||||||
#endif
|
|
||||||
if_id = BifEnum::DCE_RPC_unknown_if;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if_id = uuid_it->second;
|
|
||||||
|
|
||||||
if ( dce_rpc_bind )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new StringVal(if_uuid.to_string()));
|
|
||||||
// vl->append(new EnumVal(if_id, BifType::Enum::dce_rpc_if_id));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_bind, vl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverRequest(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::DCE_RPC_Request* req = pdu->body()->request();
|
|
||||||
|
|
||||||
opnum = req->opnum();
|
|
||||||
|
|
||||||
if ( dce_rpc_request )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(opnum, TYPE_COUNT));
|
|
||||||
vl->append(new StringVal(req->stub().length(),
|
|
||||||
(const char*) req->stub().begin()));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_request, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( if_id ) {
|
|
||||||
case BifEnum::DCE_RPC_epmapper:
|
|
||||||
DeliverEpmapperRequest(pdu, req);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverResponse(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::DCE_RPC_Response* resp = pdu->body()->response();
|
|
||||||
|
|
||||||
if ( dce_rpc_response )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new Val(opnum, TYPE_COUNT));
|
|
||||||
vl->append(new StringVal(resp->stub().length(),
|
|
||||||
(const char*) resp->stub().begin()));
|
|
||||||
analyzer->ConnectionEvent(dce_rpc_response, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( if_id ) {
|
|
||||||
case BifEnum::DCE_RPC_epmapper:
|
|
||||||
DeliverEpmapperResponse(pdu, resp);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperRequest(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* /* pdu */,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Request* /* req */)
|
|
||||||
{
|
|
||||||
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
|
||||||
// ### TODO(rpang): generate an event on epmapper request
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperResponse(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp)
|
|
||||||
{
|
|
||||||
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
|
||||||
switch ( opnum ) {
|
|
||||||
case 3: // Map
|
|
||||||
DeliverEpmapperMapResponse(pdu, resp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DCE_RPC_Session::DeliverEpmapperMapResponse(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::epmapper_map_resp epm_resp;
|
|
||||||
|
|
||||||
epm_resp.Parse(resp->stub().begin(), resp->stub().end(),
|
|
||||||
pdu->byteorder());
|
|
||||||
|
|
||||||
for ( unsigned int twr_i = 0;
|
|
||||||
twr_i < epm_resp.towers()->actual_count(); ++twr_i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::epm_tower* twr =
|
|
||||||
(*epm_resp.towers()->towers())[twr_i]->tower();
|
|
||||||
|
|
||||||
mapped.addr = dce_rpc_endpoint_addr();
|
|
||||||
mapped.uuid = UUID();
|
|
||||||
|
|
||||||
for ( int floor_i = 0; floor_i < twr->num_floors();
|
|
||||||
++floor_i )
|
|
||||||
{
|
|
||||||
binpac::DCE_RPC_Simple::epm_floor* floor =
|
|
||||||
(*twr->floors())[floor_i];
|
|
||||||
|
|
||||||
switch ( floor->protocol() ) {
|
|
||||||
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_UUID:
|
|
||||||
if ( floor_i == 0 )
|
|
||||||
mapped.uuid = UUID(floor->lhs()->data()->uuid()->if_uuid());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_TCP:
|
|
||||||
mapped.addr.port =
|
|
||||||
floor->rhs()->data()->tcp();
|
|
||||||
mapped.addr.proto = TRANSPORT_TCP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_UDP:
|
|
||||||
mapped.addr.port =
|
|
||||||
floor->rhs()->data()->udp();
|
|
||||||
mapped.addr.proto = TRANSPORT_UDP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_IP:
|
|
||||||
uint32 hostip = floor->rhs()->data()->ip();
|
|
||||||
mapped.addr.addr = IPAddr(IPv4, &hostip, IPAddr::Host);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( mapped.addr.is_valid_addr() )
|
|
||||||
add_dce_rpc_endpoint(mapped.addr, mapped.uuid);
|
|
||||||
|
|
||||||
if ( epm_map_response )
|
|
||||||
{
|
|
||||||
val_list* vl = new val_list;
|
|
||||||
vl->append(analyzer->BuildConnVal());
|
|
||||||
vl->append(new StringVal(mapped.uuid.to_string()));
|
|
||||||
vl->append(new PortVal(mapped.addr.port, mapped.addr.proto));
|
|
||||||
vl->append(new AddrVal(mapped.addr.addr));
|
|
||||||
|
|
||||||
analyzer->ConnectionEvent(epm_map_response, vl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( const binpac::Exception& e )
|
|
||||||
{
|
|
||||||
analyzer->Weird(e.msg().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Contents_DCE_RPC_Analyzer::Contents_DCE_RPC_Analyzer(Connection* conn,
|
|
||||||
bool orig, DCE_RPC_Session* arg_session, bool speculative)
|
|
||||||
: tcp::TCP_SupportAnalyzer("CONTENTS_DCE_RPC", conn, orig)
|
|
||||||
{
|
|
||||||
session = arg_session;
|
|
||||||
msg_buf = 0;
|
|
||||||
buf_len = 0;
|
|
||||||
speculation = speculative ? 0 : 1;
|
|
||||||
|
|
||||||
InitState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::InitState()
|
|
||||||
{
|
|
||||||
// Allocate space for header.
|
|
||||||
if ( ! msg_buf )
|
|
||||||
{
|
|
||||||
buf_len = DCE_RPC_HEADER_LENGTH;
|
|
||||||
msg_buf = new u_char[buf_len];
|
|
||||||
}
|
|
||||||
|
|
||||||
buf_n = 0;
|
|
||||||
msg_len = 0;
|
|
||||||
hdr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Contents_DCE_RPC_Analyzer::~Contents_DCE_RPC_Analyzer()
|
|
||||||
{
|
|
||||||
delete [] msg_buf;
|
|
||||||
delete hdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
|
||||||
{
|
|
||||||
tcp::TCP_SupportAnalyzer::DeliverStream(len, data, orig);
|
|
||||||
|
|
||||||
tcp::TCP_Analyzer* tcp =
|
|
||||||
static_cast<tcp::TCP_ApplicationAnalyzer*>(Parent())->TCP();
|
|
||||||
|
|
||||||
if ( tcp->HadGap(orig) || tcp->IsPartial() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( speculation == 0 ) // undecided
|
|
||||||
{
|
|
||||||
if ( ! DCE_RPC_Session::LooksLikeRPC(len, data) )
|
|
||||||
speculation = -1;
|
|
||||||
else
|
|
||||||
speculation = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( speculation < 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
ASSERT(buf_len >= DCE_RPC_HEADER_LENGTH);
|
|
||||||
while ( len > 0 )
|
|
||||||
{
|
|
||||||
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
|
||||||
{
|
|
||||||
while ( buf_n < DCE_RPC_HEADER_LENGTH && len > 0 )
|
|
||||||
{
|
|
||||||
msg_buf[buf_n] = *data;
|
|
||||||
++buf_n; ++data; --len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( ! ParseHeader() )
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( buf_n < msg_len && len > 0 )
|
|
||||||
{
|
|
||||||
msg_buf[buf_n] = *data;
|
|
||||||
++buf_n; ++data; --len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( buf_n < msg_len )
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( msg_len > 0 )
|
|
||||||
DeliverPDU(msg_len, msg_buf);
|
|
||||||
// Reset for next message
|
|
||||||
InitState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Contents_DCE_RPC_Analyzer::DeliverPDU(int len, const u_char* data)
|
|
||||||
{
|
|
||||||
session->DeliverPDU(IsOrig(), len, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Contents_DCE_RPC_Analyzer::ParseHeader()
|
|
||||||
{
|
|
||||||
delete hdr;
|
|
||||||
hdr = 0;
|
|
||||||
|
|
||||||
if ( msg_buf[0] != 5 ) // DCE/RPC version
|
|
||||||
{
|
|
||||||
Conn()->Weird("DCE/RPC_version_error (non-DCE/RPC?)");
|
|
||||||
Conn()->SetSkip(1);
|
|
||||||
msg_len = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr = new DCE_RPC_Header(this, msg_buf);
|
|
||||||
|
|
||||||
msg_len = hdr->FragLen();
|
|
||||||
if ( msg_len > buf_len )
|
|
||||||
{
|
|
||||||
u_char* new_msg_buf = new u_char[msg_len];
|
|
||||||
memcpy(new_msg_buf, msg_buf, buf_n);
|
|
||||||
delete [] msg_buf;
|
|
||||||
buf_len = msg_len;
|
|
||||||
msg_buf = new_msg_buf;
|
|
||||||
hdr->SetBytes(new_msg_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection* conn, bool arg_speculative)
|
|
||||||
: tcp::TCP_ApplicationAnalyzer("DCE_RPC", conn)
|
|
||||||
{
|
|
||||||
session = new DCE_RPC_Session(this);
|
|
||||||
speculative = arg_speculative;
|
|
||||||
|
|
||||||
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, true, session,
|
|
||||||
speculative));
|
|
||||||
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, false, session,
|
|
||||||
speculative));
|
|
||||||
}
|
|
||||||
|
|
||||||
DCE_RPC_Analyzer::~DCE_RPC_Analyzer()
|
|
||||||
{
|
|
||||||
delete session;
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
|
||||||
|
|
||||||
#ifndef ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
|
|
||||||
#define ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
|
|
||||||
|
|
||||||
// NOTE: This is a somewhat crude analyzer for DCE/RPC (used on Microsoft
|
|
||||||
// Windows systems) and shouldn't be considered as stable.
|
|
||||||
|
|
||||||
#include "NetVar.h"
|
|
||||||
#include "analyzer/protocol/tcp/TCP.h"
|
|
||||||
#include "analyzer/protocol/dce-rpc/events.bif.h"
|
|
||||||
#include "IPAddr.h"
|
|
||||||
|
|
||||||
#include "dce_rpc_simple_pac.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace analyzer { namespace dce_rpc {
|
|
||||||
|
|
||||||
class UUID {
|
|
||||||
public:
|
|
||||||
UUID();
|
|
||||||
UUID(const u_char data[16]);
|
|
||||||
UUID(const binpac::bytestring &uuid);
|
|
||||||
UUID(const char* s);
|
|
||||||
|
|
||||||
const char* to_string() const { return s.c_str(); }
|
|
||||||
const string& str() const { return s; }
|
|
||||||
bool operator==(const UUID& u) const
|
|
||||||
{ return s == u.str(); }
|
|
||||||
bool operator<(const UUID& u) const
|
|
||||||
{ return s < u.str(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
u_char data[16];
|
|
||||||
string s;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* uuid_to_string(const u_char* uuid_data);
|
|
||||||
|
|
||||||
struct dce_rpc_endpoint_addr {
|
|
||||||
// All fields are in host byteorder.
|
|
||||||
IPAddr addr;
|
|
||||||
u_short port;
|
|
||||||
TransportProto proto;
|
|
||||||
|
|
||||||
dce_rpc_endpoint_addr()
|
|
||||||
{
|
|
||||||
addr = IPAddr();
|
|
||||||
port = 0;
|
|
||||||
proto = TRANSPORT_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_valid_addr() const
|
|
||||||
{ return addr != IPAddr() && port != 0 && proto != TRANSPORT_UNKNOWN; }
|
|
||||||
|
|
||||||
bool operator<(dce_rpc_endpoint_addr const &e) const
|
|
||||||
{
|
|
||||||
if ( addr != e.addr )
|
|
||||||
return addr < e.addr;
|
|
||||||
if ( proto != e.proto )
|
|
||||||
return proto < e.proto;
|
|
||||||
if ( port != e.port )
|
|
||||||
return port < e.port;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string to_string() const
|
|
||||||
{
|
|
||||||
static char buf[128];
|
|
||||||
snprintf(buf, sizeof(buf), "%s/%d/%s",
|
|
||||||
addr.AsString().c_str(), port,
|
|
||||||
proto == TRANSPORT_TCP ? "tcp" :
|
|
||||||
(proto == TRANSPORT_UDP ? "udp" : "?"));
|
|
||||||
|
|
||||||
return string(buf);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
enum DCE_RPC_PTYPE {
|
|
||||||
DCE_RPC_REQUEST, DCE_RPC_PING, DCE_RPC_RESPONSE, DCE_RPC_FAULT,
|
|
||||||
DCE_RPC_WORKING, DCE_RPC_NOCALL, DCE_RPC_REJECT, DCE_RPC_ACK,
|
|
||||||
DCE_RPC_CL_CANCEL, DCE_RPC_FACK, DCE_RPC_CANCEL_ACK, DCE_RPC_BIND,
|
|
||||||
DCE_RPC_BIND_ACK, DCE_RPC_BIND_NAK, DCE_RPC_ALTER_CONTEXT,
|
|
||||||
DCE_RPC_ALTER_CONTEXT_RESP, DCE_RPC_SHUTDOWN, DCE_RPC_CO_CANCEL,
|
|
||||||
DCE_RPC_ORPHANED,
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define DCE_RPC_HEADER_LENGTH 16
|
|
||||||
|
|
||||||
class DCE_RPC_Header {
|
|
||||||
public:
|
|
||||||
DCE_RPC_Header(analyzer::Analyzer* a, const u_char* bytes);
|
|
||||||
|
|
||||||
BifEnum::dce_rpc_ptype PTYPE() const { return ptype; }
|
|
||||||
int FragLen() const { return frag_len; }
|
|
||||||
int LittleEndian() const { return bytes[4] >> 4; }
|
|
||||||
bool Fragmented() const { return fragmented; }
|
|
||||||
|
|
||||||
void Weird(const char* msg) { analyzer->Weird(msg); }
|
|
||||||
void SetBytes(const u_char* b) { bytes = b; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
analyzer::Analyzer* analyzer;
|
|
||||||
const u_char* bytes;
|
|
||||||
BifEnum::dce_rpc_ptype ptype;
|
|
||||||
int frag_len;
|
|
||||||
bool fragmented;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a general DCE_RPC_Session class so that it can be used in
|
|
||||||
// case the RPC conversation is tunneled through other connections,
|
|
||||||
// e.g. through an SMB session.
|
|
||||||
|
|
||||||
class DCE_RPC_Session {
|
|
||||||
public:
|
|
||||||
DCE_RPC_Session(analyzer::Analyzer* a);
|
|
||||||
virtual ~DCE_RPC_Session() {}
|
|
||||||
virtual void DeliverPDU(int is_orig, int len, const u_char* data);
|
|
||||||
|
|
||||||
static bool LooksLikeRPC(int len, const u_char* msg);
|
|
||||||
static bool any_dce_rpc_event()
|
|
||||||
{ return dce_rpc_message || dce_rpc_bind || dce_rpc_request; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void DeliverBind(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
|
||||||
void DeliverRequest(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
|
||||||
void DeliverResponse(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
|
||||||
|
|
||||||
void DeliverEpmapperRequest(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Request* req);
|
|
||||||
void DeliverEpmapperResponse(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
|
|
||||||
void DeliverEpmapperMapResponse(
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
|
||||||
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
|
|
||||||
|
|
||||||
analyzer::Analyzer* analyzer;
|
|
||||||
UUID if_uuid;
|
|
||||||
BifEnum::dce_rpc_if_id if_id;
|
|
||||||
int opnum;
|
|
||||||
struct {
|
|
||||||
dce_rpc_endpoint_addr addr;
|
|
||||||
UUID uuid;
|
|
||||||
} mapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Contents_DCE_RPC_Analyzer : public tcp::TCP_SupportAnalyzer {
|
|
||||||
public:
|
|
||||||
Contents_DCE_RPC_Analyzer(Connection* conn, bool orig, DCE_RPC_Session* session,
|
|
||||||
bool speculative);
|
|
||||||
~Contents_DCE_RPC_Analyzer();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
|
||||||
virtual void DeliverPDU(int len, const u_char* data);
|
|
||||||
|
|
||||||
void InitState();
|
|
||||||
|
|
||||||
int speculation;
|
|
||||||
u_char* msg_buf;
|
|
||||||
int msg_len;
|
|
||||||
int buf_n; // number of bytes in msg_buf
|
|
||||||
int buf_len; // size off msg_buf
|
|
||||||
DCE_RPC_Header* hdr;
|
|
||||||
|
|
||||||
bool ParseHeader();
|
|
||||||
|
|
||||||
DCE_RPC_Session* session;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
|
||||||
public:
|
|
||||||
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
|
|
||||||
~DCE_RPC_Analyzer();
|
|
||||||
|
|
||||||
static analyzer::Analyzer* Instantiate(Connection* conn)
|
|
||||||
{ return new DCE_RPC_Analyzer(conn); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
DCE_RPC_Session* session;
|
|
||||||
bool speculative;
|
|
||||||
};
|
|
||||||
|
|
||||||
} } // namespace analyzer::*
|
|
||||||
|
|
||||||
#endif /* dce_rpc_h */
|
|
|
@ -1,141 +0,0 @@
|
||||||
# Definitions for DCE RPC.
|
|
||||||
|
|
||||||
enum dce_rpc_ptype {
|
|
||||||
DCE_RPC_REQUEST,
|
|
||||||
DCE_RPC_PING,
|
|
||||||
DCE_RPC_RESPONSE,
|
|
||||||
DCE_RPC_FAULT,
|
|
||||||
DCE_RPC_WORKING,
|
|
||||||
DCE_RPC_NOCALL,
|
|
||||||
DCE_RPC_REJECT,
|
|
||||||
DCE_RPC_ACK,
|
|
||||||
DCE_RPC_CL_CANCEL,
|
|
||||||
DCE_RPC_FACK,
|
|
||||||
DCE_RPC_CANCEL_ACK,
|
|
||||||
DCE_RPC_BIND,
|
|
||||||
DCE_RPC_BIND_ACK,
|
|
||||||
DCE_RPC_BIND_NAK,
|
|
||||||
DCE_RPC_ALTER_CONTEXT,
|
|
||||||
DCE_RPC_ALTER_CONTEXT_RESP,
|
|
||||||
DCE_RPC_SHUTDOWN,
|
|
||||||
DCE_RPC_CO_CANCEL,
|
|
||||||
DCE_RPC_ORPHANED,
|
|
||||||
};
|
|
||||||
|
|
||||||
type uuid = bytestring &length = 16;
|
|
||||||
|
|
||||||
type context_handle = record {
|
|
||||||
attrs : uint32;
|
|
||||||
uuid : bytestring &length = 16;
|
|
||||||
};
|
|
||||||
|
|
||||||
#type rpc_if_id_t = record {
|
|
||||||
# if_uuid : bytestring &length = 16;
|
|
||||||
# vers_major : uint16;
|
|
||||||
# vers_minor : uint16;
|
|
||||||
#};
|
|
||||||
|
|
||||||
type NDR_Format = record {
|
|
||||||
intchar : uint8;
|
|
||||||
floatspec : uint8;
|
|
||||||
reserved : padding[2];
|
|
||||||
} &let {
|
|
||||||
byteorder = (intchar >> 4) ? littleendian : bigendian;
|
|
||||||
};
|
|
||||||
|
|
||||||
#### There might be a endianness problem here: the frag_length
|
|
||||||
# causes problems despite the NDR_Format having a byteorder set.
|
|
||||||
|
|
||||||
type DCE_RPC_Header = record {
|
|
||||||
rpc_vers : uint8 &check(rpc_vers == 5);
|
|
||||||
rpc_vers_minor : uint8;
|
|
||||||
PTYPE : uint8;
|
|
||||||
pfc_flags : uint8;
|
|
||||||
packed_drep : NDR_Format;
|
|
||||||
frag_length : uint16;
|
|
||||||
auth_length : uint16;
|
|
||||||
call_id : uint32;
|
|
||||||
} &let {
|
|
||||||
frag = pfc_flags & 4;
|
|
||||||
lastfrag = (! frag) || (pfc_flags & 2);
|
|
||||||
} &byteorder = packed_drep.byteorder;
|
|
||||||
|
|
||||||
type Syntax = record {
|
|
||||||
uuid : bytestring &length = 16;
|
|
||||||
version : uint32;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ContextRequest = record {
|
|
||||||
id : uint16;
|
|
||||||
num_syntaxes : uint8;
|
|
||||||
reserved : padding[1];
|
|
||||||
abstract_syntax : Syntax;
|
|
||||||
transfer_syntaxes : Syntax[num_syntaxes];
|
|
||||||
};
|
|
||||||
|
|
||||||
type ContextReply = record {
|
|
||||||
ack_result : uint16;
|
|
||||||
ack_reason : uint16;
|
|
||||||
syntax : Syntax;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ContextList(is_request: bool) = record {
|
|
||||||
num_contexts : uint8;
|
|
||||||
reserved : padding[3];
|
|
||||||
req_reply : case is_request of {
|
|
||||||
true -> request_contexts : ContextRequest[num_contexts];
|
|
||||||
false -> reply_contexts : ContextReply[num_contexts];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Bind = record {
|
|
||||||
max_xmit_frag : uint16;
|
|
||||||
max_recv_frag : uint16;
|
|
||||||
assoc_group_id : uint32;
|
|
||||||
context_list : ContextList(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Bind_Ack = record {
|
|
||||||
max_xmit_frag : uint16;
|
|
||||||
max_recv_frag : uint16;
|
|
||||||
assoc_group_id : uint32;
|
|
||||||
sec_addr_length : uint16;
|
|
||||||
sec_addr : bytestring &length=sec_addr_length;
|
|
||||||
pad : padding align 4;
|
|
||||||
contexts : ContextList(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_AlterContext = record {
|
|
||||||
max_xmit_frag : uint16;
|
|
||||||
max_recv_frag : uint16;
|
|
||||||
assoc_group_id : uint32;
|
|
||||||
contexts : ContextList(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Request = record {
|
|
||||||
alloc_hint : uint32;
|
|
||||||
context_id : uint16;
|
|
||||||
opnum : uint16;
|
|
||||||
# object : uuid;
|
|
||||||
# stub_pad_0 : padding align 8;
|
|
||||||
stub : bytestring &restofdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Response = record {
|
|
||||||
alloc_hint : uint32;
|
|
||||||
context_id : uint16;
|
|
||||||
cancel_count : uint8;
|
|
||||||
reserved : uint8;
|
|
||||||
# stub_pad_0 : padding align 8;
|
|
||||||
stub : bytestring &restofdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Body(header: DCE_RPC_Header) = case header.PTYPE of {
|
|
||||||
DCE_RPC_BIND -> bind : DCE_RPC_Bind;
|
|
||||||
DCE_RPC_BIND_ACK -> bind_ack : DCE_RPC_Bind_Ack;
|
|
||||||
DCE_RPC_REQUEST -> request : DCE_RPC_Request;
|
|
||||||
DCE_RPC_RESPONSE -> response : DCE_RPC_Response;
|
|
||||||
default -> other : bytestring &restofdata;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DCE_RPC_Auth(header: DCE_RPC_Header) = uint8[header.auth_length];
|
|
|
@ -1,11 +1,3 @@
|
||||||
# this won't work correctly yet, since sometimes the parameters
|
|
||||||
# field in the transaction takes up all of the data field
|
|
||||||
|
|
||||||
%include dce_rpc-protocol.pac
|
|
||||||
|
|
||||||
%extern{
|
|
||||||
#include "DCE_RPC.h"
|
|
||||||
%}
|
|
||||||
|
|
||||||
refine connection SMB_Conn += {
|
refine connection SMB_Conn += {
|
||||||
%member{
|
%member{
|
||||||
|
@ -14,9 +6,10 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function get_tree_is_pipe(tree_id: uint16): bool
|
function get_tree_is_pipe(tree_id: uint16): bool
|
||||||
%{
|
%{
|
||||||
if ( tree_is_pipe_map.count(tree_id) == 0 )
|
if ( tree_is_pipe_map.count(tree_id) > 0 )
|
||||||
|
return tree_is_pipe_map.at(tree_id);
|
||||||
|
else
|
||||||
return false;
|
return false;
|
||||||
return tree_is_pipe_map[tree_id];
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function set_tree_is_pipe(tree_id: uint16, is_pipe: bool): bool
|
function set_tree_is_pipe(tree_id: uint16, is_pipe: bool): bool
|
||||||
|
@ -25,72 +18,36 @@ refine connection SMB_Conn += {
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_smb_pipe_message(val: SMB_Pipe_message, header: SMB_Header): bool
|
function forward_dce_rpc(pipe_data: bytestring, is_orig: bool): bool
|
||||||
%{
|
%{
|
||||||
switch ( ${val.rpc_header.PTYPE} ) {
|
if ( dcerpc )
|
||||||
case DCE_RPC_REQUEST:
|
dcerpc->DeliverStream(${pipe_data}.length(), ${pipe_data}.begin(), is_orig);
|
||||||
if ( smb_pipe_request )
|
|
||||||
BifEvent::generate_smb_pipe_request(bro_analyzer(),
|
|
||||||
bro_analyzer()->Conn(),
|
|
||||||
BuildHeaderVal(header),
|
|
||||||
${val.rpc_body.request.opnum});
|
|
||||||
break;
|
|
||||||
case DCE_RPC_RESPONSE:
|
|
||||||
if ( smb_pipe_response )
|
|
||||||
BifEvent::generate_smb_pipe_response(bro_analyzer(),
|
|
||||||
bro_analyzer()->Conn(),
|
|
||||||
BuildHeaderVal(header));
|
|
||||||
break;
|
|
||||||
case DCE_RPC_BIND_ACK:
|
|
||||||
if ( smb_pipe_bind_ack_response )
|
|
||||||
BifEvent::generate_smb_pipe_bind_ack_response(bro_analyzer(),
|
|
||||||
bro_analyzer()->Conn(),
|
|
||||||
BuildHeaderVal(header));
|
|
||||||
break;
|
|
||||||
case DCE_RPC_BIND:
|
|
||||||
if ( smb_pipe_bind_request )
|
|
||||||
{
|
|
||||||
// TODO - the version number needs to be calculated properly
|
|
||||||
if ( ${val.rpc_body.bind.context_list.num_contexts} > 0 )
|
|
||||||
{
|
|
||||||
const char * uuid = analyzer::dce_rpc::uuid_to_string(${val.rpc_body.bind.context_list.request_contexts[0].abstract_syntax.uuid}.begin());
|
|
||||||
uint32_t version = ${val.rpc_body.bind.context_list.request_contexts[0].abstract_syntax.version};
|
|
||||||
|
|
||||||
BifEvent::generate_smb_pipe_bind_request(bro_analyzer(),
|
|
||||||
bro_analyzer()->Conn(),
|
|
||||||
BuildHeaderVal(header),
|
|
||||||
new StringVal(uuid),
|
|
||||||
new StringVal(fmt("%d.0", version)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_Pipe_message(header: SMB_Header, byte_count: uint16) = record {
|
|
||||||
rpc_header : DCE_RPC_Header;
|
#type SMB_Pipe_message(header: SMB_Header, byte_count: uint16) = record {
|
||||||
rpc_body : DCE_RPC_Body(rpc_header);
|
# rpc_header : DCE_RPC_Header;
|
||||||
|
# rpc_body : DCE_RPC_Body(rpc_header);
|
||||||
# pipe_type: case $context.connection.determine_pipe_msg_type(rpc, opnum) of {
|
# pipe_type: case $context.connection.determine_pipe_msg_type(rpc, opnum) of {
|
||||||
# 1 -> atsvc_request : AT_SVC_Request(unicode, opnum);
|
# 1 -> atsvc_request : AT_SVC_Request(unicode, opnum);
|
||||||
# 2 -> atsvc_reply : AT_SVC_Reply(unicode, opnum);
|
# 2 -> atsvc_reply : AT_SVC_Reply(unicode, opnum);
|
||||||
# default -> unknown : bytestring &restofdata;
|
# default -> unknown : bytestring &restofdata;
|
||||||
# };
|
# };
|
||||||
} &let {
|
#} &let {
|
||||||
proc: bool = $context.connection.proc_smb_pipe_message(this, header);
|
# proc: bool = $context.connection.proc_smb_pipe_message(this, header);
|
||||||
} &byteorder = littleendian;
|
#} &byteorder = littleendian;
|
||||||
|
#
|
||||||
type SMB_RAP_message(unicode: bool, byte_count: uint16) = record {
|
#type SMB_RAP_message(unicode: bool, byte_count: uint16) = record {
|
||||||
rap_code : uint16;
|
# rap_code : uint16;
|
||||||
param_desc : SMB_string(unicode, offsetof(param_desc));
|
# param_desc : SMB_string(unicode, offsetof(param_desc));
|
||||||
data_desc : SMB_string(unicode, offsetof(data_desc));
|
# data_desc : SMB_string(unicode, offsetof(data_desc));
|
||||||
data : bytestring &restofdata;
|
# data : bytestring &restofdata;
|
||||||
} &byteorder = littleendian;
|
#} &byteorder = littleendian;
|
||||||
|
|
||||||
type AT_SVC_Request(unicode: bool, opnum: uint8) = record {
|
type AT_SVC_Request(unicode: bool, opnum: uint8) = record {
|
||||||
empty: padding[1];
|
empty: padding[1];
|
||||||
op: case opnum of {
|
op: case opnum of {
|
||||||
0 -> add : AT_SVC_NetrJobAdd(unicode);
|
0 -> add : AT_SVC_NetrJobAdd(unicode);
|
||||||
default -> unknown : bytestring &restofdata;
|
default -> unknown : bytestring &restofdata;
|
||||||
|
@ -124,6 +81,6 @@ type AT_SVC_Reply(unicode: bool, opnum: uint16) = record {
|
||||||
};
|
};
|
||||||
|
|
||||||
type AT_SVC_JobID(unicode: bool) = record {
|
type AT_SVC_JobID(unicode: bool) = record {
|
||||||
id: uint32;
|
id : uint32;
|
||||||
status: uint32;
|
status : uint32;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
%include bro.pac
|
%include bro.pac
|
||||||
|
|
||||||
%extern{
|
%extern{
|
||||||
|
#include "analyzer/Manager.h"
|
||||||
|
#include "analyzer/Analyzer.h"
|
||||||
|
// #include "analyzer/protocol/dce-rpc/DCE_RPC.h"
|
||||||
|
|
||||||
#include "smb1_events.bif.h"
|
#include "smb1_events.bif.h"
|
||||||
#include "smb2_events.bif.h"
|
#include "smb2_events.bif.h"
|
||||||
|
|
||||||
|
@ -134,3 +138,18 @@ type SMB_Protocol_Identifier(is_orig: bool, msg_len: uint32) = record {
|
||||||
flow SMB_Flow(is_orig: bool) {
|
flow SMB_Flow(is_orig: bool) {
|
||||||
flowunit = SMB_TCP(is_orig) withcontext(connection, this);
|
flowunit = SMB_TCP(is_orig) withcontext(connection, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
refine connection SMB_Conn += {
|
||||||
|
%member{
|
||||||
|
analyzer::Analyzer *dcerpc;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%init{
|
||||||
|
dcerpc = analyzer_mgr->InstantiateAnalyzer("DCE_RPC", bro_analyzer->Conn());
|
||||||
|
%}
|
||||||
|
|
||||||
|
%cleanup{
|
||||||
|
if ( dcerpc )
|
||||||
|
delete dcerpc;
|
||||||
|
%}
|
||||||
|
};
|
||||||
|
|
|
@ -80,12 +80,10 @@ type SMB1_read_andx_response(header: SMB_Header) = record {
|
||||||
|
|
||||||
byte_count : uint16;
|
byte_count : uint16;
|
||||||
pad : padding to data_offset - SMB_Header_length;
|
pad : padding to data_offset - SMB_Header_length;
|
||||||
pipe_or_not : case is_pipe of {
|
data : bytestring &length=data_len;
|
||||||
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
|
|
||||||
default -> data : bytestring &length=data_len;
|
|
||||||
} &requires(data_len);
|
|
||||||
} &let {
|
} &let {
|
||||||
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
|
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
|
||||||
|
pipe_proc : bool = $context.connection.forward_dce_rpc(data, false) &if(is_pipe);
|
||||||
|
|
||||||
padding_len : uint8 = (header.unicode == 1) ? 1 : 0;
|
padding_len : uint8 = (header.unicode == 1) ? 1 : 0;
|
||||||
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
||||||
|
|
|
@ -13,5 +13,5 @@ type SMB1_transaction_secondary_request(header: SMB_Header) = record {
|
||||||
pad1 : padding to param_offset - SMB_Header_length;
|
pad1 : padding to param_offset - SMB_Header_length;
|
||||||
parameters : bytestring &length = param_count;
|
parameters : bytestring &length = param_count;
|
||||||
pad2 : padding to data_offset - SMB_Header_length;
|
pad2 : padding to data_offset - SMB_Header_length;
|
||||||
data : SMB1_transaction_data(header, data_count, 0, SMB_UNKNOWN);
|
data : SMB1_transaction_data(header, true, data_count, 0, SMB_UNKNOWN);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,9 +4,31 @@ enum Trans_subcommands {
|
||||||
NT_TRANSACT_CREATE2 = 0x0009,
|
NT_TRANSACT_CREATE2 = 0x0009,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
refine connection SMB_Conn += {
|
refine connection SMB_Conn += {
|
||||||
|
|
||||||
|
%member{
|
||||||
|
map<uint16, bool> is_file_a_pipe;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function get_is_file_a_pipe(id: uint16): bool
|
||||||
|
%{
|
||||||
|
if ( is_file_a_pipe.count(id) > 0 )
|
||||||
|
{
|
||||||
|
bool is_pipe = is_file_a_pipe.at(id);
|
||||||
|
is_file_a_pipe.erase(id);
|
||||||
|
|
||||||
|
return is_pipe;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function set_is_file_a_pipe(id: uint16, is_it: bool): bool
|
||||||
|
%{
|
||||||
|
is_file_a_pipe[id] = is_it;
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
function proc_smb1_transaction_request(header: SMB_Header, val: SMB1_transaction_request): bool
|
function proc_smb1_transaction_request(header: SMB_Header, val: SMB1_transaction_request): bool
|
||||||
%{
|
%{
|
||||||
if ( smb1_transaction_request )
|
if ( smb1_transaction_request )
|
||||||
|
@ -15,6 +37,7 @@ refine connection SMB_Conn += {
|
||||||
BuildHeaderVal(header),
|
BuildHeaderVal(header),
|
||||||
smb_string2stringval(${val.name}),
|
smb_string2stringval(${val.name}),
|
||||||
${val.sub_cmd});
|
${val.sub_cmd});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -38,14 +61,16 @@ refine connection SMB_Conn += {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
type SMB1_transaction_data(header: SMB_Header, count: uint16, sub_cmd: uint16,
|
type SMB1_transaction_data(header: SMB_Header, is_orig: bool, count: uint16, sub_cmd: uint16,
|
||||||
trans_type: TransactionType) = case trans_type of {
|
trans_type: int) = case trans_type of {
|
||||||
# SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(header.unicode, count);
|
# SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(header.unicode, count);
|
||||||
# SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(header.unicode, count);
|
# SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(header.unicode, count);
|
||||||
# SMB_RAP -> rap : SMB_Pipe_message(header.unicode, count);
|
# SMB_RAP -> rap : SMB_Pipe_message(header.unicode, count);
|
||||||
SMB_PIPE -> pipe : SMB_Pipe_message(header, count);
|
SMB_PIPE -> pipe_data : bytestring &restofdata;
|
||||||
SMB_UNKNOWN -> unknown : bytestring &restofdata &transient;
|
SMB_UNKNOWN -> unknown : bytestring &restofdata &transient;
|
||||||
default -> data : bytestring &restofdata &transient;
|
default -> data : bytestring &restofdata &transient;
|
||||||
|
} &let {
|
||||||
|
pipe_proc : bool = $context.connection.forward_dce_rpc(pipe_data, is_orig) &if(trans_type == SMB_PIPE);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB1_transaction_setup(header: SMB_Header) = record {
|
type SMB1_transaction_setup(header: SMB_Header) = record {
|
||||||
|
@ -79,9 +104,13 @@ type SMB1_transaction_request(header: SMB_Header) = record {
|
||||||
pad1 : padding to param_offset - SMB_Header_length;
|
pad1 : padding to param_offset - SMB_Header_length;
|
||||||
parameters : bytestring &length = param_count;
|
parameters : bytestring &length = param_count;
|
||||||
pad2 : padding to data_offset - SMB_Header_length;
|
pad2 : padding to data_offset - SMB_Header_length;
|
||||||
data : SMB1_transaction_data(header, data_count, sub_cmd, determine_transaction_type(setup_count, name));
|
data : SMB1_transaction_data(header, true, data_count, sub_cmd, transtype);
|
||||||
} &let {
|
} &let {
|
||||||
sub_cmd : uint16 = setup_count ? setup.op_code : 0;
|
sub_cmd : uint16 = setup_count ? setup.op_code : 0;
|
||||||
|
transtype : int = determine_transaction_type(setup_count, name);
|
||||||
|
is_pipe : bool = (transtype == SMB_PIPE);
|
||||||
|
|
||||||
|
proc_set_pipe : bool = $context.connection.set_is_file_a_pipe(header.mid, is_pipe);
|
||||||
proc : bool = $context.connection.proc_smb1_transaction_request(header, this);
|
proc : bool = $context.connection.proc_smb1_transaction_request(header, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,8 +133,8 @@ type SMB1_transaction_response(header: SMB_Header) = record {
|
||||||
pad0 : padding to param_offset - SMB_Header_length;
|
pad0 : padding to param_offset - SMB_Header_length;
|
||||||
parameters : bytestring &length = param_count;
|
parameters : bytestring &length = param_count;
|
||||||
pad1 : padding to data_offset - SMB_Header_length;
|
pad1 : padding to data_offset - SMB_Header_length;
|
||||||
data : SMB1_transaction_data(header, data_count, 0, is_tree_a_pipe ? SMB_PIPE : SMB_UNKNOWN)[data_count>0 ? 1 : 0];
|
data : SMB1_transaction_data(header, false, data_count, 0, is_pipe ? SMB_PIPE : SMB_UNKNOWN)[data_count>0 ? 1 : 0];
|
||||||
} &let {
|
} &let {
|
||||||
proc : bool = $context.connection.proc_smb1_transaction_response(header, this);
|
proc : bool = $context.connection.proc_smb1_transaction_response(header, this);
|
||||||
is_tree_a_pipe: bool = $context.connection.get_tree_is_pipe(header.tid);
|
is_pipe: bool = $context.connection.get_is_file_a_pipe(header.mid);
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,12 +52,11 @@ type SMB1_write_andx_request(header: SMB_Header) = record {
|
||||||
|
|
||||||
byte_count : uint16;
|
byte_count : uint16;
|
||||||
pad : padding to data_offset - SMB_Header_length;
|
pad : padding to data_offset - SMB_Header_length;
|
||||||
pipe_or_not : case is_pipe of {
|
data : bytestring &length=data_len;
|
||||||
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
|
|
||||||
default -> data : bytestring &length=data_len;
|
|
||||||
} &requires(data_len);
|
|
||||||
} &let {
|
} &let {
|
||||||
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
|
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
|
||||||
|
pipe_proc : bool = $context.connection.forward_dce_rpc(data, true) &if(is_pipe);
|
||||||
|
|
||||||
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
||||||
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
|
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
|
||||||
offset : uint64 = (offset_high * 0x10000) + offset_low;
|
offset : uint64 = (offset_high * 0x10000) + offset_low;
|
||||||
|
|
|
@ -67,14 +67,10 @@ type SMB2_read_response(header: SMB2_Header) = record {
|
||||||
data_remaining : uint32;
|
data_remaining : uint32;
|
||||||
reserved : uint32;
|
reserved : uint32;
|
||||||
pad : padding to data_offset - header.head_length;
|
pad : padding to data_offset - header.head_length;
|
||||||
pipe_or_not : case is_pipe of {
|
data : bytestring &length=data_len;
|
||||||
# The SMB_Pipe_message type doesn't support smb2 pipes yet.
|
|
||||||
#true -> pipe_data : SMB_Pipe_message(header, data_len) &length=data_len;
|
|
||||||
true -> pipe_data : bytestring &length=data_len;
|
|
||||||
false -> data : bytestring &length=data_len;
|
|
||||||
};
|
|
||||||
} &let {
|
} &let {
|
||||||
is_pipe: bool = $context.connection.get_tree_is_pipe(header.tree_id);
|
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tree_id);
|
||||||
|
pipe_proc : bool = $context.connection.forward_dce_rpc(data, false) &if(is_pipe);
|
||||||
|
|
||||||
proc: bool = $context.connection.proc_smb2_read_response(header, this);
|
proc: bool = $context.connection.proc_smb2_read_response(header, this);
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,14 +43,10 @@ type SMB2_write_request(header: SMB2_Header) = record {
|
||||||
channel_info_len : uint16; # ignore
|
channel_info_len : uint16; # ignore
|
||||||
flags : uint32;
|
flags : uint32;
|
||||||
pad : padding to data_offset - header.head_length;
|
pad : padding to data_offset - header.head_length;
|
||||||
pipe_or_not : case is_pipe of {
|
data : bytestring &length=data_len;
|
||||||
# The SMB_Pipe_message type doesn't support smb2 pipes yet.
|
|
||||||
#true -> pipe_data : SMB_Pipe_message(header, data_len) &length=data_len;
|
|
||||||
true -> pipe_data : bytestring &length=data_len;
|
|
||||||
false -> data : bytestring &length=data_len;
|
|
||||||
};
|
|
||||||
} &let {
|
} &let {
|
||||||
is_pipe: bool = $context.connection.get_tree_is_pipe(header.tree_id);
|
is_pipe: bool = $context.connection.get_tree_is_pipe(header.tree_id);
|
||||||
|
pipe_proc : bool = $context.connection.forward_dce_rpc(data, true) &if(is_pipe);
|
||||||
|
|
||||||
proc : bool = $context.connection.proc_smb2_write_request(header, this);
|
proc : bool = $context.connection.proc_smb2_write_request(header, this);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,44 +1,5 @@
|
||||||
##! Declaration of various types that the Bro core uses internally.
|
##! Declaration of various types that the Bro core uses internally.
|
||||||
|
|
||||||
enum dce_rpc_ptype %{
|
|
||||||
DCE_RPC_REQUEST,
|
|
||||||
DCE_RPC_PING,
|
|
||||||
DCE_RPC_RESPONSE,
|
|
||||||
DCE_RPC_FAULT,
|
|
||||||
DCE_RPC_WORKING,
|
|
||||||
DCE_RPC_NOCALL,
|
|
||||||
DCE_RPC_REJECT,
|
|
||||||
DCE_RPC_ACK,
|
|
||||||
DCE_RPC_CL_CANCEL,
|
|
||||||
DCE_RPC_FACK,
|
|
||||||
DCE_RPC_CANCEL_ACK,
|
|
||||||
DCE_RPC_BIND,
|
|
||||||
DCE_RPC_BIND_ACK,
|
|
||||||
DCE_RPC_BIND_NAK,
|
|
||||||
DCE_RPC_ALTER_CONTEXT,
|
|
||||||
DCE_RPC_ALTER_CONTEXT_RESP,
|
|
||||||
DCE_RPC_SHUTDOWN,
|
|
||||||
DCE_RPC_CO_CANCEL,
|
|
||||||
DCE_RPC_ORPHANED,
|
|
||||||
%}
|
|
||||||
|
|
||||||
enum dce_rpc_if_id %{
|
|
||||||
DCE_RPC_unknown_if,
|
|
||||||
DCE_RPC_epmapper,
|
|
||||||
DCE_RPC_lsarpc,
|
|
||||||
DCE_RPC_lsa_ds,
|
|
||||||
DCE_RPC_mgmt,
|
|
||||||
DCE_RPC_netlogon,
|
|
||||||
DCE_RPC_samr,
|
|
||||||
DCE_RPC_srvsvc,
|
|
||||||
DCE_RPC_spoolss,
|
|
||||||
DCE_RPC_drs,
|
|
||||||
DCE_RPC_winspipe,
|
|
||||||
DCE_RPC_wkssvc,
|
|
||||||
DCE_RPC_oxid,
|
|
||||||
DCE_RPC_ISCMActivator,
|
|
||||||
%}
|
|
||||||
|
|
||||||
enum rpc_status %{
|
enum rpc_status %{
|
||||||
RPC_SUCCESS,
|
RPC_SUCCESS,
|
||||||
RPC_PROG_UNAVAIL,
|
RPC_PROG_UNAVAIL,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path dce_rpc
|
||||||
|
#open 2016-04-01-05-18-25
|
||||||
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p rtt named_pipe endpoint operation
|
||||||
|
#types time string addr port addr port interval string string string
|
||||||
|
1073392738.147860 CXWv6p3arKYeMETxOg 205.227.227.226 49467 205.227.227.243 445 0.004077 \\PIPE\\lsass dssetup DsRolerGetPrimaryDomainInformation
|
||||||
|
#close 2016-04-01-05-18-25
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
# @TEST-EXEC: bro -b -C -r $TRACES/smb/dssetup_DsRoleGetPrimaryDomainInformation_standalone_workstation.cap %INPUT
|
||||||
|
# @TEST-EXEC: btest-diff dce_rpc.log
|
||||||
|
|
||||||
|
@load base/protocols/dce-rpc
|
||||||
|
@load base/protocols/smb
|
Loading…
Add table
Add a link
Reference in a new issue