Commit message: proto/detect: workaround dns misdetected as dcerpc

The DCERPC UDP detection would misfire on DNS with transaction
ID 0x0400. This would happen as the protocol detection engine
gives preference to pattern based detection over probing parsers for
performance reasons.

This hack/workaround fixes this specific case by still running the
probing parser if DCERPC has been detected on UDP. The probing
parser result will take precedence.

Bug #2736.
CVE: CVE-2019-1010251
CWE: CWE-20
Vulnerability description: Open Information Security Foundation Suricata prior to version 4.1.2 is affected by: Denial of Service - DNS detection bypass. The impact is: An attacker can evade a signature detection with a specialy formed network packet. The component is: app-layer-detect-proto.c, decode.c, decode-teredo.c and decode-ipv6.c (https://github.com/OISF/suricata/pull/3590/commits/11f3659f64a4e42e90cb3c09fcef66894205aefe, https://github.com/OISF/suricata/pull/3590/commits/8357ef3f8ffc7d99ef6571350724160de356158b). The attack vector is: An attacker can trigger the vulnerability by sending a specifically crafted network request. The fixed version is: 4.1.2.

Project: suricata
File: app-layer-detect-proto.c

static int AppLayerProtoDetectPMPrepareMpm(AppLayerProtoDetectPMCtx *ctx)
{
    SCEnter();

    int ret = 0;
    MpmCtx *mpm_ctx = &ctx->mpm_ctx;

    if (mpm_table[mpm_ctx->mpm_type].Prepare(mpm_ctx) < 0)
        goto error;

    goto end;
 error:
    ret = -1;
 end:
    SCReturnInt(ret);
}

Project: suricata
File: app-layer-detect-proto.c

static void AppLayerProtoDetectPrintProbingParsers(AppLayerProtoDetectProbingParser *pp)
{
    SCEnter();

    AppLayerProtoDetectProbingParserPort *pp_port = NULL;
    AppLayerProtoDetectProbingParserElement *pp_pe = NULL;

    printf("\nProtocol Detection Configuration\n");

    for ( ; pp != NULL; pp = pp->next) {
        /* print ip protocol */
        if (pp->ipproto == IPPROTO_TCP)
            printf("IPProto: TCP\n");
        else if (pp->ipproto == IPPROTO_UDP)
            printf("IPProto: UDP\n");
        else
            printf("IPProto: %"PRIu8"\n", pp->ipproto);

        pp_port = pp->port;
        for ( ; pp_port != NULL; pp_port = pp_port->next) {
            if (pp_port->dp != NULL) {
                printf("    Port: %"PRIu16 "\n", pp_port->port);

                printf("        Destination port: (max-depth: %"PRIu16 ", "
                        "mask - %"PRIu32")\n",
                        pp_port->dp_max_depth,
                        pp_port->alproto_mask);
                pp_pe = pp_port->dp;
                for ( ; pp_pe != NULL; pp_pe = pp_pe->next) {

                    if (pp_pe->alproto == ALPROTO_HTTP)
                        printf("            alproto: ALPROTO_HTTP\n");
                    else if (pp_pe->alproto == ALPROTO_FTP)
                        printf("            alproto: ALPROTO_FTP\n");
                    else if (pp_pe->alproto == ALPROTO_FTPDATA)
                        printf("            alproto: ALPROTO_FTPDATA\n");
                    else if (pp_pe->alproto == ALPROTO_SMTP)
                        printf("            alproto: ALPROTO_SMTP\n");
                    else if (pp_pe->alproto == ALPROTO_TLS)
                        printf("            alproto: ALPROTO_TLS\n");
                    else if (pp_pe->alproto == ALPROTO_SSH)
                        printf("            alproto: ALPROTO_SSH\n");
                    else if (pp_pe->alproto == ALPROTO_IMAP)
                        printf("            alproto: ALPROTO_IMAP\n");
                    else if (pp_pe->alproto == ALPROTO_MSN)
                        printf("            alproto: ALPROTO_MSN\n");
                    else if (pp_pe->alproto == ALPROTO_JABBER)
                        printf("            alproto: ALPROTO_JABBER\n");
                    else if (pp_pe->alproto == ALPROTO_SMB)
                        printf("            alproto: ALPROTO_SMB\n");
                    else if (pp_pe->alproto == ALPROTO_SMB2)
                        printf("            alproto: ALPROTO_SMB2\n");
                    else if (pp_pe->alproto == ALPROTO_DCERPC)
                        printf("            alproto: ALPROTO_DCERPC\n");
                    else if (pp_pe->alproto == ALPROTO_IRC)
                        printf("            alproto: ALPROTO_IRC\n");
                    else if (pp_pe->alproto == ALPROTO_DNS)
                        printf("            alproto: ALPROTO_DNS\n");
                    else if (pp_pe->alproto == ALPROTO_MODBUS)
                        printf("            alproto: ALPROTO_MODBUS\n");
                    else if (pp_pe->alproto == ALPROTO_ENIP)
                        printf("            alproto: ALPROTO_ENIP\n");
                    else if (pp_pe->alproto == ALPROTO_NFS)
                        printf("            alproto: ALPROTO_NFS\n");
                    else if (pp_pe->alproto == ALPROTO_NTP)
                        printf("            alproto: ALPROTO_NTP\n");
                    else if (pp_pe->alproto == ALPROTO_TFTP)
                        printf("            alproto: ALPROTO_TFTP\n");
                    else if (pp_pe->alproto == ALPROTO_IKEV2)
                        printf("            alproto: ALPROTO_IKEV2\n");
                    else if (pp_pe->alproto == ALPROTO_KRB5)
                        printf("            alproto: ALPROTO_KRB5\n");
                    else if (pp_pe->alproto == ALPROTO_DHCP)
                        printf("            alproto: ALPROTO_DHCP\n");
                    else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST)
                        printf("            alproto: ALPROTO_TEMPLATE_RUST\n");
                    else if (pp_pe->alproto == ALPROTO_TEMPLATE)
                        printf("            alproto: ALPROTO_TEMPLATE\n");
                    else if (pp_pe->alproto == ALPROTO_DNP3)
                        printf("            alproto: ALPROTO_DNP3\n");
                    else
                        printf("impossible\n");

                    printf("            port: %"PRIu16 "\n", pp_pe->port);
                    printf("            mask: %"PRIu32 "\n", pp_pe->alproto_mask);
                    printf("            min_depth: %"PRIu32 "\n", pp_pe->min_depth);
                    printf("            max_depth: %"PRIu32 "\n", pp_pe->max_depth);

                    printf("\n");
                }
            }

            if (pp_port->sp == NULL) {
                continue;
            }

            printf("        Source port: (max-depth: %"PRIu16 ", "
                   "mask - %"PRIu32")\n",
                   pp_port->sp_max_depth,
                   pp_port->alproto_mask);
            pp_pe = pp_port->sp;
            for ( ; pp_pe != NULL; pp_pe = pp_pe->next) {

                if (pp_pe->alproto == ALPROTO_HTTP)
                    printf("            alproto: ALPROTO_HTTP\n");
                else if (pp_pe->alproto == ALPROTO_FTP)
                    printf("            alproto: ALPROTO_FTP\n");
                else if (pp_pe->alproto == ALPROTO_FTPDATA)
                    printf("            alproto: ALPROTO_FTPDATA\n");
                else if (pp_pe->alproto == ALPROTO_SMTP)
                    printf("            alproto: ALPROTO_SMTP\n");
                else if (pp_pe->alproto == ALPROTO_TLS)
                    printf("            alproto: ALPROTO_TLS\n");
                else if (pp_pe->alproto == ALPROTO_SSH)
                    printf("            alproto: ALPROTO_SSH\n");
                else if (pp_pe->alproto == ALPROTO_IMAP)
                    printf("            alproto: ALPROTO_IMAP\n");
                else if (pp_pe->alproto == ALPROTO_MSN)
                    printf("            alproto: ALPROTO_MSN\n");
                else if (pp_pe->alproto == ALPROTO_JABBER)
                    printf("            alproto: ALPROTO_JABBER\n");
                else if (pp_pe->alproto == ALPROTO_SMB)
                    printf("            alproto: ALPROTO_SMB\n");
                else if (pp_pe->alproto == ALPROTO_SMB2)
                    printf("            alproto: ALPROTO_SMB2\n");
                else if (pp_pe->alproto == ALPROTO_DCERPC)
                    printf("            alproto: ALPROTO_DCERPC\n");
                else if (pp_pe->alproto == ALPROTO_IRC)
                    printf("            alproto: ALPROTO_IRC\n");
                else if (pp_pe->alproto == ALPROTO_DNS)
                    printf("            alproto: ALPROTO_DNS\n");
                else if (pp_pe->alproto == ALPROTO_MODBUS)
                    printf("            alproto: ALPROTO_MODBUS\n");
                else if (pp_pe->alproto == ALPROTO_ENIP)
                    printf("            alproto: ALPROTO_ENIP\n");
                else if (pp_pe->alproto == ALPROTO_NFS)
                    printf("            alproto: ALPROTO_NFS\n");
                else if (pp_pe->alproto == ALPROTO_NTP)
                    printf("            alproto: ALPROTO_NTP\n");
                else if (pp_pe->alproto == ALPROTO_TFTP)
                    printf("            alproto: ALPROTO_TFTP\n");
                else if (pp_pe->alproto == ALPROTO_IKEV2)
                    printf("            alproto: ALPROTO_IKEV2\n");
                else if (pp_pe->alproto == ALPROTO_KRB5)
                    printf("            alproto: ALPROTO_KRB5\n");
                else if (pp_pe->alproto == ALPROTO_DHCP)
                    printf("            alproto: ALPROTO_DHCP\n");
                else if (pp_pe->alproto == ALPROTO_TEMPLATE_RUST)
                    printf("            alproto: ALPROTO_TEMPLATE_RUST\n");
                else if (pp_pe->alproto == ALPROTO_TEMPLATE)
                    printf("            alproto: ALPROTO_TEMPLATE\n");
                else if (pp_pe->alproto == ALPROTO_DNP3)
                    printf("            alproto: ALPROTO_DNP3\n");
                else
                    printf("impossible\n");

                printf("            port: %"PRIu16 "\n", pp_pe->port);
                printf("            mask: %"PRIu32 "\n", pp_pe->alproto_mask);
                printf("            min_depth: %"PRIu32 "\n", pp_pe->min_depth);
                printf("            max_depth: %"PRIu32 "\n", pp_pe->max_depth);

                printf("\n");
            }
        }
    }

    SCReturn;
}

Project: suricata
File: app-layer-detect-proto.c

int AppLayerProtoDetectSetup(void)
{
    SCEnter();

    int i, j;

    memset(&alpd_ctx, 0, sizeof(alpd_ctx));

    uint16_t spm_matcher = SinglePatternMatchDefaultMatcher();
    uint16_t mpm_matcher = PatternMatchDefaultMatcher();

    alpd_ctx.spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
    if (alpd_ctx.spm_global_thread_ctx == NULL) {
        SCLogError(SC_ERR_FATAL, "Unable to alloc SpmGlobalThreadCtx.");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
        for (j = 0; j < 2; j++) {
            MpmInitCtx(&alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx, mpm_matcher);
        }
    }

    AppLayerExpectationSetup();

    SCReturnInt(0);
}

Project: suricata
File: app-layer-detect-proto.c

static int AppLayerProtoDetectTest01(void)
{
    AppLayerProtoDetectUnittestCtxBackup();
    AppLayerProtoDetectSetup();

    const char *buf;
    int r = 0;

    buf = "HTTP";
    AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
    buf = "GET";
    AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOSERVER);

    AppLayerProtoDetectPrepareState();
    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1) {
        printf("Failure - "
               "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 1\n");
        goto end;
    }
    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1) {
        printf("Failure - "
               "alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 1\n");
        goto end;
    }

    r = 1;

 end:
    AppLayerProtoDetectDeSetup();
    AppLayerProtoDetectUnittestCtxRestore();
    return r;
}

Project: suricata
File: app-layer-detect-proto.c

static int AppLayerProtoDetectTest05(void)
{
    AppLayerProtoDetectUnittestCtxBackup();
    AppLayerProtoDetectSetup();

    uint8_t l7data[] = "HTTP/1.1 200 OK\r\nServer: Apache/1.0\r\n\r\n<HTML><BODY>Blahblah</BODY></HTML>";
    const char *buf;
    int r = 0;
    Flow f;
    AppProto pm_results[ALPROTO_MAX];
    AppLayerProtoDetectThreadCtx *alpd_tctx;

    memset(&f, 0x00, sizeof(f));
    f.protomap = FlowGetProtoMapping(IPPROTO_TCP);

    memset(pm_results, 0, sizeof(pm_results));

    buf = "HTTP";
    AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_HTTP, buf, 4, 0, STREAM_TOCLIENT);
    buf = "220 ";
    AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_FTP, buf, 4, 0, STREAM_TOCLIENT);

    AppLayerProtoDetectPrepareState();
    /* AppLayerProtoDetectGetCtxThread() should be called post AppLayerProtoDetectPrepareState(), since
     * it sets internal structures which depends on the above function. */
    alpd_tctx = AppLayerProtoDetectGetCtxThread();

    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].max_pat_id != 0\n");
        goto end;
    }
    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].max_pat_id != 2\n");
        goto end;
    }

    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[0].map != NULL\n");
        goto end;
    }
    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map == NULL) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map != NULL\n");
        goto end;
    }

    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0]->alproto != ALPROTO_FTP) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[0].alproto != ALPROTO_FTP\n");
        goto end;
    }
    if (alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1]->alproto != ALPROTO_HTTP) {
        printf("alpd_ctx.ctx_ipp[FLOW_PROTO_TCP].ctx_pm[1].map[1].alproto != ALPROTO_HTTP\n");
        goto end;
    }

    uint32_t cnt = AppLayerProtoDetectPMGetProto(alpd_tctx,
                                                 &f,
                                                 l7data, sizeof(l7data),
                                                 STREAM_TOCLIENT,
                                                 IPPROTO_TCP,
                                                 pm_results);
    if (cnt != 1 && pm_results[0] != ALPROTO_HTTP) {
        printf("cnt != 1 && pm_results[0] != AlPROTO_HTTP\n");
        goto end;
    }

    r = 1;

 end:
    if (alpd_tctx != NULL)
        AppLayerProtoDetectDestroyCtxThread(alpd_tctx);
    AppLayerProtoDetectDeSetup();
    AppLayerProtoDetectUnittestCtxRestore();
    return r;
}