From: Hui Cao (huica) Date: Wed, 6 Jul 2016 18:25:59 +0000 (-0400) Subject: Merge pull request #549 in SNORT/snort3 from smb_reassembly to master X-Git-Tag: 3.0.0-233~342 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4e4cb511778a44001db2d3be76ca06c5cdeab1ad;p=thirdparty%2Fsnort3.git Merge pull request #549 in SNORT/snort3 from smb_reassembly to master Squashed commit of the following: commit e7dae9120e13a4d520376c24aa2ec1a1950b34dd Author: mdagon Date: Fri Jul 1 03:04:07 2016 -0400 Smb ressaembly --- diff --git a/src/service_inspectors/dce_rpc/dce_co.cc b/src/service_inspectors/dce_rpc/dce_co.cc index b06f29836..8e1fc3612 100644 --- a/src/service_inspectors/dce_rpc/dce_co.cc +++ b/src/service_inspectors/dce_rpc/dce_co.cc @@ -26,6 +26,7 @@ #include "dce_smb_module.h" #include "dce_list.h" #include "dce_utils.h" +#include "dce_smb_utils.h" #include "log/messages.h" #include "main/snort_debug.h" #include "utils/util.h" @@ -217,7 +218,6 @@ static inline DCE2_CoSeg* DCE2_CoGetSegPtr(DCE2_SsnData* sd, DCE2_CoTracker* cot ********************************************************************/ static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData* sd, DCE2_CoTracker* cot, uint16_t ctx_id) { - /* This should be set if we've gotten a Bind */ if (cot->ctx_ids == nullptr) return DCE2_RET__ERROR; @@ -1234,6 +1234,8 @@ static Packet* dce_co_reassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot, dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd); int co_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__CO_CLI : DCE2_MOCK_HDR_LEN__CO_SRV; + int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : + DCE2_MOCK_HDR_LEN__SMB_SRV; if (sd->trans == DCE2_TRANS_TYPE__TCP) { @@ -1255,8 +1257,30 @@ static Packet* dce_co_reassemble(DCE2_SsnData* sd, DCE2_CoTracker* cot, { case DCE2_RPKT_TYPE__SMB_CO_FRAG: case DCE2_RPKT_TYPE__SMB_CO_SEG: - // FIXIT-M add this when SMB is ported - return nullptr; + DCE2_SmbSetRdata((DCE2_SmbSsnData*)sd, (uint8_t*)rpkt->data, + (uint16_t)(rpkt->dsize - smb_hdr_len)); + + if (rpkt_type == DCE2_RPKT_TYPE__SMB_CO_FRAG) + { + DCE2_CoSetRdata(sd, cot, (uint8_t*)rpkt->data + smb_hdr_len, + (uint16_t)(rpkt->dsize - (smb_hdr_len + co_hdr_len))); + + if (DCE2_SsnFromClient(sd->wire_pkt)) + dce_common_stats->co_cli_frag_reassembled++; + else + dce_common_stats->co_srv_frag_reassembled++; + } + else + { + if (DCE2_SsnFromClient(sd->wire_pkt)) + dce_common_stats->co_cli_seg_reassembled++; + else + dce_common_stats->co_srv_seg_reassembled++; + } + + *co_hdr = (DceRpcCoHdr*)(rpkt->data + smb_hdr_len); + cot->stub_data = rpkt->data + smb_hdr_len + co_hdr_len; + return rpkt; case DCE2_RPKT_TYPE__TCP_CO_FRAG: case DCE2_RPKT_TYPE__TCP_CO_SEG: @@ -1434,9 +1458,11 @@ static void DCE2_CoHandleFrag(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_Buffer* frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); uint16_t max_frag_data; - // FIXIT-M add SMB max_frag_data when SMB is ported - - max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__TCP_CO_FRAG); + /* Check for potential overflow */ + if (sd->trans == DCE2_TRANS_TYPE__SMB) + max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__SMB_CO_FRAG); + else + max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__TCP_CO_FRAG); ret_val = dce_co_handle_frag(sd, cot,co_hdr, frag_ptr, frag_len,frag_buf,max_frag_data); if (ret_val == DCE2_RET__SUCCESS) @@ -2099,7 +2125,8 @@ static Packet* DCE2_CoGetSegRpkt(DCE2_SsnData* sd, const uint8_t* data_ptr, uint32_t data_len) { Packet* rpkt = nullptr; - // FIXIT-M add smb_hdr_len when SMB is ported + int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : + DCE2_MOCK_HDR_LEN__SMB_SRV; if (sd->trans == DCE2_TRANS_TYPE__TCP) { @@ -2113,7 +2140,15 @@ static Packet* DCE2_CoGetSegRpkt(DCE2_SsnData* sd, switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: - // FIXIT-M add when SMB is ported + rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__SMB_CO_SEG, + data_ptr, data_len); + if (rpkt == nullptr) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to create reassembly packet.\n"); + return nullptr; + } + DCE2_SmbSetRdata((DCE2_SmbSsnData*)sd, (uint8_t*)rpkt->data, + (uint16_t)(rpkt->dsize - smb_hdr_len)); break; case DCE2_TRANS_TYPE__TCP: @@ -2149,7 +2184,8 @@ static void DCE2_CoSegDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_CoSeg* const uint8_t* frag_ptr = nullptr; uint16_t frag_len = 0; dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd); - // FIXIT-M add smb_hdr_len when SMB is ported + int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : + DCE2_MOCK_HDR_LEN__SMB_SRV; if (DCE2_SsnFromClient(sd->wire_pkt)) dce_common_stats->co_cli_seg_reassembled++; @@ -2173,7 +2209,8 @@ static void DCE2_CoSegDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_CoSeg* switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: - // FIXIT-M add when SMB is ported + frag_ptr = rpkt->data + smb_hdr_len; + frag_len = rpkt->dsize - smb_hdr_len; break; case DCE2_TRANS_TYPE__TCP: @@ -2188,14 +2225,24 @@ static void DCE2_CoSegDecode(DCE2_SsnData* sd, DCE2_CoTracker* cot, DCE2_CoSeg* return; } - // FIXIT-L PORT_IF_NEEDED - //Push packet on stack and call detect + if (DCE2_PushPkt(rpkt,sd) != DCE2_RET__SUCCESS) + { + DebugMessage(DEBUG_DCE_COMMON, "Failed to push packet onto packet stack.\n"); + return; + } /* All is good. Decode the pdu */ DCE2_CoDecode(sd, cot, frag_ptr, frag_len); DebugMessage(DEBUG_DCE_COMMON, "Reassembled CO segmented packet\n"); DCE2_PrintPktData(rpkt->data, rpkt->dsize); + + /* Call detect since this is a reassembled packet that the + * detection engine hasn't seen yet */ + if (!co_reassembled) + DCE2_Detect(sd); + + DCE2_PopPkt(sd); } static DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer* seg_buf, const uint8_t* data_ptr, @@ -2296,71 +2343,89 @@ void DCE2_CoProcess(DCE2_SsnData* sd, DCE2_CoTracker* cot, { DCE2_CoSeg* seg = DCE2_CoGetSegPtr(sd, cot); dce2CommonStats* dce_common_stats = dce_get_proto_stats_ptr(sd); + uint32_t num_frags = 0; dce_common_stats->co_pdus++; - co_reassembled = 0; - // FIXIT-L PORT_IF_NEEDED - //Loop to walk through multiple fragments in a packet - - /* Fast track full fragments */ - if (DCE2_BufferIsEmpty(seg->buf)) + while (data_len > 0) { - const uint8_t* frag_ptr = data_ptr; - uint16_t frag_len; + num_frags++; - // FIXIT-L PORT_IF_NEEDED - //Not enough data left for a headerr + DebugFormat(DEBUG_DCE_COMMON, "DCE/RPC message number: %u\n", num_frags); - if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)data_ptr) != DCE2_RET__SUCCESS) - return; + /* Fast track full fragments */ + if (DCE2_BufferIsEmpty(seg->buf)) + { + const uint8_t* frag_ptr = data_ptr; + uint16_t frag_len; - frag_len = DceRpcCoFragLen((DceRpcCoHdr*)data_ptr); + // FIXIT-L PORT_IF_NEEDED + //Not enough data left for a headerr - /* Not enough data left for the pdu. */ - if (data_len < frag_len) - { - DebugFormat(DEBUG_DCE_COMMON, - "Not enough data in packet for fragment length: %hu\n", frag_len); + if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr*)data_ptr) != DCE2_RET__SUCCESS) + return; - /* Set frag length so we don't have to check it again in seg code */ - seg->frag_len = frag_len; + frag_len = DceRpcCoFragLen((DceRpcCoHdr*)data_ptr); - DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, frag_len); - goto dce2_coprocess_exit; - } + /* Not enough data left for the pdu. */ + if (data_len < frag_len) + { + DebugFormat(DEBUG_DCE_COMMON, + "Not enough data in packet for fragment length: %hu\n", frag_len); - /* Got a full DCE/RPC pdu */ - DCE2_CoDecode(sd, cot, frag_ptr, frag_len); + /* Set frag length so we don't have to check it again in seg code */ + seg->frag_len = frag_len; - // FIXIT-L PORT_IF_NEEDED - //Detect first frag and reset frag count, DCE_MOVE - } - else /* We've already buffered data */ - { - DebugFormat(DEBUG_DCE_COMMON, "Segmentation buffer has %u bytes\n", - DCE2_BufferLength(seg->buf)); + DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, frag_len); + goto dce2_coprocess_exit; + } - // FIXIT-L PORT_IF_NEEDED - //Need more data to get header + DCE2_MOVE(data_ptr, data_len, frag_len); - /* Need more data for full pdu */ - if (DCE2_BufferLength(seg->buf) < seg->frag_len) - { - DCE2_Ret status = DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, - seg->frag_len); + /* Got a full DCE/RPC pdu */ + DCE2_CoDecode(sd, cot, frag_ptr, frag_len); - /* Still not enough */ - if (status != DCE2_RET__SUCCESS) - goto dce2_coprocess_exit; + /* If we're configured to do defragmentation only detect on first frag + * since we'll detect on reassembled */ + if (!DCE2_GcDceDefrag((dce2CommonProtoConf*)sd->config) || ((num_frags == 1) && + !co_reassembled)) + DCE2_Detect(sd); + + /* Reset if this is a last frag */ + if (DceRpcCoLastFrag((DceRpcCoHdr*)frag_ptr)) + num_frags = 0; } + else /* We've already buffered data */ + { + DebugFormat(DEBUG_DCE_COMMON, "Segmentation buffer has %u bytes\n", + DCE2_BufferLength(seg->buf)); - // FIXIT-L PORT_IF_NEEDED - //Reset frag count, DCE_MOVE, data_used + // FIXIT-L PORT_IF_NEEDED + //Need more data to get header - /* Got the full DCE/RPC pdu. Need to create new packet before decoding */ - DCE2_CoSegDecode(sd, cot, seg); + /* Need more data for full pdu */ + if (DCE2_BufferLength(seg->buf) < seg->frag_len) + { + DCE2_Ret status = DCE2_CoHandleSegmentation(sd, seg, data_ptr, data_len, + seg->frag_len); + + /* Still not enough */ + if (status != DCE2_RET__SUCCESS) + goto dce2_coprocess_exit; + } + + // FIXIT-L PORT_IF_NEEDED + // DCE_MOVE, data_used + + /* Do this before calling DCE2_CoSegDecode since it will empty + * seg buffer */ + if (DceRpcCoLastFrag((DceRpcCoHdr*)seg->buf->data)) + num_frags = 0; + + /* Got the full DCE/RPC pdu. Need to create new packet before decoding */ + DCE2_CoSegDecode(sd, cot, seg); + } } dce2_coprocess_exit: diff --git a/src/service_inspectors/dce_rpc/dce_common.cc b/src/service_inspectors/dce_rpc/dce_common.cc index 1c979aca5..994d0e568 100644 --- a/src/service_inspectors/dce_rpc/dce_common.cc +++ b/src/service_inspectors/dce_rpc/dce_common.cc @@ -22,6 +22,7 @@ #include "dce_tcp.h" #include "dce_smb.h" #include "dce_co.h" +#include "dce_smb_utils.h" #include "framework/base_api.h" #include "framework/module.h" #include "flow/flow.h" @@ -350,9 +351,20 @@ uint16_t DCE2_GetRpktMaxData(DCE2_SsnData* sd, DCE2_RpktType rtype) { case DCE2_RPKT_TYPE__SMB_SEG: case DCE2_RPKT_TYPE__SMB_TRANS: + break; + case DCE2_RPKT_TYPE__SMB_CO_SEG: + if (DCE2_SsnFromClient(p)) + overhead += DCE2_MOCK_HDR_LEN__SMB_CLI; + else + overhead += DCE2_MOCK_HDR_LEN__SMB_SRV; + break; + case DCE2_RPKT_TYPE__SMB_CO_FRAG: - // FIXIT-M add support for these when SMB is ported + if (DCE2_SsnFromClient(p)) + overhead += DCE2_MOCK_HDR_LEN__SMB_CLI + DCE2_MOCK_HDR_LEN__CO_CLI; + else + overhead += DCE2_MOCK_HDR_LEN__SMB_SRV + DCE2_MOCK_HDR_LEN__CO_SRV; break; case DCE2_RPKT_TYPE__TCP_CO_SEG: @@ -371,37 +383,101 @@ uint16_t DCE2_GetRpktMaxData(DCE2_SsnData* sd, DCE2_RpktType rtype) return (DCE2_REASSEMBLY_BUF_SIZE - overhead); } +static void dce2_fill_rpkt_info(Packet* rpkt, Packet* p) +{ + DceEndianness* endianness = (DceEndianness*)rpkt->endianness; + rpkt->reset(); + rpkt->endianness = (Endianness*)endianness; + ((DceEndianness*)rpkt->endianness)->reset(); + rpkt->pkth = p->pkth; + rpkt->ptrs = p->ptrs; + rpkt->flow = p->flow; + rpkt->proto_bits = p->proto_bits; + rpkt->packet_flags = p->packet_flags; + rpkt->packet_flags |= PKT_PSEUDO; + rpkt->user_policy_id = p->user_policy_id; +} + Packet* DCE2_GetRpkt(Packet* p,DCE2_RpktType rpkt_type, const uint8_t* data, uint32_t data_len) { Packet* rpkt = nullptr; uint16_t data_overhead = 0; - DceEndianness* endianness; switch (rpkt_type) { case DCE2_RPKT_TYPE__SMB_SEG: + rpkt = dce2_smb_rpkt[rpkt_type - DCE2_SMB_RPKT_TYPE_START]; + dce2_fill_rpkt_info(rpkt, p); + rpkt->pseudo_type = PSEUDO_PKT_SMB_SEG; + break; + case DCE2_RPKT_TYPE__SMB_TRANS: + rpkt = dce2_smb_rpkt[rpkt_type - DCE2_SMB_RPKT_TYPE_START]; + dce2_fill_rpkt_info(rpkt, p); + rpkt->pseudo_type = PSEUDO_PKT_SMB_TRANS; + if (DCE2_SsnFromClient(p)) + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_CLIENT); + } + else + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_SERVER); + } + break; + case DCE2_RPKT_TYPE__SMB_CO_SEG: + rpkt = dce2_smb_rpkt[rpkt_type - DCE2_SMB_RPKT_TYPE_START]; + dce2_fill_rpkt_info(rpkt, p); + rpkt->pseudo_type = PSEUDO_PKT_DCE_SEG; + if (DCE2_SsnFromClient(p)) + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_CLIENT); + } + else + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_SERVER); + } + break; + case DCE2_RPKT_TYPE__SMB_CO_FRAG: + rpkt = dce2_smb_rpkt[rpkt_type - DCE2_SMB_RPKT_TYPE_START]; + dce2_fill_rpkt_info(rpkt, p); + rpkt->pseudo_type = PSEUDO_PKT_DCE_FRAG; + if (DCE2_SsnFromClient(p)) + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI + DCE2_MOCK_HDR_LEN__CO_CLI; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_CLIENT); + DCE2_CoInitRdata((uint8_t*)rpkt->data + + DCE2_MOCK_HDR_LEN__SMB_CLI, PKT_FROM_CLIENT); + } + else + { + data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV + DCE2_MOCK_HDR_LEN__CO_SRV; + memset((void*)rpkt->data, 0, data_overhead); + DCE2_SmbInitRdata((uint8_t*)rpkt->data, PKT_FROM_SERVER); + DCE2_CoInitRdata((uint8_t*)rpkt->data + + DCE2_MOCK_HDR_LEN__SMB_SRV, PKT_FROM_SERVER); + } + break; + case DCE2_RPKT_TYPE__UDP_CL_FRAG: - // FIXIT-M add support when SMB, UDP are ported + // FIXIT-M add support when UDP is ported return nullptr; case DCE2_RPKT_TYPE__TCP_CO_SEG: case DCE2_RPKT_TYPE__TCP_CO_FRAG: rpkt = dce2_tcp_rpkt[rpkt_type - DCE2_TCP_RPKT_TYPE_START]; - endianness = (DceEndianness*)rpkt->endianness; - rpkt->reset(); - rpkt->endianness = (Endianness*)endianness; - ((DceEndianness*)rpkt->endianness)->reset(); - rpkt->pkth = p->pkth; - rpkt->ptrs = p->ptrs; - rpkt->flow = p->flow; - rpkt->proto_bits = p->proto_bits; - rpkt->packet_flags = p->packet_flags; - rpkt->packet_flags |= PKT_PSEUDO; - rpkt->user_policy_id = p->user_policy_id; + dce2_fill_rpkt_info(rpkt, p); if (rpkt_type == DCE2_RPKT_TYPE__TCP_CO_FRAG) { rpkt->pseudo_type = PSEUDO_PKT_DCE_FRAG; diff --git a/src/service_inspectors/dce_rpc/dce_smb.cc b/src/service_inspectors/dce_rpc/dce_smb.cc index f394342be..669216d52 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.cc +++ b/src/service_inspectors/dce_rpc/dce_smb.cc @@ -2890,6 +2890,7 @@ static void dce2_smb_thread_init() { Packet* p = (Packet*)snort_calloc(sizeof(Packet)); p->data = (uint8_t*)snort_calloc(DCE2_REASSEMBLY_BUF_SIZE); + p->endianness = (Endianness*)new DceEndianness(); p->dsize = DCE2_REASSEMBLY_BUF_SIZE; dce2_smb_rpkt[i] = p; } @@ -2914,6 +2915,7 @@ static void dce2_smb_thread_term() { snort_free((void*)p->data); } + delete p->endianness; snort_free(p); dce2_smb_rpkt[i] = nullptr; } diff --git a/src/service_inspectors/dce_rpc/dce_smb.h b/src/service_inspectors/dce_rpc/dce_smb.h index 459f461b7..d950e5317 100644 --- a/src/service_inspectors/dce_rpc/dce_smb.h +++ b/src/service_inspectors/dce_rpc/dce_smb.h @@ -31,6 +31,7 @@ #define DCE2_SMB_NAME "dce_smb" #define DCE2_SMB_HELP "dce over smb inspection" #define DCE2_SMB_RPKT_TYPE_MAX 4 +#define DCE2_SMB_RPKT_TYPE_START 1 #define DCE2_SMB_BAD_NBSS_TYPE 2 #define DCE2_SMB_BAD_TYPE 3 diff --git a/src/service_inspectors/dce_rpc/dce_smb_commands.cc b/src/service_inspectors/dce_rpc/dce_smb_commands.cc index 661bb41fd..0cfb00b4f 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_commands.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_commands.cc @@ -29,7 +29,6 @@ #include "utils/util.h" #include "detection/detect.h" - #define SERVICE_0 (0) // IPC start #define SERVICE_1 (SERVICE_0+4) // DISK start #define SERVICE_FS (SERVICE_1+3) // Failure @@ -322,28 +321,25 @@ static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData* ssd, const SmbNtHdr if (ftracker->fp_writex_raw->remaining == 0) { - //FIXIT-M - port Create reassembled packet -/* - const uint8_t *data_ptr = DCE2_BufferData(ftracker->fp_writex_raw->buf); - uint32_t data_len = DCE2_BufferLength(ftracker->fp_writex_raw->buf); - SFSnortPacket *rpkt = DCE2_SmbGetRpkt(ssd, - &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); + const uint8_t* data_ptr = DCE2_BufferData(ftracker->fp_writex_raw->buf); + uint32_t data_len = DCE2_BufferLength(ftracker->fp_writex_raw->buf); + Packet* rpkt = DCE2_SmbGetRpkt(ssd, + &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); - if (rpkt == nullptr) - { - DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); - return DCE2_RET__ERROR; - } + if (rpkt == nullptr) + { + DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); + return DCE2_RET__ERROR; + } - DebugMessage(DEBUG_DCE_SMB, - "Reassembled WriteAndX raw mode request\n")); - DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); + DebugMessage(DEBUG_DCE_SMB, + "Reassembled WriteAndX raw mode request\n"); + DCE2_PrintPktData(rpkt->data, rpkt->dsize); - (void)DCE2_SmbProcessRequestData(ssd, fid, data_ptr, data_len, 0); + (void)DCE2_SmbProcessRequestData(ssd, fid, data_ptr, data_len, 0); - DCE2_SmbReturnRpkt(); - DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); -*/ + DCE2_SmbReturnRpkt(ssd); + DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); } } else diff --git a/src/service_inspectors/dce_rpc/dce_smb_transaction.cc b/src/service_inspectors/dce_rpc/dce_smb_transaction.cc index 4652dc187..2ff58fd1b 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_transaction.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_transaction.cc @@ -936,24 +936,34 @@ static DCE2_Ret DCE2_SmbUpdateTransResponse(DCE2_SmbSsnData* ssd, { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr); - if ((dcnt != 0) - && (DCE2_SmbBufferTransactionData(ttracker, nb_ptr, dcnt, ddisp) - != DCE2_RET__SUCCESS)) + if ((ttracker->dsent < ttracker->tdcnt) + || (ttracker->psent < ttracker->tpcnt) + || !DCE2_BufferIsEmpty(ttracker->dbuf)) { - return DCE2_RET__ERROR; - } + if ((dcnt != 0) + && (DCE2_SmbBufferTransactionData(ttracker, nb_ptr, dcnt, ddisp) + != DCE2_RET__SUCCESS)) + { + return DCE2_RET__ERROR; + } + } } if (data_params & DCE2_SMB_TRANS__PARAMS) { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + poff) - nb_ptr); - - if ((pcnt != 0) - && (DCE2_SmbBufferTransactionParameters(ttracker, nb_ptr, pcnt, pdisp) - != DCE2_RET__SUCCESS)) + + if ((ttracker->dsent < ttracker->tdcnt) + || (ttracker->psent < ttracker->tpcnt) + || !DCE2_BufferIsEmpty(ttracker->dbuf)) { - return DCE2_RET__ERROR; - } + if ((pcnt != 0) + && (DCE2_SmbBufferTransactionParameters(ttracker, nb_ptr, pcnt, pdisp) + != DCE2_RET__SUCCESS)) + { + return DCE2_RET__ERROR; + } + } } if ((ttracker->dsent == ttracker->tdcnt) @@ -1168,20 +1178,40 @@ DCE2_Ret DCE2_SmbTransaction(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_hdr, switch (ttracker->subcom) { case TRANS_TRANSACT_NMPIPE: - case TRANS_READ_NMPIPE: - { - // FIXIT-M port reassembly case + case TRANS_READ_NMPIPE: + if (!DCE2_BufferIsEmpty(ttracker->dbuf)) + { + const uint8_t* data_ptr = DCE2_BufferData(ttracker->dbuf); + uint32_t data_len = DCE2_BufferLength(ttracker->dbuf); + Packet* rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, + &data_len, DCE2_RPKT_TYPE__SMB_TRANS); - uint16_t dcnt = SmbTransactionRespDataCnt((SmbTransactionResp*)nb_ptr); - uint16_t doff = SmbTransactionRespDataOff((SmbTransactionResp*)nb_ptr); + if (rpkt == nullptr) + return DCE2_RET__ERROR; - DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr); + DebugMessage(DEBUG_DCE_SMB, "Reassembled Transaction response\n"); + DCE2_PrintPktData(rpkt->data, rpkt->dsize); - if (DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt) != DCE2_RET__SUCCESS) - return DCE2_RET__ERROR; + status = DCE2_SmbProcessResponseData(ssd, data_ptr, data_len); + + DCE2_SmbReturnRpkt(ssd); + + if (status != DCE2_RET__SUCCESS) + return status; + } + else + { + uint16_t dcnt = SmbTransactionRespDataCnt((SmbTransactionResp*)nb_ptr); + uint16_t doff = SmbTransactionRespDataOff((SmbTransactionResp*)nb_ptr); + + DCE2_MOVE(nb_ptr, nb_len, ((uint8_t*)smb_hdr + doff) - nb_ptr); + + if (DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt) != DCE2_RET__SUCCESS) + return DCE2_RET__ERROR; + } break; - } + case TRANS_SET_NMPIPE_STATE: DebugFormat(DEBUG_DCE_SMB, "Setting pipe " "to %s mode\n", ttracker->pipe_byte_mode ? "byte" : "message"); @@ -1617,23 +1647,20 @@ DCE2_Ret DCE2_SmbTransactionSecondary(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_ case TRANS_TRANSACT_NMPIPE: case TRANS_WRITE_NMPIPE: { -// FIXIT-M uncomment after porting packet reassembly code -/* - const uint8_t *data_ptr = DCE2_BufferData(ttracker->dbuf); - uint32_t data_len = DCE2_BufferLength(ttracker->dbuf); - rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); + const uint8_t* data_ptr = DCE2_BufferData(ttracker->dbuf); + uint32_t data_len = DCE2_BufferLength(ttracker->dbuf); + Packet* rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); - if (rpkt == nullptr) - return DCE2_RET__ERROR; + if (rpkt == nullptr) + return DCE2_RET__ERROR; - DebugMessage(DEBUG_DCE_SMB, "Reassembled Transaction request\n")); - DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); + DebugMessage(DEBUG_DCE_SMB, "Reassembled Transaction request\n"); + DCE2_PrintPktData(rpkt->data, rpkt->dsize); - status = DCE2_SmbTransactionReq(ssd, ttracker, data_ptr, data_len, - DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf)); + status = DCE2_SmbTransactionReq(ssd, ttracker, data_ptr, data_len, + DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf)); - DCE2_SmbReturnRpkt(); -*/ + DCE2_SmbReturnRpkt(ssd); } break; @@ -1722,3 +1749,4 @@ DCE2_Ret DCE2_SmbNtTransactSecondary(DCE2_SmbSsnData* ssd, const SmbNtHdr* smb_h return DCE2_RET__SUCCESS; } + diff --git a/src/service_inspectors/dce_rpc/dce_smb_utils.cc b/src/service_inspectors/dce_rpc/dce_smb_utils.cc index 72ff8ea98..94ff9aba1 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_utils.cc +++ b/src/service_inspectors/dce_rpc/dce_smb_utils.cc @@ -1071,15 +1071,11 @@ DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData* ssd, { DCE2_SmbFileTracker* ftracker = DCE2_SmbGetFileTracker(ssd, fid); - DebugFormat(DEBUG_DCE_SMB, - "Entering Processing request data with Fid: 0x%04X, ftracker ? %s ~~~~~~~~~~~~~~~~~\n", - fid,ftracker ? "TRUE" : "FALSE"); if (ftracker == nullptr) return DCE2_RET__ERROR; DebugFormat(DEBUG_DCE_SMB, - "Processing request data with Fid: 0x%04X, is_ipc ? %s ~~~~~~~~~~~~~~~~~\n", ftracker->fid, - ftracker->is_ipc ? "TRUE" : "FALSE"); + "Processing request data with Fid: 0x%04X ~~~~~~~~~~~~~~~~~\n", ftracker->fid); // Set this in case of chained commands or reassembled packet ssd->cur_rtracker->ftracker = ftracker; @@ -1138,3 +1134,187 @@ DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData* ssd, return DCE2_RET__SUCCESS; } +static inline uint16_t SmbHtons(const uint16_t* ptr) +{ + return alignedNtohs(ptr); +} + +/******************************************************************** + * Function: DCE2_SmbInitRdata() + * + * Purpose: + * Initializes the reassembled packet structure for an SMB + * reassembled packet. Uses WriteAndX and ReadAndX. + * TODO Use command that was used when reassembly occurred. + * One issue with this is that multiple different write/read + * commands can be used to write/read the full DCE/RPC + * request/response. + * + * Arguments: + * uint8_t * - pointer to the start of the NetBIOS header where + * data initialization should start. + * int dir - FLAG_FROM_CLIENT or FLAG_FROM_SERVER + * + * Returns: None + * + ********************************************************************/ +void DCE2_SmbInitRdata(uint8_t* nb_ptr, int dir) +{ + NbssHdr* nb_hdr = (NbssHdr*)nb_ptr; + SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(NbssHdr)); + + nb_hdr->type = NBSS_SESSION_TYPE__MESSAGE; + memcpy((void*)smb_hdr->smb_idf, (void*)"\xffSMB", sizeof(smb_hdr->smb_idf)); + + if (dir == PKT_FROM_CLIENT) + { + SmbWriteAndXReq* writex = + (SmbWriteAndXReq*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr)); + uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq); + + smb_hdr->smb_com = SMB_COM_WRITE_ANDX; + smb_hdr->smb_flg = 0x00; + + writex->smb_wct = 12; + writex->smb_com2 = SMB_COM_NO_ANDX_COMMAND; + writex->smb_doff = SmbHtons(&offset); + } + else + { + SmbReadAndXResp* readx = + (SmbReadAndXResp*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr)); + uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp); + + smb_hdr->smb_com = SMB_COM_READ_ANDX; + smb_hdr->smb_flg = 0x80; + + readx->smb_wct = 12; + readx->smb_com2 = SMB_COM_NO_ANDX_COMMAND; + readx->smb_doff = SmbHtons(&offset); + } +} + +/******************************************************************** + * Function: DCE2_SmbSetRdata() + * + * Purpose: + * When a reassembled packet is needed this function is called to + * fill in appropriate fields to make the reassembled packet look + * correct from an SMB standpoint. + * + * Arguments: + * DCE2_SmbSsnData * - the session data structure. + * uint8_t * - pointer to the start of the NetBIOS header where + * data initialization should start. + * uint16_t - the length of the connection-oriented DCE/RPC data. + * + * Returns: None + * + ********************************************************************/ +void DCE2_SmbSetRdata(DCE2_SmbSsnData* ssd, uint8_t* nb_ptr, uint16_t co_len) +{ + NbssHdr* nb_hdr = (NbssHdr*)nb_ptr; + SmbNtHdr* smb_hdr = (SmbNtHdr*)((uint8_t*)nb_hdr + sizeof(NbssHdr)); + uint16_t uid = (ssd->cur_rtracker == nullptr) ? 0 : ssd->cur_rtracker->uid; + uint16_t tid = (ssd->cur_rtracker == nullptr) ? 0 : ssd->cur_rtracker->tid; + DCE2_SmbFileTracker* ftracker = (ssd->cur_rtracker == nullptr) ? nullptr : + ssd->cur_rtracker->ftracker; + + smb_hdr->smb_uid = SmbHtons((const uint16_t*)&uid); + smb_hdr->smb_tid = SmbHtons((const uint16_t*)&tid); + + if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) + { + SmbWriteAndXReq* writex = + (SmbWriteAndXReq*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr)); + uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq) + co_len; + + /* The data will get truncated anyway since we can only fit + * 64K in the reassembly buffer */ + if (nb_len > UINT16_MAX) + nb_len = UINT16_MAX; + + nb_hdr->length = htons((uint16_t)nb_len); + + if ((ftracker != nullptr) && (ftracker->fid > 0)) + { + uint16_t fid = (uint16_t)ftracker->fid; + writex->smb_fid = SmbHtons(&fid); + } + else + { + writex->smb_fid = 0; + } + + writex->smb_countleft = SmbHtons(&co_len); + writex->smb_dsize = SmbHtons(&co_len); + writex->smb_bcc = SmbHtons(&co_len); + } + else + { + SmbReadAndXResp* readx = + (SmbReadAndXResp*)((uint8_t*)smb_hdr + sizeof(SmbNtHdr)); + uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp) + co_len; + + /* The data will get truncated anyway since we can only fit + * 64K in the reassembly buffer */ + if (nb_len > UINT16_MAX) + nb_len = UINT16_MAX; + + nb_hdr->length = htons((uint16_t)nb_len); + + readx->smb_remaining = SmbHtons(&co_len); + readx->smb_dsize = SmbHtons(&co_len); + readx->smb_bcc = SmbHtons(&co_len); + } +} + +Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData* ssd, + const uint8_t** data, uint32_t* data_len, DCE2_RpktType rtype) +{ + if ((ssd == nullptr) || (data == nullptr) || (*data == nullptr) + || (data_len == nullptr) || (*data_len == 0)) + return nullptr; + + Packet* rpkt = DCE2_GetRpkt(ssd->sd.wire_pkt, rtype, *data, *data_len); + + if (rpkt == nullptr) + { + DebugFormat(DEBUG_DCE_SMB, + "%s(%d) Failed to create reassembly packet.", + __FILE__, __LINE__); + + return nullptr; + } + + if (DCE2_PushPkt(rpkt, &ssd->sd) != DCE2_RET__SUCCESS) + { + DebugFormat(DEBUG_DCE_SMB, + "%s(%d) Failed to push packet onto packet stack.", + __FILE__, __LINE__); + return nullptr; + } + + *data = rpkt->data; + *data_len = rpkt->dsize; + + uint16_t header_len; + switch (rtype) + { + case DCE2_RPKT_TYPE__SMB_TRANS: + if (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST) + header_len = DCE2_MOCK_HDR_LEN__SMB_CLI; + else + header_len = DCE2_MOCK_HDR_LEN__SMB_SRV; + DCE2_SmbSetRdata(ssd, (uint8_t*)rpkt->data, + (uint16_t)(rpkt->dsize - header_len)); + DCE2_MOVE(*data, *data_len, header_len); + break; + case DCE2_RPKT_TYPE__SMB_SEG: + default: + break; + } + + return rpkt; +} + diff --git a/src/service_inspectors/dce_rpc/dce_smb_utils.h b/src/service_inspectors/dce_rpc/dce_smb_utils.h index 6af78a4fa..369663591 100644 --- a/src/service_inspectors/dce_rpc/dce_smb_utils.h +++ b/src/service_inspectors/dce_rpc/dce_smb_utils.h @@ -163,6 +163,10 @@ DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData*, const uint16_t, const uint8_t*, uint32_t, uint64_t); DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData*, const uint8_t*, uint32_t); +void DCE2_SmbInitRdata(uint8_t*, int); +void DCE2_SmbSetRdata(DCE2_SmbSsnData*, uint8_t*, uint16_t); +Packet* DCE2_SmbGetRpkt(DCE2_SmbSsnData*, const uint8_t **, + uint32_t*, DCE2_RpktType); /******************************************************************** * Inline functions @@ -322,5 +326,10 @@ inline bool DCE2_SmbIsTransactionComplete(DCE2_SmbTransactionTracker* ttracker) return false; } +inline void DCE2_SmbReturnRpkt(DCE2_SmbSsnData* ssd) +{ + DCE2_PopPkt(&ssd->sd); +} + #endif