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:
Seth Hall 2016-04-01 09:38:52 -04:00
parent d249b76390
commit 5721db4be7
31 changed files with 1890 additions and 1819 deletions

View file

@ -43,6 +43,7 @@
@endif
@load base/protocols/conn
@load base/protocols/dce-rpc
@load base/protocols/dhcp
@load base/protocols/dnp3
@load base/protocols/dns

View file

@ -0,0 +1,2 @@
@load ./consts
@load ./main

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
signature dpd_dce_rpc {
ip-proto == tcp
payload /^\x05[\x00\x01][\x00-\x13]\x03/
enable "DCE_RPC"
}

View 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.
}

View file

@ -5,8 +5,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
bro_plugin_begin(Bro DCE_RPC)
bro_plugin_cc(DCE_RPC.cc Plugin.cc)
bro_plugin_bif(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_simple.pac dce_rpc-protocol.pac epmapper.pac)
bro_plugin_end()

View file

@ -9,580 +9,52 @@
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),
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)
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection *conn)
: 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));
interp = new binpac::DCE_RPC::DCE_RPC_Conn(this);
}
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()));
}
}

View file

@ -35,7 +35,7 @@ protected:
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 {
// All fields are in host byteorder.
@ -88,6 +88,7 @@ enum DCE_RPC_PTYPE {
};
*/
/*
#define DCE_RPC_HEADER_LENGTH 16
class DCE_RPC_Header {
@ -172,18 +173,23 @@ protected:
DCE_RPC_Session* session;
};
*/
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
DCE_RPC_Analyzer(Connection* conn);
~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)
{ return new DCE_RPC_Analyzer(conn); }
protected:
DCE_RPC_Session* session;
bool speculative;
binpac::DCE_RPC::DCE_RPC_Conn* interp;
};
} } // namespace analyzer::*

View file

@ -13,7 +13,7 @@ public:
plugin::Configuration Configure()
{
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;
config.name = "Bro::DCE_RPC";

View file

@ -1,135 +1,163 @@
# DCE/RPC protocol data unit.
type DCE_RPC_PDU = record {
# Set header's byteorder to little-endian (or big-endian) to
# avoid cyclic dependency.
header : DCE_RPC_Header;
# 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;
refine connection DCE_RPC_Conn += {
%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;
%}
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;
# %}
#};

View file

@ -29,6 +29,23 @@ type context_handle = record {
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 {
# if_uuid : bytestring &length = 16;
# vers_major : uint16;
@ -46,7 +63,7 @@ type NDR_Format = record {
#### 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 {
type DCE_RPC_Header(is_orig: bool) = record {
rpc_vers : uint8 &check(rpc_vers == 5);
rpc_vers_minor : uint8;
PTYPE : uint8;

View file

@ -2,16 +2,26 @@
%include bro.pac
%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;
# flow : DCE_RPC_Flow;
#};
connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
upflow = DCE_RPC_Flow(true);
downflow = DCE_RPC_Flow(false);
};
%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 dce_rpc-analyzer.pac

View file

@ -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;

View file

@ -2,54 +2,31 @@
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind 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 dce_rpc_message%(c: connection, is_orig: bool, ptype: dce_rpc_ptype, msg: string%);
event dce_rpc_message%(c: connection, is_orig: bool, ptype_id: count, ptype: DCE_RPC::PType%);
## TODO.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_message 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 dce_rpc_bind%(c: connection, uuid: string%);
event dce_rpc_bind%(c: connection, uuid: string, version: string%);
event dce_rpc_bind_ack%(c: connection, sec_addr: string%);
## TODO.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## 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%);
## TODO.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## 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%);
## TODO.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## 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%);

View 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,
%}

View file

@ -47,7 +47,6 @@ bro_plugin_pac(
smb-pipe.pac
smb-mailslot.pac
smb-ntlmssp.pac
dce_rpc-protocol.pac
smb1-protocol.pac
smb1-com-check-directory.pac

View file

@ -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;
}

View file

@ -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 */

View file

@ -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];

View file

@ -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 += {
%member{
@ -14,9 +6,10 @@ refine connection SMB_Conn += {
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 tree_is_pipe_map[tree_id];
%}
function set_tree_is_pipe(tree_id: uint16, is_pipe: bool): bool
@ -25,72 +18,36 @@ refine connection SMB_Conn += {
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} ) {
case DCE_RPC_REQUEST:
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;
}
if ( dcerpc )
dcerpc->DeliverStream(${pipe_data}.length(), ${pipe_data}.begin(), is_orig);
return true;
%}
};
type SMB_Pipe_message(header: SMB_Header, byte_count: uint16) = record {
rpc_header : DCE_RPC_Header;
rpc_body : DCE_RPC_Body(rpc_header);
#type SMB_Pipe_message(header: SMB_Header, byte_count: uint16) = record {
# rpc_header : DCE_RPC_Header;
# rpc_body : DCE_RPC_Body(rpc_header);
# pipe_type: case $context.connection.determine_pipe_msg_type(rpc, opnum) of {
# 1 -> atsvc_request : AT_SVC_Request(unicode, opnum);
# 2 -> atsvc_reply : AT_SVC_Reply(unicode, opnum);
# default -> unknown : bytestring &restofdata;
# };
} &let {
proc: bool = $context.connection.proc_smb_pipe_message(this, header);
} &byteorder = littleendian;
type SMB_RAP_message(unicode: bool, byte_count: uint16) = record {
rap_code : uint16;
param_desc : SMB_string(unicode, offsetof(param_desc));
data_desc : SMB_string(unicode, offsetof(data_desc));
data : bytestring &restofdata;
} &byteorder = littleendian;
#} &let {
# proc: bool = $context.connection.proc_smb_pipe_message(this, header);
#} &byteorder = littleendian;
#
#type SMB_RAP_message(unicode: bool, byte_count: uint16) = record {
# rap_code : uint16;
# param_desc : SMB_string(unicode, offsetof(param_desc));
# data_desc : SMB_string(unicode, offsetof(data_desc));
# data : bytestring &restofdata;
#} &byteorder = littleendian;
type AT_SVC_Request(unicode: bool, opnum: uint8) = record {
empty: padding[1];
empty: padding[1];
op: case opnum of {
0 -> add : AT_SVC_NetrJobAdd(unicode);
default -> unknown : bytestring &restofdata;
@ -124,6 +81,6 @@ type AT_SVC_Reply(unicode: bool, opnum: uint16) = record {
};
type AT_SVC_JobID(unicode: bool) = record {
id: uint32;
status: uint32;
id : uint32;
status : uint32;
};

View file

@ -2,6 +2,10 @@
%include bro.pac
%extern{
#include "analyzer/Manager.h"
#include "analyzer/Analyzer.h"
// #include "analyzer/protocol/dce-rpc/DCE_RPC.h"
#include "smb1_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) {
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;
%}
};

View file

@ -80,12 +80,10 @@ type SMB1_read_andx_response(header: SMB_Header) = record {
byte_count : uint16;
pad : padding to data_offset - SMB_Header_length;
pipe_or_not : case is_pipe of {
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
default -> data : bytestring &length=data_len;
} &requires(data_len);
data : bytestring &length=data_len;
} &let {
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;
data_len : uint32 = (data_len_high << 16) + data_len_low;

View file

@ -13,5 +13,5 @@ type SMB1_transaction_secondary_request(header: SMB_Header) = record {
pad1 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
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);
};

View file

@ -4,9 +4,31 @@ enum Trans_subcommands {
NT_TRANSACT_CREATE2 = 0x0009,
};
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
%{
if ( smb1_transaction_request )
@ -15,6 +37,7 @@ refine connection SMB_Conn += {
BuildHeaderVal(header),
smb_string2stringval(${val.name}),
${val.sub_cmd});
return true;
%}
@ -38,14 +61,16 @@ refine connection SMB_Conn += {
};
type SMB1_transaction_data(header: SMB_Header, count: uint16, sub_cmd: uint16,
trans_type: TransactionType) = case trans_type of {
# SMB_MAILSLOT_BROWSE -> mailslot : 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_PIPE -> pipe : SMB_Pipe_message(header, count);
SMB_UNKNOWN -> unknown : bytestring &restofdata &transient;
default -> data : bytestring &restofdata &transient;
type SMB1_transaction_data(header: SMB_Header, is_orig: bool, count: uint16, sub_cmd: uint16,
trans_type: int) = case trans_type of {
# SMB_MAILSLOT_BROWSE -> mailslot : 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_PIPE -> pipe_data : bytestring &restofdata;
SMB_UNKNOWN -> unknown : 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 {
@ -79,9 +104,13 @@ type SMB1_transaction_request(header: SMB_Header) = record {
pad1 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
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 {
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);
};
@ -104,8 +133,8 @@ type SMB1_transaction_response(header: SMB_Header) = record {
pad0 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
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 {
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);
};

View file

@ -52,12 +52,11 @@ type SMB1_write_andx_request(header: SMB_Header) = record {
byte_count : uint16;
pad : padding to data_offset - SMB_Header_length;
pipe_or_not : case is_pipe of {
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
default -> data : bytestring &length=data_len;
} &requires(data_len);
data : bytestring &length=data_len;
} &let {
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;
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
offset : uint64 = (offset_high * 0x10000) + offset_low;

View file

@ -67,14 +67,10 @@ type SMB2_read_response(header: SMB2_Header) = record {
data_remaining : uint32;
reserved : uint32;
pad : padding to data_offset - header.head_length;
pipe_or_not : case is_pipe of {
# 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;
};
data : bytestring &length=data_len;
} &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);
};

View file

@ -43,14 +43,10 @@ type SMB2_write_request(header: SMB2_Header) = record {
channel_info_len : uint16; # ignore
flags : uint32;
pad : padding to data_offset - header.head_length;
pipe_or_not : case is_pipe of {
# 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;
};
data : bytestring &length=data_len;
} &let {
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);
};

View file

@ -1,44 +1,5 @@
##! 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 %{
RPC_SUCCESS,
RPC_PROG_UNAVAIL,

View file

@ -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

View file

@ -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