From 122c0eeec8a4e1ae3e6b066da72e9d0277849d42 Mon Sep 17 00:00:00 2001 From: Robert David Graham Date: Wed, 15 Nov 2023 22:32:22 -0500 Subject: [PATCH] added --rawudp, prints raw UDP regardless of other banners printed for packet --- src/main-conf.c | 20 +++++++- src/masscan-app.c | 2 + src/masscan-app.h | 1 + src/masscan.h | 1 + src/output.c | 8 ++-- src/output.h | 3 +- src/proto-dns.c | 1 + src/proto-isakmp.c | 108 +++++++++++++++++++++++++++++++++++-------- src/proto-isakmp.h | 8 +++- src/proto-udp.c | 40 ++++++++++------ src/templ-payloads.c | 2 +- 11 files changed, 151 insertions(+), 43 deletions(-) diff --git a/src/main-conf.c b/src/main-conf.c index 0414c2ec..d3b525fb 100644 --- a/src/main-conf.c +++ b/src/main-conf.c @@ -1112,6 +1112,20 @@ static int SET_banners(struct Masscan *masscan, const char *name, const char *va return CONF_OK; } +static int SET_banners_rawudp(struct Masscan *masscan, const char *name, const char *value) +{ + UNUSEDPARM(name); + if (masscan->echo) { + if (masscan->is_banners_rawudp || masscan->echo_all) + fprintf(masscan->echo, "rawudp = %s\n", masscan->is_banners_rawudp?"true":"false"); + return 0; + } + masscan->is_banners_rawudp = parseBoolean(value); + if (masscan->is_banners_rawudp) + masscan->is_banners = true; + return CONF_OK; +} + static int SET_capture(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { @@ -2343,7 +2357,8 @@ struct ConfigParameter config_parameters[] = { {"randomize-hosts", SET_randomize_hosts, F_BOOL, {0}}, {"rate", SET_rate, 0, {"max-rate",0}}, {"shard", SET_shard, 0, {"shards",0}}, - {"banners", SET_banners, F_BOOL, {"banner",0}}, + {"banners", SET_banners, F_BOOL, {"banner",0}}, /* --banners */ + {"rawudp", SET_banners_rawudp, F_BOOL, {"rawudp",0}}, /* --rawudp */ {"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}}, {"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}}, {"noreset", SET_noreset, F_BOOL, {0}}, @@ -3236,7 +3251,8 @@ masscan_command_line(struct Masscan *masscan, int argc, char *argv[]) masscan->op = Operation_ReadScan; /* Default to reading banners */ - masscan->is_banners = 1; + masscan->is_banners = true; + masscan->is_banners_rawudp = true; /* This option may be followed by many filenames, therefore, * skip forward in the argument list until the next diff --git a/src/masscan-app.c b/src/masscan-app.c index b289f471..37749b60 100644 --- a/src/masscan-app.c +++ b/src/masscan-app.c @@ -46,6 +46,7 @@ masscan_app_to_string(enum ApplicationProtocol proto) case PROTO_MC: return "minecraft"; case PROTO_VNC_RFB: return "vnc"; case PROTO_VNC_INFO: return "vnc-info"; + case PROTO_ISAKMP: return "isakmp"; case PROTO_ERROR: return "error"; @@ -98,6 +99,7 @@ masscan_string_to_app(const char *str) {"minecraft", PROTO_MC}, {"vnc", PROTO_VNC_RFB}, {"vnc-info", PROTO_VNC_INFO}, + {"isakmp", PROTO_ISAKMP}, {0,0} }; size_t i; diff --git a/src/masscan-app.h b/src/masscan-app.h index e3739e3c..56871419 100644 --- a/src/masscan-app.h +++ b/src/masscan-app.h @@ -41,6 +41,7 @@ enum ApplicationProtocol { PROTO_MC, /* 32 - Minecraft server */ PROTO_VNC_RFB, PROTO_VNC_INFO, + PROTO_ISAKMP, /* 35 - IPsec key exchange */ PROTO_ERROR, diff --git a/src/masscan.h b/src/masscan.h index fa988850..5e0854d4 100644 --- a/src/masscan.h +++ b/src/masscan.h @@ -187,6 +187,7 @@ struct Masscan unsigned is_pfring:1; /* --pfring */ unsigned is_sendq:1; /* --sendq */ unsigned is_banners:1; /* --banners */ + unsigned is_banners_rawudp:1; /* --rawudp */ unsigned is_offline:1; /* --offline */ unsigned is_noreset:1; /* --noreset, don't transmit RST */ unsigned is_gmt:1; /* --gmt, all times in GMT */ diff --git a/src/output.c b/src/output.c index 0f543189..ccaa5c76 100644 --- a/src/output.c +++ b/src/output.c @@ -392,7 +392,8 @@ output_create(const struct Masscan *masscan, unsigned thread_index) out->redis.port = masscan->redis.port; out->redis.ip = masscan->redis.ip; out->redis.password = masscan ->redis.password; - out->is_banner = masscan->is_banners; + out->is_banner = masscan->is_banners; /* --banners */ + out->is_banner_rawudp = masscan->is_banners_rawudp; /* --rawudp */ out->is_gmt = masscan->is_gmt; out->is_interactive = masscan->output.is_interactive; out->is_show_open = masscan->output.is_show_open; @@ -779,10 +780,7 @@ output_report_status(struct Output *out, time_t timestamp, int status, fprintf(stdout, "\n"); fflush(stdout); - } - - - if (fp == NULL) { + } else if (fp == NULL) { ERRMSG("no output file, use `--output-filename ` to set one\n"); ERRMSG("for `stdout`, use `--output-filename -`\n"); return; diff --git a/src/output.h b/src/output.h index 462c68e4..722195af 100644 --- a/src/output.h +++ b/src/output.h @@ -81,7 +81,8 @@ struct Output char *directory; } rotate; - unsigned is_banner:1; + unsigned is_banner:1; /* --banners */ + unsigned is_banner_rawudp:1; /* --rawudp */ unsigned is_gmt:1; /* --gmt */ unsigned is_interactive:1; /* echo to command line */ unsigned is_show_open:1; /* show open ports (default) */ diff --git a/src/proto-dns.c b/src/proto-dns.c index ef9e4c3c..287c7871 100644 --- a/src/proto-dns.c +++ b/src/proto-dns.c @@ -444,6 +444,7 @@ handle_dns(struct Output *out, time_t timestamp, PROTO_DNS_VERSIONBIND, parsed->ip_ttl, px + offset, txtlen); + return 1; } diff --git a/src/proto-isakmp.c b/src/proto-isakmp.c index 6080b6c5..0ed0f335 100644 --- a/src/proto-isakmp.c +++ b/src/proto-isakmp.c @@ -1,4 +1,23 @@ -/* ISAKMP protocol support */ +/* ISAKMP protocol support + + 1 2 3 +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +! Initiator ! +! Cookie ! ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +! Responder ! +! Cookie ! ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags ! ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +! Message ID ! ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +! Length ! ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + */ #include "proto-isakmp.h" #include "proto-banout.h" @@ -7,7 +26,44 @@ #include "massip-port.h" #include "output.h" +static const unsigned char +sample_response[] = + "\x00\x00\x00\x00\xc1\x18\x84\xda\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x10\x02\x00\x00\x00\x00\x00\x00\x00\x01`\x00\x00\x01D\x00" + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x018\x01\x01\x00\x0d\x03\x00" + "\x00\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00" + "\x01\x80\x04\x00\x02\x80\x0b\x00\x01\x80\x0c\x00\x01\x03\x00\x00" + "\x00\x01\x00\x00\x80\x01\x00\x01\x80\x02\x00\x01\x80\x03\x00\x01" + "\x80\x04\x00\x02\x80\x0b\x00\x01\x80\x0c\x00\x01\x03\x00\x00" + "\x00\x01\x00\x00\x80\x01\x00\x07\x80\x02\x00\x04\x80\x03\x00\x01" + "\x80\x04\x00\x0e\x80\x0b\x00\x01\x80\x0c\x00\x01\x03\x00\x00\x14" + "\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x02" + "\x03\x00\x00\x14\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02" + "\x80\x03\x00\x03\x03\x00\x00\x14\x00\x01\x00\x00\x80\x01\x00\x05" + "\x80\x02\x00\x02\x80\x03\x00\x04\x03\x00\x00\x14\x00\x01\x00\x00" + "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x08\x03\x00\x00\x14" + "\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfa\xdd" + "\x03\x00\x00\x14\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02" + "\x80\x03\xfa\xdf\x03\x00\x00\x14\x00\x01\x00\x00\x80\x01\x00\x05" + "\x80\x02\x00\x02\x80\x03\xfd\xe9\x03\x00\x00\x14\x00\x01\x00\x00" + "\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xeb\x03\x00\x00\x14" + "\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\xfd\xed" + "\x03\x00\x00\x14\x00\x01\x00\x00\x80\x01\x00\x05\x80\x02\x00\x02" + "\x80\x03\xfd\xef\x00\x00\x00\x08\x00\x01\x00\x00"; + static unsigned +isakmp_parse_banner(struct Output *out, time_t timestamp, + const unsigned char *px, unsigned length, + struct PreprocessedInfo *parsed, + uint64_t entropy + ) { + size_t offset = 0; + + /* TODO: parse ISAKMP values */ + return 0; +} + +unsigned isakmp_parse(struct Output *out, time_t timestamp, const unsigned char *px, unsigned length, struct PreprocessedInfo *parsed, @@ -18,33 +74,47 @@ isakmp_parse(struct Output *out, time_t timestamp, ipaddress ip_me; unsigned port_them = parsed->port_src; unsigned port_me = parsed->port_dst; - unsigned cookie; - unsigned resp_cookie; + uint64_t cookie; + uint64_t resp_cookie; unsigned char i; - - /* All memcached responses will be at least 8 bytes */ + + /* All responses will be at least 8 bytes */ if (length < 16) return 0; - + /* Grab IP addresses */ ip_them = parsed->src_ip; ip_me = parsed->dst_ip; - + + /* Calculate the expected SYN-cookie */ cookie = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy); + + /* Extract the SYN-cookie from the response*/ + resp_cookie = (uint64_t)px[0] << 56ull; + resp_cookie |= (uint64_t)px[1] << 48ull; + resp_cookie |= (uint64_t)px[2] << 40ull; + resp_cookie |= (uint64_t)px[3] << 32ull; + resp_cookie |= (uint64_t)px[4] << 24ull; + resp_cookie |= (uint64_t)px[5] << 16ull; + resp_cookie |= (uint64_t)px[6] << 8ull; + resp_cookie |= (uint64_t)px[7] << 0ull; - resp_cookie = 0; - for(i = 0; i < 8; i++) - resp_cookie |= px[8 + i] << (56 - 8 * i); - - if (resp_cookie != cookie) - output_report_banner(out, timestamp, ip_them, 17, port_them, + if (resp_cookie != cookie) { + /* If they aren't equal, then this is some other protocol. + * TODO: we should use a heuristic on these bytes to + * discover what the protocol probably is */ + /*output_report_banner(out, timestamp, ip_them, 17, port_them, PROTO_ERROR, parsed->ip_ttl, - (unsigned char *) "IP-MISSMATCH", 12); - - output_report_banner(out, timestamp, ip_them, 17, port_them, PROTO_NONE, - parsed->ip_ttl, px, length); + (unsigned char *) "IP-MISSMATCH", 12);*/ + return 0; + } else { + /* We've found our protocol, so report the banner + * TODO: we should parse this better. */ + output_report_banner(out, timestamp, ip_them, 17, port_them, PROTO_ISAKMP, + parsed->ip_ttl, px, length); + return 1; + } - return 0; } unsigned @@ -65,3 +135,5 @@ isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno) return 0; } + + diff --git a/src/proto-isakmp.h b/src/proto-isakmp.h index 53012f13..f8bdfff1 100644 --- a/src/proto-isakmp.h +++ b/src/proto-isakmp.h @@ -1,8 +1,14 @@ #ifndef PROTO_ISAKMP_H #define PROTO_ISAKMP_H - +#include #include #include +struct Output; +struct PreprocessedInfo; + +unsigned isakmp_parse(struct Output *out, time_t timestamp, + const unsigned char *px, unsigned length, + struct PreprocessedInfo *parsed, uint64_t entropy); unsigned isakmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno); diff --git a/src/proto-udp.c b/src/proto-udp.c index 5ab29a38..276b335e 100644 --- a/src/proto-udp.c +++ b/src/proto-udp.c @@ -1,6 +1,7 @@ #include "proto-udp.h" #include "proto-coap.h" #include "proto-dns.h" +#include "proto-isakmp.h" #include "proto-netbios.h" #include "proto-snmp.h" #include "proto-memcached.h" @@ -59,6 +60,18 @@ handle_udp(struct Output *out, time_t timestamp, unsigned port_them = parsed->port_src; unsigned status = 0; + /* Report "open" status regardless */ + output_report_status( + out, + timestamp, + PortStatus_Open, + ip_them, + 17, /* ip proto = udp */ + port_them, + 0, + parsed->ip_ttl, + parsed->mac_src); + switch (port_them) { case 53: /* DNS - Domain Name System (amplifier) */ @@ -73,8 +86,13 @@ handle_udp(struct Output *out, time_t timestamp, case 161: /* SNMP - Simple Network Managment Protocol (amplifier) */ status = handle_snmp(out, timestamp, px, length, parsed, entropy); break; + case 500: /* ISAKMP - IPsec key negotiation */ + status = isakmp_parse(out, timestamp, + px + parsed->app_offset, parsed->app_length, parsed, entropy); + break; case 5683: - status = coap_handle_response(out, timestamp, px + parsed->app_offset, parsed->app_length, parsed, entropy); + status = coap_handle_response(out, timestamp, + px + parsed->app_offset, parsed->app_length, parsed, entropy); break; case 11211: /* memcached (amplifier) */ px += parsed->app_offset; @@ -94,19 +112,12 @@ handle_udp(struct Output *out, time_t timestamp, break; } - if (status == 0) { - if (px != 0 && parsed->app_length == 0) { - output_report_status( - out, - timestamp, - PortStatus_Open, - ip_them, - 17, /* ip proto = udp */ - port_them, - 0, - parsed->ip_ttl, - parsed->mac_src); - } else { + + /* Report banner if some parser didn't already do so. + * Also report raw dump if `--rawudp` specified on the + * command-line, even if a protocol above already created a more detailed + * banner. */ + if (status == 0 || out->is_banner_rawudp) { output_report_banner( out, timestamp, @@ -117,6 +128,5 @@ handle_udp(struct Output *out, time_t timestamp, parsed->ip_ttl, px + parsed->app_offset, parsed->app_length); - } } } diff --git a/src/templ-payloads.c b/src/templ-payloads.c index a35d2d42..6d446e3d 100644 --- a/src/templ-payloads.c +++ b/src/templ-payloads.c @@ -314,7 +314,7 @@ struct PayloadUDP_Default hard_coded_udp_payloads[] = { /* ISAKMP */ {500, 500, 352, 0, isakmp_set_cookie, /* ISAKMP */ - "\x00\x11\x22\x33\x44\x55\x66\x77"/* init_cookie */ + "\x00\x11\x22\x33\x44\x55\x66\x77"/* init_cookie, overwritten on send() */ "\x00\x00\x00\x00\x00\x00\x00\x00" /* resp_cookie*/ "\x01" /* next_payload: SA */ "\x10" /* version */