diff --git a/src/main-conf.c b/src/main-conf.c index f16ecc616a2a4547b66e016a9616392f1e71a531..af8fff38006a1ca852b5091f538187b999011fdc 100644 --- a/src/main-conf.c +++ b/src/main-conf.c @@ -30,6 +30,10 @@ #define min(a,b) ((a)<(b)?(a):(b)) #endif +static void masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all); +static void masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value); + + /*************************************************************************** ***************************************************************************/ /*static struct Range top_ports_tcp[] = { @@ -240,15 +244,17 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) else sprintf_s(zzz, sizeof(zzz), "[%u]", i); - fprintf(fp, "adapter%s = %s\n", zzz, masscan->nic[i].ifname); - if (masscan->nic[i].src.ip.first == masscan->nic[i].src.ip.last) + if (masscan->nic[i].ifname && masscan->nic[i].ifname[0]) + fprintf(fp, "adapter%s = %s\n", zzz, masscan->nic[i].ifname); + + if (masscan->nic[i].src.ip.first+1 == masscan->nic[i].src.ip.last) fprintf(fp, "adapter-ip%s = %u.%u.%u.%u\n", zzz, (masscan->nic[i].src.ip.first>>24)&0xFF, (masscan->nic[i].src.ip.first>>16)&0xFF, (masscan->nic[i].src.ip.first>> 8)&0xFF, (masscan->nic[i].src.ip.first>> 0)&0xFF ); - else + else if (masscan->nic[i].src.ip.first+1 < masscan->nic[i].src.ip.last) fprintf(fp, "adapter-ip%s = %u.%u.%u.%u-%u.%u.%u.%u\n", zzz, (masscan->nic[i].src.ip.first>>24)&0xFF, (masscan->nic[i].src.ip.first>>16)&0xFF, @@ -260,13 +266,14 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) (masscan->nic[i].src.ip.last>> 0)&0xFF ); - fprintf(fp, "adapter-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz, - masscan->nic[i].my_mac[0], - masscan->nic[i].my_mac[1], - masscan->nic[i].my_mac[2], - masscan->nic[i].my_mac[3], - masscan->nic[i].my_mac[4], - masscan->nic[i].my_mac[5]); + if (masscan->nic[i].my_mac_count) + fprintf(fp, "adapter-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz, + masscan->nic[i].my_mac[0], + masscan->nic[i].my_mac[1], + masscan->nic[i].my_mac[2], + masscan->nic[i].my_mac[3], + masscan->nic[i].my_mac[4], + masscan->nic[i].my_mac[5]); if (masscan->nic[i].router_ip) { fprintf(fp, "router-ip%s = %u.%u.%u.%u\n", zzz, (masscan->nic[i].router_ip>>24)&0xFF, @@ -274,8 +281,8 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) (masscan->nic[i].router_ip>> 8)&0xFF, (masscan->nic[i].router_ip>> 0)&0xFF ); - } else - fprintf(fp, "router-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz, + } else if (memcmp(masscan->nic[i].router_mac, "\0\0\0\0\0\0", 6) != 0) + fprintf(fp, "router-mac%s = %02x:%02x:%02x:%02x:%02x:%02x\n", zzz, masscan->nic[i].router_mac[0], masscan->nic[i].router_mac[1], masscan->nic[i].router_mac[2], @@ -285,192 +292,6 @@ masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) } -/*************************************************************************** - * Prints the current configuration to the command-line then exits. - * Use#1: create a template file of all setable parameters. - * Use#2: make sure your configuration was interpreted correctly. - ***************************************************************************/ -static void -masscan_echo(struct Masscan *masscan, FILE *fp) -{ - unsigned i; - unsigned l = 0; - - fprintf(fp, "rate = %10.2f\n", masscan->max_rate); - fprintf(fp, "randomize-hosts = true\n"); - fprintf(fp, "seed = %" PRIu64 "\n", masscan->seed); - fprintf(fp, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of); - if (masscan->is_banners) - fprintf(fp, "banners = true\n"); - if (masscan->is_arp) - fprintf(fp, "arp = true\n"); - if (masscan->is_noreset) - fprintf(fp, "noreset = true\n"); - - fprintf(fp, "# ADAPTER SETTINGS\n"); - if (masscan->nic_count == 0) - masscan_echo_nic(masscan, fp, 0); - else { - for (i=0; i<masscan->nic_count; i++) - masscan_echo_nic(masscan, fp, i); - } - - - /* - * Output information - */ - fprintf(fp, "# OUTPUT/REPORTING SETTINGS\n"); - switch (masscan->output.format) { - case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break; - case Output_List: fprintf(fp, "output-format = list\n"); break; - case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break; - case Output_XML: fprintf(fp, "output-format = xml\n"); break; - case Output_Binary: fprintf(fp, "output-format = binary\n"); break; - case Output_Grepable: fprintf(fp, "output-format = grepable\n"); break; - case Output_JSON: fprintf(fp, "output-format = json\n"); break; - case Output_NDJSON: fprintf(fp, "output-format = ndjson\n"); break; - case Output_Certs: fprintf(fp, "output-format = certs\n"); break; - case Output_None: fprintf(fp, "output-format = none\n"); break; - case Output_Redis: - fprintf(fp, "output-format = redis\n"); - fprintf(fp, "redis = %u.%u.%u.%u:%u\n", - (unsigned char)(masscan->redis.ip>>24), - (unsigned char)(masscan->redis.ip>>16), - (unsigned char)(masscan->redis.ip>> 8), - (unsigned char)(masscan->redis.ip>> 0), - masscan->redis.port); - break; - - default: - fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format); - break; - } - fprintf(fp, "show = %s,%s,%s\n", - masscan->output.is_show_open?"open":"", - masscan->output.is_show_closed?"closed":"", - masscan->output.is_show_host?"host":"" - ); - if (!masscan->output.is_show_open) - fprintf(fp, "noshow = open\n"); - fprintf(fp, "output-filename = %s\n", masscan->output.filename); - if (masscan->output.is_append) - fprintf(fp, "output-append = true\n"); - fprintf(fp, "rotate = %u\n", masscan->output.rotate.timeout); - fprintf(fp, "rotate-dir = %s\n", masscan->output.rotate.directory); - fprintf(fp, "rotate-offset = %u\n", masscan->output.rotate.offset); - fprintf(fp, "rotate-filesize = %" PRIu64 "\n", masscan->output.rotate.filesize); - fprintf(fp, "pcap = %s\n", masscan->pcap_filename); - - /* - * Targets - */ - - fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n"); - fprintf(fp, "retries = %u\n", masscan->retries); - fprintf(fp, "ports = "); - /* Disable comma generation for the first element */ - l = 0; - for (i=0; i<masscan->ports.count; i++) { - struct Range range = masscan->ports.list[i]; - do { - struct Range rrange = range; - unsigned done = 0; - if (l) - fprintf(fp, ","); - l = 1; - if (rrange.begin >= Templ_ICMP_echo) { - rrange.begin -= Templ_ICMP_echo; - rrange.end -= Templ_ICMP_echo; - fprintf(fp,"I:"); - done = 1; - } else if (rrange.begin >= Templ_SCTP) { - rrange.begin -= Templ_SCTP; - rrange.end -= Templ_SCTP; - fprintf(fp,"S:"); - range.begin = Templ_ICMP_echo; - } else if (rrange.begin >= Templ_UDP) { - rrange.begin -= Templ_UDP; - rrange.end -= Templ_UDP; - fprintf(fp,"U:"); - range.begin = Templ_SCTP; - } else - range.begin = Templ_UDP; - rrange.end = min(rrange.end, 65535); - if (rrange.begin == rrange.end) - fprintf(fp, "%u", rrange.begin); - else - fprintf(fp, "%u-%u", rrange.begin, rrange.end); - if (done) - break; - } while (range.begin <= range.end); - } - fprintf(fp, "\n"); - for (i=0; i<masscan->targets.count; i++) { - struct Range range = masscan->targets.list[i]; - fprintf(fp, "range = "); - fprintf(fp, "%u.%u.%u.%u", - (range.begin>>24)&0xFF, - (range.begin>>16)&0xFF, - (range.begin>> 8)&0xFF, - (range.begin>> 0)&0xFF - ); - if (range.begin != range.end) { - unsigned cidr_bits = count_cidr_bits(range); - - if (cidr_bits) { - fprintf(fp, "/%u", cidr_bits); - } else - fprintf(fp, "-%u.%u.%u.%u", - (range.end>>24)&0xFF, - (range.end>>16)&0xFF, - (range.end>> 8)&0xFF, - (range.end>> 0)&0xFF - ); - } - fprintf(fp, "\n"); - } - - fprintf(fp, "\n"); - if (masscan->http_user_agent) - fprintf( fp, - "http-user-agent = %.*s\n", - masscan->http_user_agent_length, - masscan->http_user_agent); - - for (i=0; i<sizeof(masscan->http_headers)/sizeof(masscan->http_headers[0]); i++) { - if (masscan->http_headers[i].header_name == 0) - continue; - fprintf( fp, - "http-header[%s] = %.*s\n", - masscan->http_headers[i].header_name, - masscan->http_headers[i].header_value_length, - masscan->http_headers[i].header_value); - } - - - fprintf(fp, "%scapture = cert\n", masscan->is_capture_cert?"":"no"); - fprintf(fp, "%scapture = html\n", masscan->is_capture_html?"":"no"); - fprintf(fp, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no"); - fprintf(fp, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no"); - - if (masscan->is_hello_ssl) { - fprintf(fp, "hello = ssl\n"); - } - - /* - * TCP payloads - */ - fprintf(fp, "\n"); - fprintf(fp, "min-packet = %u\n", masscan->min_packet_size); - - { - struct TcpCfgPayloads *pay; - for (pay = masscan->tcp_payloads; pay; pay = pay->next) { - fprintf(fp, "hello-string[%u] = %s\n", - pay->port, pay->payload_base64); - } - } -} /*************************************************************************** ***************************************************************************/ @@ -493,10 +314,8 @@ masscan_save_state(struct Masscan *masscan) return; } - fprintf(fp, "\n# resume information\n"); - fprintf(fp, "resume-index = %" PRIu64 "\n", masscan->resume.index); - - masscan_echo(masscan, fp); + + masscan_echo(masscan, fp, 0); fclose(fp); } @@ -976,16 +795,683 @@ config_top_ports(struct Masscan *masscan, unsigned n) 65129,65389}; struct RangeList *ports = &masscan->ports; - if (masscan->scan_type.tcp) { - for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++) - rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]); + if (masscan->scan_type.tcp) { + for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++) + rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]); + } + if (masscan->scan_type.udp) { + for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++) + rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]); + } +} + +/*************************************************************************** + ***************************************************************************/ +static int +isInteger(const char *value) +{ + size_t i; + + if (value == NULL) + return 0; + + for (i=0; value[i]; i++) + if (!isdigit(value[i]&0xFF)) + return 0; + return 1; +} + +/*************************************************************************** + ***************************************************************************/ +typedef int (*SET_PARAMETER)(struct Masscan *masscan, const char *name, const char *value); +enum {CONF_OK, CONF_WARN, CONF_ERR}; + +static int SET_arpscan(struct Masscan *masscan, const char *name, const char *value) +{ + struct Range range; + if (masscan->echo) { + if (masscan->scan_type.arp || masscan->echo_all) + fprintf(masscan->echo, "arpscan = %s\n", masscan->scan_type.arp?"true":"false"); + return 0; + } + range.begin = Templ_ARP; + range.end = Templ_ARP; + rangelist_add_range(&masscan->ports, range.begin, range.end); + masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff"); + masscan->scan_type.arp = 1; + LOG(5, "--arpscan\n"); + return CONF_OK; +} + +static int SET_banners(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->is_banners || masscan->echo_all) + fprintf(masscan->echo, "banners = %s\n", masscan->is_banners?"true":"false"); + return 0; + } + masscan->is_banners = parseBoolean(value); + return CONF_OK; +} + +static int SET_capture(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (!masscan->is_capture_cert || masscan->echo_all) + fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no"); + if (masscan->is_capture_html || masscan->echo_all) + fprintf(masscan->echo, "%scapture = html\n", masscan->is_capture_html?"":"no"); + if (masscan->is_capture_heartbleed || masscan->echo_all) + fprintf(masscan->echo, "%scapture = heartbleed\n", masscan->is_capture_heartbleed?"":"no"); + if (masscan->is_capture_ticketbleed || masscan->echo_all) + fprintf(masscan->echo, "%scapture = ticketbleed\n", masscan->is_capture_ticketbleed?"":"no"); + return 0; + } + if (EQUALS("capture", name)) { + if (EQUALS("cert", value)) + masscan->is_capture_cert = 1; + else if (EQUALS("html", value)) + masscan->is_capture_html = 1; + else if (EQUALS("heartbleed", value)) + masscan->is_capture_heartbleed = 1; + else if (EQUALS("ticketbleed", value)) + masscan->is_capture_ticketbleed = 1; + else { + fprintf(stderr, "FAIL: %s: unknown capture type\n", value); + return CONF_ERR; + } + } else if (EQUALS("nocapture", name)) { + if (EQUALS("cert", value)) + masscan->is_capture_cert = 0; + else if (EQUALS("html", value)) + masscan->is_capture_html = 0; + else if (EQUALS("heartbleed", value)) + masscan->is_capture_heartbleed = 0; + else if (EQUALS("ticketbleed", value)) + masscan->is_capture_ticketbleed = 0; + else { + fprintf(stderr, "FAIL: %s: unknown nocapture type\n", value); + return CONF_ERR; + } + } + return CONF_OK; +} + +static int SET_hello(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->is_hello_ssl) { + fprintf(masscan->echo, "hello = ssl\n"); + } + return 0; + } + if (EQUALS("ssl", value)) + masscan->is_hello_ssl = 1; + else { + fprintf(stderr, "FAIL: %s: unknown hello type\n", value); + return CONF_ERR; + } + return CONF_OK; +} + +static int SET_hello_file(struct Masscan *masscan, const char *name, const char *value) +{ + unsigned index; + FILE *fp; + int x; + char buf[16384]; + char buf2[16384]; + size_t bytes_read; + size_t bytes_encoded; + char foo[64]; + + if (masscan->echo) { + //Echoed as a string "hello-string" that was originally read + //from a file, not the "hello-filename" + return 0; + } + + index = ARRAY(name); + if (index >= 65536) { + fprintf(stderr, "%s: bad index\n", name); + return CONF_ERR; + } + + /* When connecting via TCP, send this file */ + x = fopen_s(&fp, value, "rb"); + if (x != 0) { + LOG(0, "[FAILED] could not read hello file\n"); + perror(value); + return CONF_ERR; + } + + bytes_read = fread(buf, 1, sizeof(buf), fp); + if (bytes_read == 0) { + LOG(0, "[FAILED] could not read hello file\n"); + perror(value); + fclose(fp); + return CONF_ERR; + } + fclose(fp); + + bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read); + buf2[bytes_encoded] = '\0'; + + sprintf_s(foo, sizeof(foo), "hello-string[%u]", (unsigned)index); + + masscan_set_parameter(masscan, foo, buf2); + + return CONF_OK; +} + +static int SET_hello_string(struct Masscan *masscan, const char *name, const char *value) +{ + unsigned index; + char *value2; + struct TcpCfgPayloads *pay; + + if (masscan->echo) { + for (pay = masscan->tcp_payloads; pay; pay = pay->next) { + fprintf(masscan->echo, "hello-string[%u] = %s\n", + pay->port, pay->payload_base64); + } + return 0; + } + + index = ARRAY(name); + if (index >= 65536) { + fprintf(stderr, "%s: bad index\n", name); + exit(1); + } + + + value2 = (char*)malloc(strlen(value)+1); + memcpy(value2, value, strlen(value)+1); + + pay = (struct TcpCfgPayloads *)malloc(sizeof(*pay)); + + pay->payload_base64 = value2; + pay->port = index; + pay->next = masscan->tcp_payloads; + masscan->tcp_payloads = pay; + return CONF_OK; +} + +static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->tcp_hello_timeout || masscan->echo_all) + fprintf(masscan->echo, "hello-timeout = %u\n", masscan->tcp_hello_timeout); + return 0; + } + masscan->tcp_hello_timeout = (unsigned)parseInt(value); + return CONF_OK; +} + +static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->min_packet_size != 60 || masscan->echo_all) + fprintf(masscan->echo, "min-packet = %u\n", masscan->min_packet_size); + return 0; + } + masscan->min_packet_size = (unsigned)parseInt(value); + return CONF_OK; +} + + +static int SET_nobanners(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + return 0; + } + masscan->is_banners = !parseBoolean(value); + return CONF_OK; +} + +static int SET_noreset(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->is_noreset || masscan->echo_all) + fprintf(masscan->echo, "noreset = %s\n", masscan->is_noreset?"true":"false"); + return 0; + } + masscan->is_noreset = parseBoolean(value); + return CONF_OK; +} + +static int SET_output_append(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->output.is_append || masscan->echo_all) + fprintf(masscan->echo, "output-append = %s\n", + masscan->output.is_append?"true":"false"); + return 0; + } + if (EQUALS("overwrite", name) || !parseBoolean(value)) + masscan->output.is_append = 0; + else + masscan->output.is_append = 1; + return CONF_OK; +} + +static int SET_output_filename(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->output.filename[0] || masscan->echo_all) + fprintf(masscan->echo, "output-filename = %s\n", masscan->output.filename); + return 0; + } + if (masscan->output.format == 0) + masscan->output.format = Output_XML; /*TODO: Why is the default XML?*/ + strcpy_s(masscan->output.filename, + sizeof(masscan->output.filename), + value); + return CONF_OK; +} + +static int SET_output_format(struct Masscan *masscan, const char *name, const char *value) +{ + enum OutputFormat x = 0; + if (masscan->echo) { + FILE *fp = masscan->echo; + switch (masscan->output.format) { + case Output_Default: if (masscan->echo_all) fprintf(fp, "output-format = interactive\n"); break; + case Output_Interactive:fprintf(fp, "output-format = interactive\n"); break; + case Output_List: fprintf(fp, "output-format = list\n"); break; + case Output_Unicornscan:fprintf(fp, "output-format = unicornscan\n"); break; + case Output_XML: fprintf(fp, "output-format = xml\n"); break; + case Output_Binary: fprintf(fp, "output-format = binary\n"); break; + case Output_Grepable: fprintf(fp, "output-format = grepable\n"); break; + case Output_JSON: fprintf(fp, "output-format = json\n"); break; + case Output_NDJSON: fprintf(fp, "output-format = ndjson\n"); break; + case Output_Certs: fprintf(fp, "output-format = certs\n"); break; + case Output_None: fprintf(fp, "output-format = none\n"); break; + case Output_Redis: + fprintf(fp, "output-format = redis\n"); + fprintf(fp, "redis = %u.%u.%u.%u:%u\n", + (unsigned char)(masscan->redis.ip>>24), + (unsigned char)(masscan->redis.ip>>16), + (unsigned char)(masscan->redis.ip>> 8), + (unsigned char)(masscan->redis.ip>> 0), + masscan->redis.port); + break; + + default: + fprintf(fp, "output-format = unknown(%u)\n", masscan->output.format); + break; + } + return 0; + } + if (EQUALS("unknown(0)", value)) x = Output_Interactive; + else if (EQUALS("interactive", value)) x = Output_Interactive; + else if (EQUALS("list", value)) x = Output_List; + else if (EQUALS("unicornscan", value)) x = Output_Unicornscan; + else if (EQUALS("xml", value)) x = Output_XML; + else if (EQUALS("binary", value)) x = Output_Binary; + else if (EQUALS("greppable", value)) x = Output_Grepable; + else if (EQUALS("grepable", value)) x = Output_Grepable; + else if (EQUALS("json", value)) x = Output_JSON; + else if (EQUALS("ndjson", value)) x = Output_NDJSON; + else if (EQUALS("certs", value)) x = Output_Certs; + else if (EQUALS("none", value)) x = Output_None; + else if (EQUALS("redis", value)) x = Output_Redis; + else { + LOG(0, "FAIL: unknown output-format: %s\n", value); + LOG(0, " hint: 'binary', 'xml', 'grepable', ...\n"); + return CONF_ERR; + } + masscan->output.format = x; + + return CONF_OK; +} + +static int SET_output_noshow(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->echo_all) { + fprintf(masscan->echo, "output-noshow = %s%s%s\n", + (!masscan->output.is_show_open)?"open,":"", + (!masscan->output.is_show_closed)?"closed,":"", + (!masscan->output.is_show_host)?"host,":"" + ); + } + return 0; + } + for (;;) { + const char *val2 = value; + unsigned val2_len = INDEX_OF(val2, ','); + if (val2_len == 0) + break; + if (EQUALSx("open", val2, val2_len)) + masscan->output.is_show_open = 0; + else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) + masscan->output.is_show_closed = 0; + else if (EQUALSx("open", val2, val2_len)) + masscan->output.is_show_host = 0; + else if (EQUALSx("all",val2,val2_len)) { + masscan->output.is_show_open = 0; + masscan->output.is_show_host = 0; + masscan->output.is_show_closed = 0; + } + else { + LOG(0, "FAIL: unknown 'noshow' spec: %.*s\n", val2_len, val2); + exit(1); + } + value += val2_len; + while (*value == ',') + value++; + } + return CONF_OK; +} + +static int SET_output_show(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->echo_all) { + fprintf(masscan->echo, "output-show = %s%s%s\n", + masscan->output.is_show_open?"open,":"", + masscan->output.is_show_closed?"closed,":"", + masscan->output.is_show_host?"host,":"" + ); + } + return 0; + } + for (;;) { + const char *val2 = value; + unsigned val2_len = INDEX_OF(val2, ','); + if (val2_len == 0) + break; + if (EQUALSx("open", val2, val2_len)) + masscan->output.is_show_open = 1; + else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) + masscan->output.is_show_closed = 1; + else if (EQUALSx("open", val2, val2_len)) + masscan->output.is_show_host = 1; + else if (EQUALSx("all",val2,val2_len)) { + masscan->output.is_show_open = 1; + masscan->output.is_show_host = 1; + masscan->output.is_show_closed = 1; + } + else { + LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2); + exit(1); + } + value += val2_len; + while (*value == ',') + value++; + } + return CONF_OK; +} +static int SET_output_show_open(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + return 0; + } + /* "open" "open-only" */ + masscan->output.is_show_open = 1; + masscan->output.is_show_closed = 0; + masscan->output.is_show_host = 0; + return CONF_OK; +} +static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->pcap_filename && masscan->pcap_filename[0]) + fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename); + return 0; + } + if (value) + strcpy_s(masscan->pcap_filename, sizeof(masscan->pcap_filename), value); + return CONF_OK; +} + +static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + //fprintf(masscan->echo, "randomize-hosts = true\n"); + return 0; + } + return CONF_OK; +} + + +static int SET_rate(struct Masscan *masscan, const char *name, const char *value) +{ + double rate = 0.0; + double point = 10.0; + unsigned i; + + if (masscan->echo) { + fprintf(masscan->echo, "rate = %-10.2f\n", masscan->max_rate); + return 0; + } + + for (i=0; value[i] && value[i] != '.'; i++) { + char c = value[i]; + if (c < '0' || '9' < c) { + fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value); + return CONF_ERR; + } + rate = rate * 10.0 + (c - '0'); + } + + if (value[i] == '.') { + i++; + while (value[i]) { + char c = value[i]; + if (c < '0' || '9' < c) { + fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", + name, value); + return CONF_ERR; + } + rate += (c - '0')/point; + point /= 10.0; + value++; + } + } + + masscan->max_rate = rate; + return CONF_OK; +} + +static int SET_resume_count(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->resume.count || masscan->echo_all) { + fprintf(masscan->echo, "resume-count = %" PRIu64 "\n", masscan->resume.count); + } + return 0; + } + masscan->resume.count = parseInt(value); + return CONF_OK; +} + +static int SET_resume_index(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->resume.index || masscan->echo_all) { + fprintf(masscan->echo, "\n# resume information\n"); + fprintf(masscan->echo, "resume-index = %" PRIu64 "\n", masscan->resume.index); + } + return 0; + } + masscan->resume.index = parseInt(value); + return CONF_OK; +} + +static int SET_retries(struct Masscan *masscan, const char *name, const char *value) +{ + uint64_t x; + + if (masscan->echo) { + if (masscan->retries || masscan->echo_all) + fprintf(masscan->echo, "retries = %u\n", masscan->retries); + return 0; + } + x = strtoul(value, 0, 0); + if (x >= 1000) { + fprintf(stderr, "FAIL: retries=<n>: expected number less than 1000\n"); + return CONF_ERR; + } + masscan->retries = (unsigned)x; + return CONF_OK; + +} + +static int SET_rotate_time(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->output.rotate.timeout || masscan->echo_all) + fprintf(masscan->echo, "rotate = %u\n", masscan->output.rotate.timeout); + return 0; + } + masscan->output.rotate.timeout = (unsigned)parseTime(value); + return CONF_OK; +} +static int SET_rotate_directory(struct Masscan *masscan, const char *name, const char *value) +{ + char *p; + if (masscan->echo) { + if (memcmp(masscan->output.rotate.directory, ".",2) != 0 || masscan->echo_all) { + fprintf(masscan->echo, "rotate-dir = %s\n", masscan->output.rotate.directory); + } + return 0; + } + strcpy_s( masscan->output.rotate.directory, + sizeof(masscan->output.rotate.directory), + value); + /* strip trailing slashes */ + p = masscan->output.rotate.directory; + while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '/')) + p[strlen(p)-1] = '\0'; + return CONF_OK; +} +static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value) +{ + /* Time offset, otherwise output files are aligned to nearest time + * interval, e.g. at the start of the hour for "hourly" */ + if (masscan->echo) { + if (masscan->output.rotate.offset || masscan->echo_all) + fprintf(masscan->echo, "rotate-offset = %u\n", masscan->output.rotate.offset); + return 0; + } + masscan->output.rotate.offset = (unsigned)parseTime(value); + return CONF_OK; +} +static int SET_rotate_filesize(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + if (masscan->output.rotate.filesize || masscan->echo_all) + fprintf(masscan->echo, "rotate-size = %" PRIu64 "\n", masscan->output.rotate.filesize); + return 0; + } + masscan->output.rotate.filesize = parseSize(value); + return CONF_OK; + +} + + + +static int SET_seed(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + fprintf(masscan->echo, "seed = %" PRIu64 "\n", masscan->seed); + return 0; + } + if (EQUALS("time", value)) + masscan->seed = time(0); + else + masscan->seed = parseInt(value); + return CONF_OK; +} + +static int SET_space(struct Masscan *masscan, const char *name, const char *value) +{ + if (masscan->echo) { + fprintf(masscan->echo, "\n"); + return 0; } - if (masscan->scan_type.udp) { - for (i=0; i<n && i<sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); i++) - rangelist_add_range(ports, top_tcp_ports[i], top_tcp_ports[i]); + return CONF_OK; +} + +static int SET_shard(struct Masscan *masscan, const char *name, const char *value) +{ + unsigned one = 0; + unsigned of = 0; + + if (masscan->echo) { + if ((masscan->shard.one != 1 && masscan->shard.of != 1) || masscan->echo_all) + fprintf(masscan->echo, "shard = %u/%u\n", masscan->shard.one, masscan->shard.of); + return 0; + } + while (isdigit(*value)) + one = one*10 + (*(value++)) - '0'; + while (ispunct(*value)) + value++; + while (isdigit(*value)) + of = of*10 + (*(value++)) - '0'; + + if (one < 1) { + LOG(0, "FAIL: shard index can't be zero\n"); + LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); + return CONF_ERR; + } + if (one > of) { + LOG(0, "FAIL: shard spec is wrong\n"); + LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); + return CONF_ERR; } + masscan->shard.one = one; + masscan->shard.of = of; + return CONF_OK; } + + + +struct ConfigParameter { + const char *name; + SET_PARAMETER set; + unsigned flags; + const char *alts[6]; +}; +enum {F_NONE, F_BOOL}; +struct ConfigParameter config_parameters[] = { + {"resume-index", SET_resume_index}, + {"resume-count", SET_resume_count}, + {"seed", SET_seed}, + {"arpscan", SET_arpscan, F_BOOL, {"arp",0}}, + {"randomize-hosts", SET_randomize_hosts, F_BOOL}, + {"rate", SET_rate, 0, {"max-rate",0}}, + {"shard", SET_shard, 0, {"shards",0}}, + {"banners", SET_banners, F_BOOL, {"banner",0}}, + {"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}}, + {"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}}, + {"noreset", SET_noreset, F_BOOL}, + {"pcap-filename", SET_pcap_filename, 0, {"pcap",0}}, + {"hello", SET_hello}, + {"hello-file", SET_hello_file, 0, {"hello-filename",0}}, + {"hello-string", SET_hello_string}, + {"hello-timeout", SET_hello_timeout}, + {"min-packet", SET_min_packet, 0, {"min-pkt",0}}, + {"capture", SET_capture}, + {"SPACE", SET_space}, + {"output-filename", SET_output_filename, 0, {"output-file",0}}, + {"output-format", SET_output_format}, + {"output-show", SET_output_show, 0, {"output-status", "show",0}}, + {"output-noshow", SET_output_noshow, 0, {"noshow",0}}, + {"output-show-open",SET_output_show_open, F_BOOL, {"open", "open-only", 0}}, + {"output-append", SET_output_append, 0, {"append-output",0}}, + {"rotate", SET_rotate_time, 0, {"output-rotate", "rotate-output", "rotate-time", 0}}, + {"rotate-dir", SET_rotate_directory, 0, {"output-rotate-dir", "rotate-directory", 0}}, + {"rotate-offset", SET_rotate_offset, 0, {"output-rotate-offset", 0}}, + {"rotate-size", SET_rotate_filesize, 0, {"output-rotate-filesize", "rotate-filesize", 0}}, + + {"SPACE", SET_space}, + {0} +}; + /*************************************************************************** * Called either from the "command-line" parser when it sees a --parm, * or from the "config-file" parser for normal options. @@ -999,7 +1485,36 @@ masscan_set_parameter(struct Masscan *masscan, fprintf(stderr, "%s: bad index\n", name); exit(1); } + + /* + * NEW: + * Go through configured list of parameters + */ + { + size_t i; + + for (i=0; config_parameters[i].name; i++) { + if (EQUALS(config_parameters[i].name, name)) { + config_parameters[i].set(masscan, name, value); + return; + } else { + size_t j; + for (j=0; config_parameters[i].alts[j]; j++) { + if (EQUALS(config_parameters[i].alts[j], name)) { + config_parameters[i].set(masscan, name, value); + return; + } + } + } + } + } + /* + * OLD: + * Configure the old parameters, the ones we don't have in the new config + * system yet (see the NEW part above). + * TODO: transition all these old params to the new system + */ if (EQUALS("conf", name) || EQUALS("config", name)) { masscan_read_config_file(masscan, value); } else if (EQUALS("adapter", name) || EQUALS("if", name) || EQUALS("interface", name)) { @@ -1040,7 +1555,7 @@ masscan_set_parameter(struct Masscan *masscan, masscan->nic[index].src.ip.first = range.begin; masscan->nic[index].src.ip.last = range.end; - masscan->nic[index].src.ip.range = (uint64_t)range.end - range.begin + 1; + masscan->nic[index].src.ip.range = range.end - range.begin + 1; } else if (EQUALS("adapter-port", name) || EQUALS("source-port", name) || EQUALS("src-port", name)) { /* Send packets FROM this port number */ @@ -1124,38 +1639,6 @@ masscan_set_parameter(struct Masscan *masscan, masscan->nic[index].router_ip = range.begin; } - else if (EQUALS("rate", name) || EQUALS("max-rate", name) ) { - double rate = 0.0; - double point = 10.0; - unsigned i; - - for (i=0; value[i] && value[i] != '.'; i++) { - char c = value[i]; - if (c < '0' || '9' < c) { - fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", name, value); - return; - } - rate = rate * 10.0 + (c - '0'); - } - - if (value[i] == '.') { - i++; - while (value[i]) { - char c = value[i]; - if (c < '0' || '9' < c) { - fprintf(stderr, "CONF: non-digit in rate spec: %s=%s\n", - name, value); - return; - } - rate += (c - '0')/point; - point /= 10.0; - value++; - } - } - - masscan->max_rate = rate; - - } else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) { unsigned is_error = 0; masscan->scan_type.udp = 1; @@ -1196,73 +1679,26 @@ masscan_set_parameter(struct Masscan *masscan, fprintf(stderr, "err\n"); exit(1); } - } - else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) { + } else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) { unsigned is_error = 0; rangelist_parse_ports(&masscan->exclude_port, value, &is_error, 0); if (is_error) { LOG(0, "FAIL: bad exclude port: %s\n", value); exit(1); } - } else if (EQUALS("arp", name) || EQUALS("arpscan", name)) { - /* Add ICMP ping request */ - struct Range range; - range.begin = Templ_ARP; - range.end = Templ_ARP; - rangelist_add_range(&masscan->ports, range.begin, range.end); - masscan_set_parameter(masscan, "router-mac", "ff-ff-ff-ff-ff-ff"); - masscan->is_arp = 1; /* needs additional flag */ - LOG(5, "--arpscan\n"); - } else if (EQUALS("noreset", name)) { - if (value && value[0]) - masscan->is_noreset = parseBoolean(value); - else - masscan->is_noreset = 1; } else if (EQUALS("bpf", name)) { size_t len = strlen(value) + 1; if (masscan->bpf_filter) free(masscan->bpf_filter); masscan->bpf_filter = (char*)malloc(len); memcpy(masscan->bpf_filter, value, len); - } else if (EQUALS("hello", name)) { - if (EQUALS("ssl", value)) - masscan->is_hello_ssl = 1; - else { - fprintf(stderr, "FAIL: %s: unknown hello type\n", value); - exit(1); - } - } else if (EQUALS("capture", name)) { - if (EQUALS("cert", value)) - masscan->is_capture_cert = 1; - else if (EQUALS("html", value)) - masscan->is_capture_html = 1; - else if (EQUALS("heartbleed", value)) - masscan->is_capture_heartbleed = 1; - else if (EQUALS("ticketbleed", value)) - masscan->is_capture_ticketbleed = 1; - else { - fprintf(stderr, "FAIL: %s: unknown capture type\n", value); - exit(1); - } - } else if (EQUALS("nocapture", name)) { - if (EQUALS("cert", value)) - masscan->is_capture_cert = 0; - else if (EQUALS("html", value)) - masscan->is_capture_html = 0; - else if (EQUALS("heartbleed", value)) - masscan->is_capture_heartbleed = 0; - else if (EQUALS("ticketbleed", value)) - masscan->is_capture_ticketbleed = 0; - else { - fprintf(stderr, "FAIL: %s: unknown capture type\n", value); - exit(1); - } } else if (EQUALS("ping", name) || EQUALS("ping-sweep", name)) { /* Add ICMP ping request */ struct Range range; range.begin = Templ_ICMP_echo; range.end = Templ_ICMP_echo; rangelist_add_range(&masscan->ports, range.begin, range.end); + masscan->scan_type.ping = 1; LOG(5, "--ping\n"); } else if (EQUALS("range", name) || EQUALS("ranges", name) || EQUALS("ip", name) || EQUALS("ipv4", name) @@ -1321,31 +1757,20 @@ masscan_set_parameter(struct Masscan *masscan, } if (masscan->op == 0) masscan->op = Operation_Scan; - } else if (EQUALS("append-output", name) || EQUALS("output-append", name)) { - if (EQUALS("overwrite", name)) - masscan->output.is_append = 0; - else - masscan->output.is_append = 1; } else if (EQUALS("badsum", name)) { masscan->nmap.badsum = 1; } else if (EQUALS("banner1", name)) { banner1_test(value); exit(1); - } else if (EQUALS("banners", name) || EQUALS("banner", name)) { - masscan->is_banners = 1; - } else if (EQUALS("nobanners", name) || EQUALS("nobanner", name)) { - masscan->is_banners = 0; } else if (EQUALS("blackrock-rounds", name)) { masscan->blackrock_rounds = (unsigned)parseInt(value); } else if (EQUALS("connection-timeout", name) || EQUALS("tcp-timeout", name)) { - /* The timeout for "banners" TCP connections */ + /* The timeout for banners TCP connections */ masscan->tcp_connection_timeout = (unsigned)parseInt(value); - } else if (EQUALS("hello-timeout", name)) { - masscan->tcp_hello_timeout = (unsigned)parseInt(value); } else if (EQUALS("datadir", name)) { strcpy_s(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value); } else if (EQUALS("data-length", name)) { - unsigned x = strtoul(value, 0, 0); + unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 1514 - 14 - 40) { fprintf(stderr, "error: %s=<n>: expected number less than 1500\n", name); } else { @@ -1359,8 +1784,8 @@ masscan_set_parameter(struct Masscan *masscan, fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n", name); exit(1); - } else if (EQUALS("echo", name)) { - masscan_echo(masscan, stdout); + } else if (EQUALS("echo", name) || EQUALS("echo-all", name)) { + masscan_echo(masscan, stdout, EQUALS("echo-all", name)); exit(0); } else if (EQUALS("excludefile", name)) { unsigned count1 = masscan->exclude_ip.count; @@ -1381,51 +1806,6 @@ masscan_set_parameter(struct Masscan *masscan, masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "no-capture", "ticketbleed"); masscan_set_parameter(masscan, "banners", "true"); - } else if (EQUALS("hello-file", name)) { - /* When connecting via TCP, send this file */ - FILE *fp; - int x; - char buf[16384]; - char buf2[16384]; - size_t bytes_read; - size_t bytes_encoded; - char foo[64]; - - x = fopen_s(&fp, value, "rb"); - if (x != 0) { - LOG(0, "[FAILED] could not read hello file\n"); - perror(value); - exit(1); - } - - bytes_read = fread(buf, 1, sizeof(buf), fp); - if (bytes_read == 0) { - LOG(0, "[FAILED] could not read hello file\n"); - perror(value); - fclose(fp); - exit(1); - } - fclose(fp); - - bytes_encoded = base64_encode(buf2, sizeof(buf2)-1, buf, bytes_read); - buf2[bytes_encoded] = '\0'; - - sprintf_s(foo, sizeof(foo), "hello-string[%u]", (unsigned)index); - - masscan_set_parameter(masscan, foo, buf2); - } else if (EQUALS("hello-string", name)) { - char *value2; - struct TcpCfgPayloads *pay; - - value2 = (char*)malloc(strlen(value)+1); - memcpy(value2, value, strlen(value)+1); - - pay = (struct TcpCfgPayloads *)malloc(sizeof(*pay)); - - pay->payload_base64 = value2; - pay->port = index; - pay->next = masscan->tcp_payloads; - masscan->tcp_payloads = pay; } else if (EQUALS("host-timeout", name)) { fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name); exit(1); @@ -1493,12 +1873,6 @@ masscan_set_parameter(struct Masscan *masscan, } else if (EQUALS("log-errors", name)) { fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name); exit(1); - } else if (EQUALS("min-packet", name) || EQUALS("min-pkt", name)) { - masscan->min_packet_size = (unsigned)parseInt(value); - } else if (EQUALS("max-retries", name)) { - masscan_set_parameter(masscan, "retries", value); - } else if (EQUALS("max-rate", name)) { - masscan_set_parameter(masscan, "rate", value); } else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) { fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name); exit(1); @@ -1537,92 +1911,12 @@ masscan_set_parameter(struct Masscan *masscan, /* Run in "offline" mode where it thinks it's sending packets, but * it's not */ masscan->is_offline = 1; - } else if (EQUALS("open", name) || EQUALS("open-only", name)) { - masscan->output.is_show_open = 1; - masscan->output.is_show_closed = 0; - masscan->output.is_show_host = 0; - } else if (EQUALS("output-status", name) || EQUALS("show", name)) { - for (;;) { - const char *val2 = value; - unsigned val2_len = INDEX_OF(val2, ','); - if (val2_len == 0) - break; - if (EQUALSx("open", val2, val2_len)) - masscan->output.is_show_open = 1; - else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) - masscan->output.is_show_closed = 1; - else if (EQUALSx("open", val2, val2_len)) - masscan->output.is_show_host = 1; - else if (EQUALSx("all",val2,val2_len)) { - masscan->output.is_show_open = 1; - masscan->output.is_show_host = 1; - masscan->output.is_show_closed = 1; - } - else { - LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2); - exit(1); - } - value += val2_len; - while (*value == ',') - value++; - } - } else if (EQUALS("noshow", name)) { - for (;;) { - const char *val2 = value; - unsigned val2_len = INDEX_OF(val2, ','); - if (val2_len == 0) - break; - if (EQUALSx("open", val2, val2_len)) - masscan->output.is_show_open = 0; - else if (EQUALSx("closed", val2, val2_len) || EQUALSx("close", val2, val2_len)) - masscan->output.is_show_closed = 0; - else if (EQUALSx("open", val2, val2_len)) - masscan->output.is_show_host = 0; - else { - LOG(0, "FAIL: unknown 'show' spec: %.*s\n", val2_len, val2); - exit(1); - } - value += val2_len; - while (*value == ',') - value++; - } } else if (EQUALS("osscan-limit", name)) { fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name); exit(1); } else if (EQUALS("osscan-guess", name)) { fprintf(stderr, "nmap(%s): OS scanning unsupported\n", name); exit(1); - } else if (EQUALS("output-format", name)) { - enum OutputFormat x = 0; - if (EQUALS("unknown(0)", value)) { - x = Output_Interactive; - } - else if (EQUALS("interactive", value)) x = Output_Interactive; - else if (EQUALS("list", value)) x = Output_List; - else if (EQUALS("unicornscan", value)) x = Output_Unicornscan; - else if (EQUALS("xml", value)) x = Output_XML; - else if (EQUALS("binary", value)) x = Output_Binary; - else if (EQUALS("greppable", value)) x = Output_Grepable; - else if (EQUALS("grepable", value)) x = Output_Grepable; - else if (EQUALS("json", value)) x = Output_JSON; - else if (EQUALS("ndjson", value)) x = Output_NDJSON; - else if (EQUALS("certs", value)) x = Output_Certs; - else if (EQUALS("none", value)) x = Output_None; - else if (EQUALS("redis", value)) x = Output_Redis; - else { - LOG(0, "FAIL: unknown output-format: %s\n", value); - LOG(0, " hint: 'binary', 'xml', 'grepable', ...\n"); - exit(1); - } - masscan->output.format = x; - } else if (EQUALS("output-filename", name) || EQUALS("output-file", name)) { - if (masscan->output.format == 0) - masscan->output.format = Output_XML; - strcpy_s(masscan->output.filename, - sizeof(masscan->output.filename), - value); - } else if (EQUALS("pcap", name)) { - strcpy_s(masscan->pcap_filename, sizeof(masscan->pcap_filename), value); } else if (EQUALS("packet-trace", name) || EQUALS("trace-packet", name)) { masscan->nmap.packet_trace = 1; } else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) { @@ -1633,9 +1927,6 @@ masscan_set_parameter(struct Masscan *masscan, } else if (EQUALS("port-ratio", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); - } else if (EQUALS("randomize-hosts", name)) { - /* already do that */ - ; } else if (EQUALS("readrange", name) || EQUALS("readranges", name)) { masscan->op = Operation_ReadRange; } else if (EQUALS("reason", name)) { @@ -1655,7 +1946,7 @@ masscan_set_parameter(struct Masscan *masscan, while (offset < max_offset && isspace(value[offset])) offset++; if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) { - port = strtoul(value+offset+1, 0, 0); + port = (unsigned)strtoul(value+offset+1, 0, 0); if (port > 65535 || port == 0) { LOG(0, "FAIL: bad redis port: %s\n", value+offset+1); exit(1); @@ -1674,34 +1965,6 @@ masscan_set_parameter(struct Masscan *masscan, } else if (EQUALS("resume", name)) { masscan_read_config_file(masscan, value); masscan_set_parameter(masscan, "output-append", "true"); - } else if (EQUALS("resume-index", name)) { - masscan->resume.index = parseInt(value); - } else if (EQUALS("resume-count", name)) { - masscan->resume.count = parseInt(value); - } else if (EQUALS("retries", name) || EQUALS("retry", name)) { - unsigned x = strtoul(value, 0, 0); - if (x >= 1000) { - fprintf(stderr, "error: retries=<n>: expected number less than 1000\n"); - } else { - masscan->retries = x; - } - } else if (EQUALS("rotate-output", name) || EQUALS("rotate", name) - || EQUALS("ouput-rotate", name) || EQUALS("rotate-time", name) ) { - masscan->output.rotate.timeout = (unsigned)parseTime(value); - } else if (EQUALS("rotate-offset", name) || EQUALS("ouput-rotate-offset", name)) { - masscan->output.rotate.offset = (unsigned)parseTime(value); - } else if (EQUALS("rotate-size", name) || EQUALS("rotate-filesize", name)) { - masscan->output.rotate.filesize = parseSize(value); - } else if (EQUALS("rotate-dir", name) || EQUALS("rotate-directory", name) || EQUALS("ouput-rotate-dir", name)) { - char *p; - strcpy_s( masscan->output.rotate.directory, - sizeof(masscan->output.rotate.directory), - value); - - /* strip trailing slashes */ - p = masscan->output.rotate.directory; - while (*p && (p[strlen(p)-1] == '/' || p[strlen(p)-1] == '/')) - p[strlen(p)-1] = '\0'; } else if (EQUALS("script", name)) { if (EQUALS("heartbleed", value)) { masscan_set_parameter(masscan, "heartbleed", "true"); @@ -1740,11 +2003,6 @@ masscan_set_parameter(struct Masscan *masscan, } else if (EQUALS("scanflags", name)) { fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name); exit(1); - } else if (EQUALS("seed", name)) { - if (EQUALS("time", value)) - masscan->seed = time(0); - else - masscan->seed = parseInt(value); } else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) { masscan->is_sendq = 1; } else if (EQUALS("send-eth", name)) { @@ -1760,31 +2018,6 @@ masscan_set_parameter(struct Masscan *masscan, return; } else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) { masscan_set_parameter(masscan, "adapter-port", value); - } else if (EQUALS("shard", name) || EQUALS("shards", name)) { - unsigned one = 0; - unsigned of = 0; - - while (isdigit(*value)) - one = one*10 + (*(value++)) - '0'; - while (ispunct(*value)) - value++; - while (isdigit(*value)) - of = of*10 + (*(value++)) - '0'; - - if (one < 1) { - LOG(0, "FAIL: shard index can't be zero\n"); - LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); - exit(1); - } - if (one > of) { - LOG(0, "FAIL: shard spec is wrong\n"); - LOG(0, "hint it goes like 1/4 2/4 3/4 4/4\n"); - exit(1); - } - - masscan->shard.one = one; - masscan->shard.of = of; - } else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) { ; } else if (EQUALS("no-stylesheet", name)) { @@ -1798,7 +2031,10 @@ masscan_set_parameter(struct Masscan *masscan, exit(1); } else if (EQUALS("top-ports", name)) { unsigned n = (unsigned)parseInt(value); - config_top_ports(masscan, n); + if (!isInteger(value)) + n = 100; + LOG(2, "top-ports = %u\n", n); + masscan->top_ports = n; } else if (EQUALS("traceroute", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); @@ -1809,7 +2045,7 @@ masscan_set_parameter(struct Masscan *masscan, if (EQUALS("csv", value)) masscan->is_test_csv = 0; } else if (EQUALS("ttl", name)) { - unsigned x = strtoul(value, 0, 0); + unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 256) { fprintf(stderr, "error: %s=<n>: expected number less than 256\n", name); } else { @@ -1842,6 +2078,7 @@ masscan_set_parameter(struct Masscan *masscan, masscan_set_parameter(masscan, "stylesheet", "http://nmap.org/svn/docs/nmap.xsl"); } else { fprintf(stderr, "CONF: unknown config option: %s=%s\n", name, value); + exit(1); } } @@ -1853,7 +2090,7 @@ static int is_singleton(const char *name) { static const char *singletons[] = { - "echo", "selftest", "self-test", "regress", + "echo", "echo-all", "selftest", "self-test", "regress", "benchmark", "system-dns", "traceroute", "version", "version-light", @@ -1863,12 +2100,10 @@ is_singleton(const char *name) "packet-trace", "release-memory", "log-errors", "append-output", "webxml", "no-stylesheet", "no-stylesheet", "heartbleed", "ticketbleed", - "send-eth", "send-ip", "iflist", "randomize-hosts", + "send-eth", "send-ip", "iflist", "nmap", "trace-packet", "pfring", "sendq", - "banners", "banner", "nobanners", "nobanner", "offline", "ping", "ping-sweep", "nobacktrace", "backtrace", - "arp", "infinite", "nointeractive", "interactive", "status", "nostatus", - "arpscan", "noreset", + "infinite", "nointeractive", "interactive", "status", "nostatus", "read-range", "read-ranges", "readrange", "read-ranges", 0}; size_t i; @@ -1877,6 +2112,20 @@ is_singleton(const char *name) if (EQUALS(singletons[i], name)) return 1; } + + for (i=0; config_parameters[i].name; i++) { + if (EQUALS(config_parameters[i].name, name)) { + return (config_parameters[i].flags & F_BOOL) == F_BOOL; + } else { + size_t j; + for (j=0; config_parameters[i].alts[j]; j++) { + if (EQUALS(config_parameters[i].alts[j], name)) { + return (config_parameters[i].flags & F_BOOL) == F_BOOL; + } + } + } + } + return 0; } @@ -1934,6 +2183,21 @@ masscan_command_line(struct Masscan *masscan, int argc, char *argv[]) if (argv[i][0] == '-' && argv[i][1] == '-') { if (strcmp(argv[i], "--help") == 0) { masscan_help(); + } else if (EQUALS("top-ports", argv[i]+2)) { + /* special handling here since the following parameter + * is optional */ + const char *value = "1000"; + unsigned n; + + /* Only consume the next parameter if it's a number, + * otherwise default to 10000 */ + if (i+1 < argc && isInteger(argv[i+1])) { + value = argv[++i]; + } + n = (unsigned)parseInt(value); + LOG(2, "top-ports = %u\n", n); + masscan->top_ports = n; + } else if (EQUALS("readscan", argv[i]+2)) { /* Read in a binary file instead of scanning the network*/ masscan->op = Operation_ReadScan; @@ -2269,8 +2533,150 @@ masscan_command_line(struct Masscan *masscan, int argc, char *argv[]) */ masscan_set_parameter(masscan, "range", argv[i]); } + + /* + * If no other "scan type" found, then default to TCP + */ + if (masscan->scan_type.udp == 0 && masscan->scan_type.sctp == 0 + && masscan->scan_type.ping == 0 && masscan->scan_type.arp == 0) + masscan->scan_type.tcp = 1; + + /* + * If "top-ports" specified, then add all those ports. This may be in + * addition to any other ports + */ + if (masscan->top_ports) { + config_top_ports(masscan, masscan->top_ports); + } +} + +/*************************************************************************** + * Prints the current configuration to the command-line then exits. + * Use#1: create a template file of all setable parameters. + * Use#2: make sure your configuration was interpreted correctly. + ***************************************************************************/ +static void +masscan_echo(struct Masscan *masscan, FILE *fp, unsigned is_echo_all) +{ + unsigned i; + unsigned l = 0; + + /* + * NEW: + * Print all configuration parameters + */ + masscan->echo = fp; + masscan->echo_all = is_echo_all; + for (i=0; config_parameters[i].name; i++) { + config_parameters[i].set(masscan, 0, 0); + } + masscan->echo = 0; + masscan->echo_all = 0; + + /* + * OLD: + * Things here below are the old way of echoing parameters. + * TODO: cleanup this code, replacing with the new way. + */ + if (masscan->nic_count == 0) + masscan_echo_nic(masscan, fp, 0); + else { + for (i=0; i<masscan->nic_count; i++) + masscan_echo_nic(masscan, fp, i); + } + + + /* + * Targets + */ + fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n"); + fprintf(fp, "ports = "); + /* Disable comma generation for the first element */ + l = 0; + for (i=0; i<masscan->ports.count; i++) { + struct Range range = masscan->ports.list[i]; + do { + struct Range rrange = range; + unsigned done = 0; + if (l) + fprintf(fp, ","); + l = 1; + if (rrange.begin >= Templ_ICMP_echo) { + rrange.begin -= Templ_ICMP_echo; + rrange.end -= Templ_ICMP_echo; + fprintf(fp,"I:"); + done = 1; + } else if (rrange.begin >= Templ_SCTP) { + rrange.begin -= Templ_SCTP; + rrange.end -= Templ_SCTP; + fprintf(fp,"S:"); + range.begin = Templ_ICMP_echo; + } else if (rrange.begin >= Templ_UDP) { + rrange.begin -= Templ_UDP; + rrange.end -= Templ_UDP; + fprintf(fp,"U:"); + range.begin = Templ_SCTP; + } else + range.begin = Templ_UDP; + rrange.end = min(rrange.end, 65535); + if (rrange.begin == rrange.end) + fprintf(fp, "%u", rrange.begin); + else + fprintf(fp, "%u-%u", rrange.begin, rrange.end); + if (done) + break; + } while (range.begin <= range.end); + } + fprintf(fp, "\n"); + for (i=0; i<masscan->targets.count; i++) { + struct Range range = masscan->targets.list[i]; + fprintf(fp, "range = "); + fprintf(fp, "%u.%u.%u.%u", + (range.begin>>24)&0xFF, + (range.begin>>16)&0xFF, + (range.begin>> 8)&0xFF, + (range.begin>> 0)&0xFF + ); + if (range.begin != range.end) { + unsigned cidr_bits = count_cidr_bits(range); + + if (cidr_bits) { + fprintf(fp, "/%u", cidr_bits); + } else + fprintf(fp, "-%u.%u.%u.%u", + (range.end>>24)&0xFF, + (range.end>>16)&0xFF, + (range.end>> 8)&0xFF, + (range.end>> 0)&0xFF + ); + } + fprintf(fp, "\n"); + } + + fprintf(fp, "\n"); + if (masscan->http_user_agent) + fprintf( fp, + "http-user-agent = %.*s\n", + masscan->http_user_agent_length, + masscan->http_user_agent); + + for (i=0; i<sizeof(masscan->http_headers)/sizeof(masscan->http_headers[0]); i++) { + if (masscan->http_headers[i].header_name == 0) + continue; + fprintf( fp, + "http-header[%s] = %.*s\n", + masscan->http_headers[i].header_name, + masscan->http_headers[i].header_value_length, + masscan->http_headers[i].header_value); + } + + + + + } + /*************************************************************************** * remove leading/trailing whitespace ***************************************************************************/ diff --git a/src/main.c b/src/main.c index 750a497f040c2e911389ebfc40395b78acf29240..0df770af49be02a8bffe4c3cc4a50e95064dbf46 100644 --- a/src/main.c +++ b/src/main.c @@ -786,7 +786,7 @@ receive_thread(void *v) * than port scanning them */ /* If we aren't doing an ARP scan, then ignore ARP responses */ - if (!masscan->is_arp) + if (!masscan->scan_type.arp) break; /* If this response isn't in our range, then ignore it */ diff --git a/src/masscan-app.c b/src/masscan-app.c index a7d88a464b66625d39b44c9e196a180f4f9b7331..9241c409a5a6c7f06082114e915a40649bce0d25 100644 --- a/src/masscan-app.c +++ b/src/masscan-app.c @@ -21,6 +21,7 @@ masscan_app_to_string(enum ApplicationProtocol proto) case PROTO_SNMP: return "snmp"; case PROTO_NBTSTAT: return "nbtstat"; case PROTO_SSL3: return "ssl"; + case PROTO_SMB: return "smb"; case PROTO_SMTP: return "smtp"; case PROTO_POP3: return "pop"; case PROTO_IMAP4: return "imap"; @@ -61,6 +62,7 @@ masscan_string_to_app(const char *str) {"ssh2", PROTO_SSH2}, {"nbtstat", PROTO_NBTSTAT}, {"ssl", PROTO_SSL3}, + {"smb", PROTO_SMB}, {"pop", PROTO_POP3}, {"imap", PROTO_IMAP4}, {"x509", PROTO_X509_CERT}, diff --git a/src/masscan-app.h b/src/masscan-app.h index 854ed6621cf6e33fdf11a4eca0295acb66c2b662..3dcfaffc0f4f86c734ed59634e6d3e3ad5fc27e1 100644 --- a/src/masscan-app.h +++ b/src/masscan-app.h @@ -16,6 +16,7 @@ enum ApplicationProtocol { PROTO_SNMP, /* simple network management protocol, udp/161 */ PROTO_NBTSTAT, /* netbios, udp/137 */ PROTO_SSL3, + PROTO_SMB, /* SMB tcp/139 and tcp/445 */ PROTO_SMTP, PROTO_POP3, PROTO_IMAP4, @@ -30,6 +31,10 @@ enum ApplicationProtocol { PROTO_VNC_RFB, PROTO_SAFE, PROTO_MEMCACHED, + + + + PROTO_end_of_list /* must be last one */ }; const char * diff --git a/src/masscan.h b/src/masscan.h index 234388e90101088fe5d89011a2ada556d41945c3..b6d19da3b4c172d951a4fdb9dc0f9ddd81917b06 100644 --- a/src/masscan.h +++ b/src/masscan.h @@ -42,6 +42,7 @@ enum Operation { * outputing simultaneously. */ enum OutputFormat { + Output_Default = 0x0000, Output_Interactive = 0x0001, /* --interactive, print to cmdline */ Output_List = 0x0002, Output_Binary = 0x0004, /* -oB, "binary", the primary format */ @@ -100,8 +101,21 @@ struct Masscan unsigned tcp:1; unsigned udp:1; unsigned sctp:1; - unsigned ping:1; + unsigned ping:1; /* --ping, ICMP echo */ + unsigned arp:1; /* --arp, local ARP scan */ } scan_type; + + /** + * After scan type has been configured, add these ports + */ + unsigned top_ports; + + /** + * Temporary file to echo parameters to, used for saving configuration + * to a file + */ + FILE *echo; + unsigned echo_all; /** * One or more network adapters that we'll use for scanning. Each adapter @@ -117,7 +131,7 @@ struct Masscan unsigned char router_mac[6]; unsigned router_ip; int link_type; /* libpcap definitions */ - unsigned char my_mac_count; + unsigned char my_mac_count; /*is there a MAC address? */ unsigned vlan_id; unsigned is_vlan:1; } nic[8]; @@ -174,8 +188,7 @@ struct Masscan unsigned is_sendq:1; /* --sendq */ unsigned is_banners:1; /* --banners */ unsigned is_offline:1; /* --offline */ - unsigned is_arp:1; /* --arp */ - unsigned is_noreset:1; /* --noreset */ + unsigned is_noreset:1; /* --noreset, don't transmit RST */ unsigned is_gmt:1; /* --gmt, all times in GMT */ unsigned is_capture_cert:1; /* --capture cert */ unsigned is_capture_html:1; /* --capture html */ diff --git a/src/proto-banner1.c b/src/proto-banner1.c index b3bb5871b6fd252108c709035558f6d254082c3e..1bc30d3748d6634971d5765ac2c18644b8e3cdc6 100644 --- a/src/proto-banner1.c +++ b/src/proto-banner1.c @@ -8,6 +8,7 @@ #include "proto-banner1.h" #include "proto-http.h" #include "proto-ssl.h" +#include "proto-smb.h" #include "proto-ssh.h" #include "proto-ftp.h" #include "proto-smtp.h" @@ -24,6 +25,16 @@ struct Patterns patterns[] = { + {"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS}, + {"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS}, + + {"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening on called name */ + {"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening for calling name */ + {"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name not present */ + {"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name present, but insufficient resources */ + {"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Unspecified error */ + + /* ...the remainder can be in any order */ {"SSH-1.", 6, PROTO_SSH1, SMACK_ANCHOR_BEGIN}, {"SSH-2.", 6, PROTO_SSH2, SMACK_ANCHOR_BEGIN}, {"HTTP/1.", 7, PROTO_HTTP, SMACK_ANCHOR_BEGIN}, @@ -31,6 +42,7 @@ struct Patterns patterns[] = { {"220 ", 4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1}, {"+OK ", 4, PROTO_POP3, SMACK_ANCHOR_BEGIN}, {"* OK ", 5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN}, + {"521 ", 4, PROTO_SMTP, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, {"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN}, @@ -141,7 +153,7 @@ banner1_parse( banout, more); break; - case PROTO_SMTP: + case PROTO_SMTP: banner_smtp.parse( banner1, banner1->http_fields, tcb_state, @@ -197,6 +209,15 @@ banner1_parse( banout, more); break; + case PROTO_SMB: + banner_smb1.parse( + banner1, + banner1->http_fields, + tcb_state, + px, length, + banout, + more); + break; case PROTO_VNC_RFB: banner_vnc.parse( banner1, banner1->http_fields, @@ -238,7 +259,9 @@ banner1_create(void) memset(b, 0, sizeof(*b)); /* - * These patterns match the start of the TCP stream + * This creates a pattern-matching blob for heuristically determining + * a protocol that runs on wrong ports, such as how FTP servers + * often respond with "220 " or VNC servers respond with "RFB". */ b->smack = smack_create("banner1", SMACK_CASE_INSENSITIVE); for (i=0; patterns[i].pattern; i++) @@ -251,12 +274,26 @@ banner1_create(void) smack_compile(b->smack); + /* + * This goes down the list of all the TCP protocol handlers and initializes + * them. + */ + banner_ftp.init(b); banner_http.init(b); - banner_vnc.init(b); + banner_imap4.init(b); banner_memcached.init(b); + banner_pop3.init(b); + banner_smtp.init(b); + banner_ssh.init(b); + banner_ssl.init(b); + banner_smb0.init(b); + banner_smb1.init(b); + banner_vnc.init(b); b->tcp_payloads[80] = &banner_http; b->tcp_payloads[8080] = &banner_http; + b->tcp_payloads[139] = (void*)&banner_smb0; + b->tcp_payloads[445] = (void*)&banner_smb1; b->tcp_payloads[443] = (void*)&banner_ssl; /* HTTP/s */ b->tcp_payloads[465] = (void*)&banner_ssl; /* SMTP/s */ @@ -437,7 +474,13 @@ banner1_selftest() fprintf(stderr, "SSL banner: selftest failed\n"); return 1; } - + + x = banner_smb1.selftest(); + if (x) { + fprintf(stderr, "SMB banner: selftest failed\n"); + return 1; + } + x = banner_http.selftest(); if (x) { fprintf(stderr, "HTTP banner: selftest failed\n"); diff --git a/src/proto-banner1.h b/src/proto-banner1.h index 0eb1998dc91178b37b01341cf9ea3bd401aad1ef..21a572dc3feb1e68dc4ec74b299bb5207e61840d 100644 --- a/src/proto-banner1.h +++ b/src/proto-banner1.h @@ -3,11 +3,21 @@ #include <stdint.h> #define STATE_DONE 0xFFFFFFFF #include <stdio.h> +#include "masscan-app.h" #include "proto-banout.h" #include "proto-x509.h" struct InteractiveData; +struct Banner1; +struct ProtocolState; +typedef void (*BannerParser)( + const struct Banner1 *banner1, + void *banner1_private, + struct ProtocolState *stream_state, + const unsigned char *px, size_t length, + struct BannerOutput *banout, + struct InteractiveData *more); struct Banner1 { struct SMACK *smack; @@ -25,6 +35,8 @@ struct Banner1 unsigned is_poodle_sslv3:1; struct ProtocolParserStream *tcp_payloads[65536]; + + BannerParser parser[PROTO_end_of_list]; }; struct BannerBase64 @@ -123,6 +135,48 @@ struct MEMCACHEDSTUFF { unsigned match; }; +struct Smb72_Negotiate { + uint16_t DialectIndex; + uint16_t SecurityMode; + uint64_t SystemTime; + uint32_t Capabilities; + uint16_t ServerTimeZone; + uint8_t ChallengeLength; + uint8_t ChallengeOffset; +}; + +struct SMBSTUFF { + unsigned char nbt_type; + unsigned char nbt_flags; + unsigned length; + unsigned nbt_err; + + struct { + unsigned char command; + unsigned status; + unsigned char flags1; + unsigned short flags2; + unsigned pid; + unsigned char signature[8]; + unsigned short tid; + unsigned short uid; + unsigned short mid; + unsigned short param_length; + unsigned short param_offset; + unsigned short byte_count; + unsigned short byte_offset; + unsigned short byte_state; + unsigned short unicode_char; + } smb1; + union { + struct Smb72_Negotiate negotiate; + } parms1; + + union { + + } pkt; +}; + struct ProtocolState { unsigned state; unsigned remaining; @@ -139,6 +193,7 @@ struct ProtocolState { struct SMTPSTUFF smtp; struct POP3STUFF pop3; struct MEMCACHEDSTUFF memcached; + struct SMBSTUFF smb; } sub; }; diff --git a/src/proto-banout.c b/src/proto-banout.c index ec07b26ef4f4fc0bb2294bf5105c404b2d578e88..e24553e39f4ac8c9d2740d23e7fdcd6bfb3c07a4 100644 --- a/src/proto-banout.c +++ b/src/proto-banout.c @@ -37,6 +37,7 @@ banout_release(struct BannerOutput *banout) free(banout->next); banout->next = next; } + banout_init(banout); } diff --git a/src/proto-smb.c b/src/proto-smb.c new file mode 100644 index 0000000000000000000000000000000000000000..28663a1c65f9f8ef3769c6b2cbc7810b18857f5c --- /dev/null +++ b/src/proto-smb.c @@ -0,0 +1,860 @@ +/* + SMB parser + + */ +#include "proto-smb.h" +#include "proto-interactive.h" +#include "unusedparm.h" +#include "masscan-app.h" +#include "siphash24.h" +#include "string_s.h" +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stddef.h> + + +/* + "NT LM 0.12" - Win2k + "SMB 2.002" 0x0202 Vista + "SMB 2.???" 0x02FF Win7, Windows 2008 + "PC NETWORK PROGRAM 1.0" MS-DOS + "MICROSOFT NETWORKS 1.03" MS-DOS + "MICROSOFT NETWORKS 3.0" MS-DOS + "LANMAN 1.0" OS/2 + "LM1.2X002" OS/2 + */ +/* +PC NETWORK PROGRAM 1.0 +LANMAN1.0 +Windows for Workgroups 3.1a +LM1.2X002 +LANMAN2.1 +NT LM 0.12 +SMB 2.002 +SMB 2.??? +Samba +XENIX CORE +*/ +/* + References: + http://pubs.opengroup.org/onlinepubs/9697999099/toc.pdf + */ + +struct SmbParams { + unsigned short command; + unsigned short external_offset; + unsigned char external_length; + unsigned char internal_type; + unsigned short internal_offset; +}; + +enum InternalType { + IT_uint0, + IT_uint8, + IT_uint16, + IT_uint32, + IT_uint64, +}; + +struct SmbParams params[] = { +/* + USHORT DialectIndex; + UCHAR SecurityMode; + USHORT MaxMpxCount; + USHORT MaxNumberVcs; + ULONG MaxBufferSize; + ULONG MaxRawSize; + ULONG SessionKey; + ULONG Capabilities; + FILETIME SystemTime; + SHORT ServerTimeZone; + UCHAR ChallengeLength; + */ + {0x72, 0, 2, IT_uint16, offsetof(struct Smb72_Negotiate, DialectIndex)}, + {0x72, 2, 1, IT_uint8, offsetof(struct Smb72_Negotiate, SecurityMode)}, + //{0x72, 3, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxMpxCount)}, + //{0x72, 5, 2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxNumberVcs)}, + //{0x72, 7, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxBufferSize)}, + //{0x72, 11, 4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxRawSize)}, + //{0x72, 15, 4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)}, + {0x72, 19, 4, IT_uint32, offsetof(struct Smb72_Negotiate, Capabilities)}, + {0x72, 23, 8, IT_uint64, offsetof(struct Smb72_Negotiate, SystemTime)}, + {0x72, 31, 2, IT_uint16, offsetof(struct Smb72_Negotiate, ServerTimeZone)}, + {0x72, 33, 1, IT_uint8, offsetof(struct Smb72_Negotiate, ChallengeLength)}, + + {0xFF, 0, 65536, IT_uint0, 0}, + +}; + +#define memberat(t, s, offset) (t*)((char*)(s)+(offset)) + +static char smb1_null_session_setup[] = { + 0x00, 0x00, 0x00, 0xbe, 0xff, 0x53, 0x4d, 0x42, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0x80, + 0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06, + 0xe8, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xca, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x75, 0x00, 0x84, + 0x00, 0x04, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4e, 0x00, + 0x54, 0x00, 0x20, 0x00, 0x31, 0x00, 0x33, 0x00, + 0x38, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, + 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, + 0x4e, 0x00, 0x54, 0x00, 0x20, 0x00, 0x34, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x2f, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x30, 0x00, + 0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x5c, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x00 +}; + + +/***************************************************************************** + * + * ****** WARNING: UGLY HACK !!!! ****** + * + * This code is an ugly hack so I can express SMB parameters (word_count) + * headers as a structure instead of writing individual parsers for them. + * This code makes no sense. If you find a bug in it, it's probably worth + * rewriting rather than figure out its convoluted logic. No really, I mean + * this. + * + *****************************************************************************/ +static size_t +smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max) +{ + size_t original_offset = offset; + size_t c; + + if (max > offset + (smb->smb1.param_length - smb->smb1.param_offset)) + max = offset + (smb->smb1.param_length - smb->smb1.param_offset); + + //printf("\n max=%04x \n", *(unsigned short*)(px+max)); + + /* Find the correct header */ + for (c=0; params[c].command != smb->smb1.command && params[c].command != 0xFF; c++) + ; + + for (; offset < max; offset++, smb->smb1.param_offset++) { + again: + + //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c); + + /* If we've gone past our header, just continue consuming bytes */ + if (params[c].command != smb->smb1.command) + continue; + + /* If we've gone past the end of this field, goto next field */ + if (params[c].external_offset + params[c].external_length <= smb->smb1.param_offset) { + c++; + goto again; + } + /* Haven't reached the next field yet */ + if (params[c].external_offset > smb->smb1.param_offset) + continue; + + //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c, px[offset]); + + /* Shift the type, because all fields little-endian */ + switch (params[c].internal_type) { + case IT_uint0: + default: + break; + case IT_uint8: + { + uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset); + *x = px[offset]; + } + break; + case IT_uint16: + { + uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + case IT_uint32: + { + uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + case IT_uint64: + { + uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset); + //*x <<= 8; + *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8); + } + break; + } + + } + + /* Return the number of bytes processed */ + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static const unsigned long long TICKS_PER_SECOND = 10000000LL; +static const unsigned long long EPOCH_DIFFERENCE = 11644473600LL; +static time_t +convert_windows_time(long long int filetime) +{ + unsigned long long seconds = filetime / TICKS_PER_SECOND; + seconds -= EPOCH_DIFFERENCE; + return (time_t)seconds; +} + +/***************************************************************************** + *****************************************************************************/ +enum { + D_START, + D_NEGOT_CHALLENGE, + D_NEGOT_DOMAINA, + D_NEGOT_NAMEA, + D_NEGOT_DOMAIN1, + D_NEGOT_DOMAIN2, + D_NEGOT_NAME1, + D_NEGOT_NAME2, + D_NEGOT_END, + + D_UNKNOWN, +}; + +/***************************************************************************** + *****************************************************************************/ +static void +name_append_char(struct BannerOutput *banout, unsigned c) +{ + if (c & 0xFF80) + banout_append_char(banout, PROTO_SMB, '.'); + else if (isalnum(c)) + banout_append_char(banout, PROTO_SMB, c); + else switch (c) { + case '-': + case '_': + case '$': + case '*': + banout_append_char(banout, PROTO_SMB, c); + break; + default: + banout_append_char(banout, PROTO_SMB, '.'); + break; + + } +} +/***************************************************************************** + *****************************************************************************/ +static size_t +smb1_parse_data(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout) +{ + size_t original_offset = offset; + unsigned state = smb->smb1.byte_state; + + + if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset)) + max = offset + (smb->smb1.byte_count - smb->smb1.byte_offset); + + for (;offset<max; offset++) + switch (state) { + case D_START: + state = D_UNKNOWN; + break; + case D_NEGOT_CHALLENGE: + if (smb->parms1.negotiate.ChallengeLength == 0) { + if (smb->smb1.flags2 & 0x0080) { + state = D_NEGOT_DOMAIN1; + } else { + state = D_NEGOT_DOMAINA; + } + offset--; + } else + smb->parms1.negotiate.ChallengeLength--; + break; + case D_NEGOT_DOMAIN1: + case D_NEGOT_NAME1: + smb->smb1.unicode_char = px[offset]; + state++; + break; + case D_NEGOT_DOMAIN2: + case D_NEGOT_DOMAINA: + case D_NEGOT_NAME2: + case D_NEGOT_NAMEA: + smb->smb1.unicode_char |= px[offset] << 8; + if (state == D_NEGOT_DOMAINA || state == D_NEGOT_NAMEA) + smb->smb1.unicode_char >>= 8; + if (smb->smb1.unicode_char == 0) { + banout_append_char(banout, PROTO_SMB, ' '); + state++; + } else { + name_append_char(banout, smb->smb1.unicode_char); + state--; + } + break; + default: + break; + } + + smb->smb1.byte_state = state; + smb->smb1.byte_offset += (offset - original_offset); + return offset - original_offset; +} + +/***************************************************************************** + *****************************************************************************/ +static void +smb_parse_record( + const struct Banner1 *banner1, + void *banner1_private, + struct ProtocolState *pstate, + const unsigned char *px, size_t length, + struct BannerOutput *banout, + struct InteractiveData *more) +{ + size_t len; /*scratch variables used in a couple places */ + size_t max; + unsigned state = pstate->state; + struct SMBSTUFF *smb = &pstate->sub.smb; + size_t i; + enum { + NBT_TYPE, + NBT_FLAGS, + NBT_LEN1, + NBT_LEN2, + NBT_ERR, + NBT_DRAIN, + + SMB_VER, + SMB1_VER_S, SMB1_VER_M, SMB1_VER_B, + + SMB1_CMD, + SMB1_STATUS1, SMB1_STATUS2, SMB1_STATUS3, SMB1_STATUS4, + SMB1_FLAGS1, + SMB1_FLAGS2, + SMB1_FLAGS3, + SMB1_PID1, SMB1_PID2, + SMB1_SIG1, SMB1_SIG2, SMB1_SIG3, SMB1_SIG4, + SMB1_SIG5, SMB1_SIG6, SMB1_SIG7, SMB1_SIG8, + SMB1_RSVD1,SMB1_RSVD2, + SMB1_TID1, SMB1_TID2, + SMB1_PID3, SMB1_PID4, + SMB1_UID1, SMB1_UID2, + SMB1_MID1, SMB1_MID2, + SMB1_WORD_COUNT, + SMB1_PARAMETERS, + SMB1_BYTE_COUNT1, + SMB1_BYTE_COUNT2, + SMB1_DATA, + SMB1_DATA_AFTER, + /* + UCHAR Protocol[4]; + UCHAR Command; + SMB_ERROR Status; + UCHAR Flags; + USHORT Flags2; + USHORT PIDHigh; + UCHAR SecurityFeatures[8]; + USHORT Reserved; + USHORT TID; + USHORT PIDLow; + USHORT UID; + USHORT MID; + */ + + + SMB2_VER_S, SMB2_VER_M, SMB2_VER_B, + NBT_UNKNOWN, + }; + + /* + * On first run thourhg, the offset will be zero + */ + i = 0; + +again: + + /* + * Make sure we don't go past the end of the NetBIOS header portion + */ + max = length; + if (state > NBT_LEN2) { + if (max > i + smb->length) + max = i + smb->length; + } + + + /* + * `for all bytes in the segment` + * `do a state transition for that byte ` + */ + for (; i<max; i++) + switch (state) { + /* + All session packets are of the following general structure: + + 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TYPE | FLAGS | LENGTH | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + / TRAILER (Packet Type Dependent) / + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + case NBT_TYPE: + smb->nbt_type = px[i]; + state++; + break; + case NBT_FLAGS: + smb->nbt_flags = px[i]; + smb->length = 0; + state++; + break; + case NBT_LEN1: + smb->length <<= 8; + smb->length |= px[i]; + state++; + break; + case NBT_LEN2: + smb->length <<= 8; + smb->length |= px[i]; + if (max > i + smb->length) + max = i + smb->length; + + /* + 00 - SESSION MESSAGE + 81 - SESSION REQUEST + 82 - POSITIVE SESSION RESPONSE + 83 - NEGATIVE SESSION RESPONSE + 84 - RETARGET SESSION RESPONSE + 85 - SESSION KEEP ALIVE + */ + switch (smb->nbt_type) { + case 0x00: + state = SMB_VER; + break; + case 0x81: + banout_append(banout, PROTO_SMB, "ERR session request", AUTO_LEN); + state = NBT_UNKNOWN; + break; + case 0x82: + case 0x85: + state = NBT_DRAIN; + break; + case 0x83: + state = NBT_ERR; + break; + case 0x84: + banout_append(banout, PROTO_SMB, "ERR retarget", AUTO_LEN); + state = NBT_UNKNOWN; + break; + default: + banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN); + break; + } + break; + case NBT_ERR: + smb->nbt_err = px[i]; + /* + 80 - Not listening on called name + 81 - Not listening for calling name + 82 - Called name not present + 83 - Called name present, but insufficient resources + 8F - Unspecified error + */ + switch (smb->nbt_err) { + case 0x80: + banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN); + break; + case 0x81: + banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN); + break; + case 0x82: + banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN); + break; + case 0x83: + banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN); + break; + case 0x8F: + banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN); + break; + default: + banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN); + break; + + } + state = NBT_DRAIN; + break; + + case NBT_DRAIN: + state = NBT_DRAIN; + break; + + case SMB_VER: + switch (px[i]) { + case 0xFF: + banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN); + state = SMB1_VER_S; + break; + case 0xFE: + banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN); + state = SMB2_VER_S; + break; + default: + banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN); + state = NBT_UNKNOWN; + } + break; + case SMB1_VER_S: + case SMB2_VER_S: + + if (px[i] != 'S') + state = NBT_UNKNOWN; + else + state++; + break; + case SMB1_VER_M: + case SMB2_VER_M: + if (px[i] != 'M') + state = NBT_UNKNOWN; + else + state++; + break; + case SMB1_VER_B: + case SMB2_VER_B: + if (px[i] != 'B') + state = NBT_UNKNOWN; + else + state++; + break; + + case SMB1_CMD: + memset(&smb->smb1, 0, sizeof(smb->smb1)); + smb->smb1.command = px[i]; + state++; + break; + case SMB1_STATUS1: case SMB1_STATUS2: case SMB1_STATUS3: case SMB1_STATUS4: + smb->smb1.status <<= 8; + smb->smb1.status |= px[i]; + state++; + break; + case SMB1_FLAGS1: + smb->smb1.flags1 = px[i]; + state++; + break; + case SMB1_FLAGS2: + case SMB1_FLAGS3: + smb->smb1.flags2 <<= 8; + smb->smb1.flags2 |= px[i]; + state++; + break; + case SMB1_PID1: case SMB1_PID2: + smb->smb1.pid <<= 8; + smb->smb1.pid |= px[i]; + state++; + break; + case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4: + case SMB1_SIG5: case SMB1_SIG6: case SMB1_SIG7: case SMB1_SIG8: + state++; + break; + case SMB1_RSVD1:case SMB1_RSVD2: + state++; + break; + case SMB1_TID1: case SMB1_TID2: + smb->smb1.tid <<= 8; + smb->smb1.tid |= px[i]; + state++; + break; + case SMB1_PID3: case SMB1_PID4: + smb->smb1.pid <<= 8; + smb->smb1.pid |= px[i]; + state++; + break; + case SMB1_UID1: case SMB1_UID2: + smb->smb1.uid <<= 8; + smb->smb1.uid |= px[i]; + state++; + break; + case SMB1_MID1: case SMB1_MID2: + smb->smb1.mid <<= 8; + smb->smb1.mid |= px[i]; + state++; + break; + case SMB1_WORD_COUNT: + smb->smb1.param_length = px[i]*2; + state++; + break; + case SMB1_PARAMETERS: + /* Transfer control to a sub-parser, which may consume zero + * or more bytes, up to the end of the parameters field + * (meaning, up to word_count*2 bytes) */ + len = smb_params_parse(smb, px, i, max); + i += len; + if (smb->smb1.param_offset < smb->smb1.param_length) + break; + + /* We've reached the end of the parameters field, so go onto + * read the byte-count/data field */ + state = SMB1_BYTE_COUNT1; + + /* Unconsume the next byte. The "word-count" field may have been + * zero when we get to this state, so therefore the logic needs + * to be written to handle this. That means when we loop around + * again, we need to counter-act the fact that we will automatically + * increment the index, so we substract one from it here. */ + i--; + + /* Process the parameter/word-count field according to what it + * actually contained + * TODO: I should make this a function, but I'm lazy + */ + switch (smb->smb1.command) { + case 0x72: + { + char str[64] = "(err)"; + time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime); + struct tm tm = {0}; + + gmtime_s(&tm, ×tamp); + + len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S ", &tm); + banout_append(banout, PROTO_SMB, str, len); + sprintf_s(str, sizeof(str), "TZ%+d ", (short)smb->parms1.negotiate.ServerTimeZone); + banout_append(banout, PROTO_SMB, str, AUTO_LEN); + smb->smb1.byte_state = D_NEGOT_CHALLENGE; + + //more->payload = smb1_null_session_setup; + //more->length = sizeof(smb1_null_session_setup); + + } + break; + default: + banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN); + smb->smb1.byte_state = D_UNKNOWN; + } + + break; + + case SMB1_BYTE_COUNT1: + smb->smb1.byte_count = px[i]; + state++; + break; + case SMB1_BYTE_COUNT2: + smb->smb1.byte_count |= px[i]<<8; + state++; + break; + case SMB1_DATA: + i += smb1_parse_data(smb, px, i, max, banout); + if (smb->smb1.byte_offset >= smb->smb1.byte_count) { + state = SMB1_DATA_AFTER; + i--; /* unconsume byte because of auto-increment */ + } + break; + + case SMB1_DATA_AFTER: + if (i < max) { + ; + } else { + state = 0; + i--; + } + break; + + default: + i = length; + break; + } + + /* + * If there are multiple response packets, then + * loop around and process the next one + */ + if (i < length) { + state = 0; + goto again; + } + + pstate->state = state; +} + +/***************************************************************************** + *****************************************************************************/ +static void * +smb_init(struct Banner1 *banner1) +{ + UNUSEDPARM(banner1); + return 0; +} + +/***************************************************************************** + *****************************************************************************/ + +static const char +smb0_hello_template[] = { + 0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46, + 0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45, + 0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46, + 0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45, + 0x44, 0x46, 0x43, 0x45, 0x46, 0x45, 0x46, 0x45, + 0x44, 0x45, 0x49, 0x45, 0x46, 0x46, 0x43, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00, + + 0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, + 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, + 0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, + 0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, + 0x00 +}; +static const char +smb1_hello_template[] = { + 0x00, 0x00, 0x00, 0x2f, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, /* */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, + 0x31, 0x32, 0x00 +}; + +/***************************************************************************** + *****************************************************************************/ +static int +smb_selftest(void) +{ + struct Banner1 *banner1; + struct ProtocolState state[1]; + struct BannerOutput banout1[1]; + struct InteractiveData more; + int x; + size_t i; + + unsigned char packet_bytes[] = { + 0x00, 0x00, 0x00, 0x69, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x00, 0x03, + 0x10, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0xe3, 0x01, 0x00, 0x1f, 0xac, 0xe7, 0x7f, + 0x8a, 0xf0, 0xd3, 0x01, 0xf0, 0x00, 0x08, 0x24, + 0x00, 0xc2, 0xe5, 0x34, 0x10, 0xfd, 0x29, 0xa7, + 0x75, 0x42, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x00, + 0x00, 0x53, 0x00, 0x48, 0x00, 0x49, 0x00, 0x50, + 0x00, 0x42, 0x00, 0x41, 0x00, 0x52, 0x00, 0x42, + 0x00, 0x4f, 0x00, 0x00, 0x00, + + /*0x00, 0x00, 0x00, 0x90, 0xff, 0x53, 0x4d, 0x42, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x98, 0x03, 0x80, + 0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06, + 0xe8, 0xfc, 0x00, 0x00, 0x00, 0x08, 0xfe, 0xca, + 0x00, 0x08, 0x00, 0x00, 0x03, 0x75, 0x00, 0x81, + 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x35, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00, + 0x4c, 0x00, 0x41, 0x00, 0x4e, 0x00, 0x20, 0x00, + 0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, + 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x53, 0x00, 0x45, 0x00, + 0x41, 0x00, 0x55, 0x00, 0x00, 0x03, 0xff, 0x00, + 0x90, 0x00, 0x01, 0x00, 0x06, 0x00, 0x49, 0x50, + 0x43, 0x00, 0x00, 0x00*/ + + }; + + banner1 = banner1_create(); + banout_init(banout1); + memset(&state[0], 0, sizeof(state[0])); + + smb_parse_record(banner1, + 0, + state, + packet_bytes, + sizeof(packet_bytes), + banout1, + &more); + x = banout_is_contains(banout1, PROTO_SMB, + "SHIPBAR"); + if (!x) { + printf("smb parser failure: google.com\n"); + return 1; + } + banner1_destroy(banner1); + banout_release(banout1); + + /* + * LET'S FUZZ THIS CRAP!!! + * + * We are going to re-parse the response packet as many times as needed, + * each time flipping one bit in the packet. This should crash the + * parser if it has such a bug that will crash it. + */ + for (i=2; i< 5 && i<sizeof(packet_bytes); i++) { + size_t j; + + for (j=0; j<8; j++) { + size_t flip = 1<<j; + + packet_bytes[i] ^= flip; + + banner1 = banner1_create(); + banout_init(banout1); + memset(&state[0], 0, sizeof(state[0])); + + smb_parse_record(banner1, + 0, + state, + packet_bytes, + sizeof(packet_bytes), + banout1, + &more); + banner1_destroy(banner1); + banout_release(banout1); + + packet_bytes[i] ^= flip; + + } + } + return 0; +} + +/***************************************************************************** + * This is the 'plugin' structure that registers callbacks for this parser in + * the main system. + *****************************************************************************/ +struct ProtocolParserStream banner_smb0 = { + "smb", 139, smb0_hello_template, sizeof(smb0_hello_template), 0, + smb_selftest, + smb_init, + smb_parse_record, +}; +struct ProtocolParserStream banner_smb1 = { + "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0, + smb_selftest, + smb_init, + smb_parse_record, +}; + diff --git a/src/proto-smb.h b/src/proto-smb.h new file mode 100644 index 0000000000000000000000000000000000000000..98649e838c1c421860a8162371d046633641f539 --- /dev/null +++ b/src/proto-smb.h @@ -0,0 +1,8 @@ +#ifndef PROTO_SMB_H +#define PROTO_SMB_H +#include "proto-banner1.h" + +extern struct ProtocolParserStream banner_smb0; +extern struct ProtocolParserStream banner_smb1; + +#endif diff --git a/src/proto-tcp.c b/src/proto-tcp.c index 1a81a3cc58c2b011fe6d9564c1f50ee486b6537d..f04f1df118ef18383893152d56ceca6aad74125f 100644 --- a/src/proto-tcp.c +++ b/src/proto-tcp.c @@ -313,7 +313,7 @@ tcpcon_set_parameter(struct TCP_ConnectionTable *tcpcon, fprintf(stderr, "tcpcon: parmeter: expected array []: %s\n", name); exit(1); } - port = strtoul(p+1, 0, 0); + port = (unsigned)strtoul(p+1, 0, 0); x = banner1->tcp_payloads[port]; if (x == NULL) { @@ -460,6 +460,42 @@ enum DestroyReason { }; +/*************************************************************************** + * Flush all the banners asssociated with this TCP connection. This always + * called when TCB is destroyed. This may also be called earlier, such + * as when a FIN is received. + ***************************************************************************/ +static void +tcpcon_flush_banners(struct TCP_ConnectionTable *tcpcon, struct TCP_Control_Block *tcb) +{ + struct BannerOutput *banout; + + /* Go through and print all the banners. Some protocols have + * multiple banners. For example, web servers have both + * HTTP and HTML banners, and SSL also has several + * X.509 certificate banners */ + for (banout = &tcb->banout; banout != NULL; banout = banout->next) { + if (banout->length && banout->protocol) { + tcpcon->report_banner( + tcpcon->out, + global_now, + tcb->ip_them, + 6, /*TCP protocol*/ + tcb->port_them, + banout->protocol & 0x0FFFFFFF, + tcb->ttl, + banout->banner, + banout->length); + } + } + + /* + * Free up all the banners. + */ + banout_release(&tcb->banout); + +} + /*************************************************************************** * Destroy a TCP connection entry. We have to unlink both from the * TCB-table as well as the timeout-table. @@ -473,8 +509,7 @@ tcpcon_destroy_tcb( { unsigned index; struct TCP_Control_Block **r_entry; - struct BannerOutput *banout; - + UNUSEDPARM(reason); //printf("." "tcb age = %u-sec, reason=%u \n", time(0) - tcb->when_created, reason); @@ -512,27 +547,10 @@ tcpcon_destroy_tcb( /* * Print out any banners associated with this TCP session. Most of the - * time, there'll only be one. + * time, there'll only be one. After printing them out, delete the + * banners. */ - for (banout = &tcb->banout; banout != NULL; banout = banout->next) { - if (banout->length && banout->protocol) { - tcpcon->report_banner( - tcpcon->out, - global_now, - tcb->ip_them, - 6, /*TCP protocol*/ - tcb->port_them, - banout->protocol & 0x0FFFFFFF, - tcb->ttl, - banout->banner, - banout->length); - } - } - - /* - * If there are multiple banners, then free the additional ones - */ - banout_release(&tcb->banout); + tcpcon_flush_banners(tcpcon, tcb); /* * Unlink this from the timeout system. @@ -1238,6 +1256,7 @@ tcpcon_handle(struct TCP_ConnectionTable *tcpcon, 0x11, 0, 0, 0); tcb->seqno_me++; + tcpcon_flush_banners(tcpcon, tcb); break; case STATE_WAITING_FOR_RESPONSE<<8 | TCP_WHAT_TIMEOUT: diff --git a/src/ranges.c b/src/ranges.c index df53fd36ac6e2f3d204d65c5108c61218ae11284..2a636eb04f10ec380493a62b3fc1dfcd6eebec41 100644 --- a/src/ranges.c +++ b/src/ranges.c @@ -642,11 +642,11 @@ rangelist_parse_ports(struct RangeList *ports, const char *string, unsigned *is_ if (!isdigit(p[0] & 0xFF)) break; - port = strtoul(p, &p, 0); + port = (unsigned)strtoul(p, &p, 0); end = port; if (*p == '-') { p++; - end = strtoul(p, &p, 0); + end = (unsigned)strtoul(p, &p, 0); } if (port > 0xFFFF || end > 0xFFFF || end < port) { diff --git a/src/rawsock-getif.c b/src/rawsock-getif.c index 77d6e2259afb8bf722f2ee0e8a13b9b6609f40c4..4da549255d3fd32f8d7d641e3cc8fb83d65e0d5c 100644 --- a/src/rawsock-getif.c +++ b/src/rawsock-getif.c @@ -43,7 +43,7 @@ int rawsock_get_default_interface(char *ifname, size_t sizeof_ifname) { int fd; - int seq = time(0); + int seq = (int)time(0); int err; struct rt_msghdr *rtm; size_t sizeof_buffer; diff --git a/src/rawsock-getroute.c b/src/rawsock-getroute.c index 95af493774fc3f0e29ae05804bf8fdb60a15a679..cbc399ad085b0d442795199e0e13792baa41dc4a 100644 --- a/src/rawsock-getroute.c +++ b/src/rawsock-getroute.c @@ -99,7 +99,7 @@ int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4) { int fd; - int seq = time(0); + int seq = (int)time(0); int err; struct rt_msghdr *rtm; size_t sizeof_buffer; diff --git a/src/rawsock-pcapfile.c b/src/rawsock-pcapfile.c index cd879bacab2dbb63f2a4ac3aadc74cf8d546e0c3..d6c583f1a5915b7cf8ae5c593e3073f84c406637 100644 --- a/src/rawsock-pcapfile.c +++ b/src/rawsock-pcapfile.c @@ -613,7 +613,7 @@ struct PcapFile *pcapfile_openread(const char *capfilename) /* Read the first frame's timestamp */ { - int loc; + long loc; char tsbuf[8]; size_t x; diff --git a/src/smack.h b/src/smack.h index 4fce5562ad105289cf9d53ae7c6556fa0a5ae142..7238267cc8ce9d4aa6cbb86d29d95edaef35584d 100644 --- a/src/smack.h +++ b/src/smack.h @@ -14,6 +14,7 @@ enum { SMACK_ANCHOR_BEGIN = 0x01, SMACK_ANCHOR_END = 0x02, SMACK_SNMP_HACK = 0x04, + SMACK_WILDCARDS = 0x08, }; enum { diff --git a/src/smack1.c b/src/smack1.c index 34f72a90a3e1ebed609a4a689212d0d923a26c09..149eceb2c873aede5de60f885e48e8dfb4a1a17b 100644 --- a/src/smack1.c +++ b/src/smack1.c @@ -212,6 +212,8 @@ struct SmackPattern unsigned is_anchor_end:1; unsigned is_snmp_hack:1; + + unsigned is_wildcards:1; }; @@ -673,6 +675,7 @@ smack_add_pattern( pat->is_anchor_begin = ((flags & SMACK_ANCHOR_BEGIN) > 0); pat->is_anchor_end = ((flags & SMACK_ANCHOR_END) > 0); pat->is_snmp_hack = ((flags & SMACK_SNMP_HACK) > 0); + pat->is_wildcards = ((flags & SMACK_WILDCARDS) > 0); pat->id = id; pat->pattern = make_copy_of_pattern(pattern, pattern_length, smack->is_nocase); if (pat->is_anchor_begin) @@ -1050,6 +1053,59 @@ smack_stage3_sort(struct SMACK *smack) smack->m_match_limit = start; } +/**************************************************************************** + * + * KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE + * + * This function currently only works in a very narrow case, for the SMB + * parser, where all the patterns are "anchored" and none overlap with the + * the SMB patterns. This allows us to modify existing states with the + * the wildcards, without adding new states. Do do this right we need + * to duplicate states in order to track wildcards + ****************************************************************************/ +static void +smack_fixup_wildcards(struct SMACK *smack) +{ + size_t i; + + for (i=0; i<smack->m_pattern_count; i++) { + size_t j; + struct SmackPattern *pat = smack->m_pattern_list[i]; + + /* skip patterns that aren't wildcards */ + if (!pat->is_wildcards) + continue; + + /* find the state leading up to the wilcard * character */ + for (j=0; j<pat->pattern_length; j++) { + unsigned row = 0; + unsigned offset = 0; + size_t row_size = (1 << smack->row_shift); + transition_t *table; + transition_t next_pattern; + transition_t base_state = (smack->is_anchor_begin?1:0); + size_t k; + + /* Skip non-wildcard characters */ + if (pat->pattern[j] != '*') + continue; + + /* find the current 'row' */ + while (offset < j) + smack_search_next(smack, &row, pat->pattern, &offset, (unsigned)j); + + row = row & 0xFFFFFF; + table = smack->table + (row << smack->row_shift); + next_pattern = table[smack->char_to_symbol['*']]; + + for (k=0; k<row_size; k++) { + if (table[k] == base_state) + table[k] = next_pattern; + } + } + } + +} /**************************************************************************** ****************************************************************************/ void @@ -1106,12 +1162,18 @@ smack_compile(struct SMACK *smack) swap_rows(smack, BASE_STATE, UNANCHORED_STATE); } + /* prettify table for debugging */ smack_stage3_sort(smack); /* * Build the final table we use for evaluation */ smack_stage4_make_final_table(smack); + + /* + * Fixup the wildcard states + */ + smack_fixup_wildcards(smack); /* * Get rid of the original pattern tables, since we no longer need them. diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj index c008f4f3c58633d6c5c7e1d4a396dc916e3b3296..b36144acc29fd3cdf1db64863cb23fb32072acde 100644 --- a/xcode4/masscan.xcodeproj/project.pbxproj +++ b/xcode4/masscan.xcodeproj/project.pbxproj @@ -89,6 +89,7 @@ 11C936C31EDCE77F0023D32E /* in-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936BF1EDCE77F0023D32E /* in-filter.c */; }; 11C936C41EDCE77F0023D32E /* in-report.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C11EDCE77F0023D32E /* in-report.c */; }; 11C936C71EDCE8B40023D32E /* rawsock-pcap.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */; }; + 11DE129620ABC2650041135D /* proto-smb.c in Sources */ = {isa = PBXBuildFile; fileRef = 11DE129520ABC2650041135D /* proto-smb.c */; }; 11E76DB41889BC5200061F45 /* pixie-backtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 11E76DB21889BC5200061F45 /* pixie-backtrace.c */; }; 11F9375419F1A54200C1947F /* script-sslv3.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375319F1A54200C1947F /* script-sslv3.c */; }; 11F9375719F1AD5000C1947F /* script-heartbleed.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375619F1AD5000C1947F /* script-heartbleed.c */; }; @@ -261,6 +262,8 @@ 11C936C21EDCE77F0023D32E /* in-report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "in-report.h"; sourceTree = "<group>"; }; 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "rawsock-pcap.c"; sourceTree = "<group>"; }; 11C936C61EDCE8B40023D32E /* rawsock-pcap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "rawsock-pcap.h"; sourceTree = "<group>"; }; + 11DE129420ABC2650041135D /* proto-smb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-smb.h"; sourceTree = "<group>"; }; + 11DE129520ABC2650041135D /* proto-smb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-smb.c"; sourceTree = "<group>"; }; 11E76DB21889BC5200061F45 /* pixie-backtrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pixie-backtrace.c"; sourceTree = "<group>"; }; 11E76DB31889BC5200061F45 /* pixie-backtrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pixie-backtrace.h"; sourceTree = "<group>"; }; 11F9375319F1A54200C1947F /* script-sslv3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "script-sslv3.c"; sourceTree = "<group>"; }; @@ -388,6 +391,8 @@ 11B360CA1F9016C00020F3A3 /* proto */ = { isa = PBXGroup; children = ( + 11DE129420ABC2650041135D /* proto-smb.h */, + 11DE129520ABC2650041135D /* proto-smb.c */, 119AB2042051FFED008E4DDD /* proto-memcached.c */, 119AB2052051FFED008E4DDD /* proto-memcached.h */, 11A921AC17DBCC7E00DDFD32 /* proto-arp.c */, @@ -604,6 +609,7 @@ 11A921F817DBCC7E00DDFD32 /* syn-cookie.c in Sources */, 11A921F917DBCC7E00DDFD32 /* templ-pkt.c in Sources */, 11A921FA17DBCC7E00DDFD32 /* xring.c in Sources */, + 11DE129620ABC2650041135D /* proto-smb.c in Sources */, 11B2DD9E17DE4DD8007FC363 /* templ-payloads.c in Sources */, 11AC80ED17E0DAD4001BCE3A /* proto-http.c in Sources */, 11AC80EE17E0DAD4001BCE3A /* proto-icmp.c in Sources */,