/* Read in the configuration for MASSCAN. Configuration parameters can be read either from the command-line or a configuration file. Long parameters of the --xxxx variety have the same name in both. Most of the code in this module is for 'nmap' options we don't support. That's because we support some 'nmap' options, and I wanted to give more feedback for some of them why they don't work as expected, such as reminding people that this is an asynchronous scanner. */ #include "masscan.h" #include "massip-addr.h" #include "masscan-version.h" #include "util-safefunc.h" #include "util-logger.h" #include "proto-banner1.h" #include "templ-payloads.h" #include "crypto-base64.h" #include "vulncheck.h" #include "masscan-app.h" #include "unusedparm.h" #include "read-service-probes.h" #include "util-malloc.h" #include "massip.h" #include "massip-parse.h" #include "massip-port.h" #include "templ-opts.h" #include #include #ifdef WIN32 #include #define getcwd _getcwd #else #include #endif #ifndef min #define min(a,b) ((a)<(b)?(a):(b)) #endif #if defined(_MSC_VER) #define strdup _strdup #endif /*************************************************************************** ***************************************************************************/ /*static struct Range top_ports_tcp[] = { {80, 80},{23, 23}, {443,443},{21,22},{25,25},{3389,3389},{110,110}, {445,445}, }; static struct Range top_ports_udp[] = { {161, 161}, {631, 631}, {137,138},{123,123},{1434},{445,445},{135,135}, {67,67}, }; static struct Range top_ports_sctp[] = { {7, 7},{9, 9},{20,22},{80,80},{179,179},{443,443},{1167,1167}, };*/ /*************************************************************************** ***************************************************************************/ void masscan_usage(void) { printf("usage: masscan [options] [... -pPORT[,PORT...]]\n"); printf("\n"); printf("examples:\n"); printf(" masscan -p80,8000-8100 10.0.0.0/8 --rate=10000\n"); printf(" scan some web ports on 10.x.x.x at 10kpps\n"); printf("\n"); printf(" masscan --nmap\n"); printf(" list those options that are compatible with nmap\n"); printf("\n"); printf(" masscan -p80 10.0.0.0/8 --banners -oB \n"); printf(" save results of scan in binary format to \n"); printf("\n"); printf(" masscan --open --banners --readscan -oX \n"); printf(" read binary scan results in and save them as xml in \n"); exit(1); } /*************************************************************************** ***************************************************************************/ static void print_version() { const char *cpu = "unknown"; const char *compiler = "unknown"; const char *compiler_version = "unknown"; const char *os = "unknown"; printf("\n"); printf("Masscan version %s ( %s )\n", MASSCAN_VERSION, "https://github.com/robertdavidgraham/masscan" ); printf("Compiled on: %s %s\n", __DATE__, __TIME__); #if defined(__x86_64) || defined(__x86_64__) cpu = "x86"; #endif #if defined(_MSC_VER) #if defined(_M_AMD64) || defined(_M_X64) cpu = "x86"; #elif defined(_M_IX86) cpu = "x86"; #elif defined (_M_ARM_FP) cpu = "arm"; #endif { int msc_ver = _MSC_VER; compiler = "VisualStudio"; if (msc_ver < 1500) compiler_version = "pre2008"; else if (msc_ver == 1500) compiler_version = "2008"; else if (msc_ver == 1600) compiler_version = "2010"; else if (msc_ver == 1700) compiler_version = "2012"; else if (msc_ver == 1800) compiler_version = "2013"; else compiler_version = "post-2013"; } #elif defined(__GNUC__) # if defined(__clang__) compiler = "clang"; # else compiler = "gcc"; # endif compiler_version = __VERSION__; #if defined(i386) || defined(__i386) || defined(__i386__) cpu = "x86"; #endif #if defined(__corei7) || defined(__corei7__) cpu = "x86-Corei7"; #endif #endif #if defined(WIN32) os = "Windows"; #elif defined(__linux__) os = "Linux"; #elif defined(__APPLE__) os = "Apple"; #elif defined(__MACH__) os = "MACH"; #elif defined(__FreeBSD__) os = "FreeBSD"; #elif defined(__NetBSD__) os = "NetBSD"; #elif defined(unix) || defined(__unix) || defined(__unix__) os = "Unix"; #endif printf("Compiler: %s %s\n", compiler, compiler_version); printf("OS: %s\n", os); printf("CPU: %s (%u bits)\n", cpu, (unsigned)(sizeof(void*))*8); #if defined(GIT) printf("GIT version: %s\n", GIT); #endif } /*************************************************************************** ***************************************************************************/ static void print_nmap_help(void) { printf("Masscan (https://github.com/robertdavidgraham/masscan)\n" "Usage: masscan [Options] -p{Target-Ports} {Target-IP-Ranges}\n" "TARGET SPECIFICATION:\n" " Can pass only IPv4/IPv6 address, CIDR networks, or ranges (non-nmap style)\n" " Ex: 10.0.0.0/8, 192.168.0.1, 10.0.0.1-10.0.0.254\n" " -iL : Input from list of hosts/networks\n" " --exclude : Exclude hosts/networks\n" " --excludefile : Exclude list from file\n" " --randomize-hosts: Randomize order of hosts (default)\n" "HOST DISCOVERY:\n" " -Pn: Treat all hosts as online (default)\n" " -n: Never do DNS resolution (default)\n" "SCAN TECHNIQUES:\n" " -sS: TCP SYN (always on, default)\n" "SERVICE/VERSION DETECTION:\n" " --banners: get the banners of the listening service if available. The\n" " default timeout for waiting to receive data is 30 seconds.\n" "PORT SPECIFICATION AND SCAN ORDER:\n" " -p : Only scan specified ports\n" " Ex: -p22; -p1-65535; -p 111,137,80,139,8080\n" "TIMING AND PERFORMANCE:\n" " --max-rate : Send packets no faster than per second\n" " --connection-timeout : time in seconds a TCP connection will\n" " timeout while waiting for banner data from a port.\n" "FIREWALL/IDS EVASION AND SPOOFING:\n" " -S/--source-ip : Spoof source address\n" " -e : Use specified interface\n" " -g/--source-port : Use given port number\n" " --ttl : Set IP time-to-live field\n" " --spoof-mac : Spoof your MAC address\n" "OUTPUT:\n" " --output-format : Sets output to binary/list/unicornscan/json/ndjson/grepable/xml\n" " --output-file : Write scan results to file. If --output-format is\n" " not given default is xml\n" " -oL/-oJ/-oD/-oG/-oB/-oX/-oU : Output scan in List/JSON/nDjson/Grepable/Binary/XML/Unicornscan format,\n" " respectively, to the given filename. Shortcut for\n" " --output-format --output-file \n" " -v: Increase verbosity level (use -vv or more for greater effect)\n" " -d: Increase debugging level (use -dd or more for greater effect)\n" " --open: Only show open (or possibly open) ports\n" " --packet-trace: Show all packets sent and received\n" " --iflist: Print host interfaces and routes (for debugging)\n" " --append-output: Append to rather than clobber specified output files\n" " --resume : Resume an aborted scan\n" "MISC:\n" " --send-eth: Send using raw ethernet frames (default)\n" " -V: Print version number\n" " -h: Print this help summary page.\n" "EXAMPLES:\n" " masscan -v -sS 192.168.0.0/16 10.0.0.0/8 -p 80\n" " masscan 23.0.0.0/0 -p80 --banners -output-format binary --output-filename internet.scan\n" " masscan --open --banners --readscan internet.scan -oG internet_scan.grepable\n" "SEE (https://github.com/robertdavidgraham/masscan) FOR MORE HELP\n" "\n"); } /*************************************************************************** ***************************************************************************/ static unsigned count_cidr6_bits(struct Range6 *range, bool *exact) { uint64_t i; /* for the comments of this function, see count_cidr_bits */ *exact = false; for (i=0; i<128; i++) { uint64_t mask_hi; uint64_t mask_lo; if (i < 64) { mask_hi = 0xFFFFFFFFffffffffull >> i; mask_lo = 0xFFFFFFFFffffffffull; } else { mask_hi = 0; mask_lo = 0xFFFFFFFFffffffffull >> (i - 64); } if ((range->begin.hi & mask_hi) != 0 || (range->begin.lo & mask_lo) != 0) { continue; } if ((range->begin.hi & ~mask_hi) == (range->end.hi & ~mask_hi) && (range->begin.lo & ~mask_lo) == (range->end.lo & ~mask_lo)) { if (((range->end.hi & mask_hi) == mask_hi) && ((range->end.lo & mask_lo) == mask_lo)) { *exact = true; return (unsigned) i; } } else { *exact = false; range->begin.hi = range->begin.hi + mask_hi; if (range->begin.lo >= 0xffffffffffffffff - 1 - mask_lo) { range->begin.hi += 1; } range->begin.lo = range->begin.lo + mask_lo + 1; return (unsigned) i; } } range->begin.lo = range->begin.lo + 1; if (range->begin.lo == 0) { range->begin.hi = range->begin.hi + 1; } return 128; } /*************************************************************************** * Echoes the configuration for one NIC ***************************************************************************/ static void masscan_echo_nic(struct Masscan *masscan, FILE *fp, unsigned i) { char idx_str[64]; /* If we have only one adapter, then don't print the array indexes. * Otherwise, we need to print the array indexes to distinguish * the NICs from each other */ if (masscan->nic_count <= 1) idx_str[0] = '\0'; else snprintf(idx_str, sizeof(idx_str), "[%u]", i); if (masscan->nic[i].ifname[0]) fprintf(fp, "adapter%s = %s\n", idx_str, masscan->nic[i].ifname); if (masscan->nic[i].src.ipv4.first != 0 || masscan->nic[i].src.ipv4.last != 0) { /** * FIX 495.1 for issue #495: Single adapter-ip is not saved at all * * The else case handles a simple invocation of one adapter-ip: * * 1. masscan ... --adapter-ip 1.2.3.1 ... [BROKEN] * * This looks like it was just copy pasta/typo. If the first ip is the same * as the last ip, it is a single adapter-ip * * This never worked as it was before so paused.conf would never save the * adapter-ip as it fell through this if/else if into nowhere. It probably * went undetected because in simple environments and/or in simple scans, * masscan is able to intelligently determine the adapter-ip and only * advanced usage requires overriding the chosen value. In addition to * that, it is probably relatively uncommon to interrupt a scan as not many * users are doing multi-hour / multi-day scans, having them paused and * then resuming them (apparently) */ if (masscan->nic[i].src.ipv4.first == masscan->nic[i].src.ipv4.last) { ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].src.ipv4.first); fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string); } /** * FIX 495.2 for issue #495: Ranges of size two don't print. When 495.1 is * added, ranges of size two print as only the first value in the range * Before 495.1, they didn't print at all, so this is not a bug that is * introduced by 495.1, just noticed while applying that fix * * The first if case here is for handling when adapter-ip is a range * * Examples of the multiple/range case: * * 1. masscan ... --adapter-ip 1.2.3.1-1.2.3.2 ... [BROKEN] * 2. masscan ... --adapter-ip 1.2.3.1-1.2.3.4 ... [OK] * * If the range spans exactly two adapter-ips, it will not hit the range * printing logic case here because of an off-by-one * * Changing it from < to <= fixes that issue and both of the above cases * now print the correct range as expected */ else if (masscan->nic[i].src.ipv4.first < masscan->nic[i].src.ipv4.last) { ipaddress_formatted_t fmt1 = ipv4address_fmt(masscan->nic[i].src.ipv4.first); ipaddress_formatted_t fmt2 = ipv4address_fmt(masscan->nic[i].src.ipv4.last); fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string); } } if (masscan->nic[i].src.ipv6.range) { if (ipv6address_is_lessthan(masscan->nic[i].src.ipv6.first, masscan->nic[i].src.ipv6.last)) { ipaddress_formatted_t fmt1 = ipv6address_fmt(masscan->nic[i].src.ipv6.first); ipaddress_formatted_t fmt2 = ipv6address_fmt(masscan->nic[i].src.ipv6.last); fprintf(fp, "adapter-ip%s = %s-%s\n", idx_str, fmt1.string, fmt2.string); } else { ipaddress_formatted_t fmt = ipv6address_fmt(masscan->nic[i].src.ipv6.first); fprintf(fp, "adapter-ip%s = %s\n", idx_str, fmt.string); } } if (masscan->nic[i].my_mac_count) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].source_mac); fprintf(fp, "adapter-mac%s = %s\n", idx_str, fmt.string); } if (masscan->nic[i].router_ip) { ipaddress_formatted_t fmt = ipv4address_fmt(masscan->nic[i].router_ip); fprintf(fp, "router-ip%s = %s\n", idx_str, fmt.string); } if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv4)) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv4); fprintf(fp, "router-mac-ipv4%s = %s\n", idx_str, fmt.string); } if (!macaddress_is_zero(masscan->nic[i].router_mac_ipv6)) { ipaddress_formatted_t fmt = macaddress_fmt(masscan->nic[i].router_mac_ipv6); fprintf(fp, "router-mac-ipv6%s = %s\n", idx_str, fmt.string); } } /*************************************************************************** ***************************************************************************/ void masscan_save_state(struct Masscan *masscan) { char filename[512]; FILE *fp; safe_strcpy(filename, sizeof(filename), "paused.conf"); fprintf(stderr, " " " \r"); fprintf(stderr, "saving resume file to: %s\n", filename); fp = fopen(filename, "wt"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: saving resume file\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); return; } masscan_echo(masscan, fp, 0); fclose(fp); } #if 0 /***************************************************************************** * Read in ranges from a file * * There can be multiple ranges on a line, delimited by spaces. In fact, * millions of ranges can be on a line: there is limit to the line length. * That makes reading the file a little bit squirrelly. From one perspective * this parser doesn't treat the new-line '\n' any different than other * space. But, from another perspective, it has to, because things like * comments are terminated by a newline. Also, it has to count the number * of lines correctly to print error messages. *****************************************************************************/ static void ranges_from_file(struct RangeList *ranges, const char *filename) { FILE *fp; unsigned line_number = 0; fp = fopen(filename, "rt"); if (fp) { perror(filename); exit(1); /* HARD EXIT: because if it's an exclusion file, we don't * want to continue. We don't want ANY chance of * accidentally scanning somebody */ } while (!feof(fp)) { int c = '\n'; /* remove leading whitespace */ while (!feof(fp)) { c = getc(fp); line_number += (c == '\n'); if (!isspace(c&0xFF)) break; } /* If this is a punctuation, like '#', then it's a comment */ if (ispunct(c&0xFF)) { while (!feof(fp)) { c = getc(fp); line_number += (c == '\n'); if (c == '\n') { break; } } /* Loop back to the begining state at the start of a line */ continue; } if (c == '\n') { continue; } /* * Read in a single entry */ if (!feof(fp)) { char address[64]; size_t i; struct Range range; unsigned offset = 0; /* Grab all bytes until the next space or comma */ address[0] = (char)c; i = 1; while (!feof(fp)) { c = getc(fp); if (c == EOF) break; line_number += (c == '\n'); if (isspace(c&0xFF) || c == ',') { break; } if (i+1 >= sizeof(address)) { LOG(0, "%s:%u:%u: bad address spec: \"%.*s\"\n", filename, line_number, offset, (int)i, address); exit(1); } else address[i] = (char)c; i++; } address[i] = '\0'; /* parse the address range */ range = range_parse_ipv4(address, &offset, (unsigned)i); if (range.begin == 0xFFFFFFFF && range.end == 0) { LOG(0, "%s:%u:%u: bad range spec: \"%.*s\"\n", filename, line_number, offset, (int)i, address); exit(1); } else { rangelist_add_range(ranges, range.begin, range.end); } } } fclose(fp); /* Target list must be sorted every time it's been changed, * before it can be used */ rangelist_sort(ranges); } #endif /*************************************************************************** ***************************************************************************/ static unsigned hexval(char c) { if ('0' <= c && c <= '9') return (unsigned)(c - '0'); if ('a' <= c && c <= 'f') return (unsigned)(c - 'a' + 10); if ('A' <= c && c <= 'F') return (unsigned)(c - 'A' + 10); return 0xFF; } /*************************************************************************** ***************************************************************************/ static int parse_mac_address(const char *text, macaddress_t *mac) { unsigned i; for (i=0; i<6; i++) { unsigned x; char c; while (isspace(*text & 0xFF) && ispunct(*text & 0xFF)) text++; c = *text; if (!isxdigit(c&0xFF)) return -1; x = hexval(c)<<4; text++; c = *text; if (!isxdigit(c&0xFF)) return -1; x |= hexval(c); text++; mac->addr[i] = (unsigned char)x; if (ispunct(*text & 0xFF)) text++; } return 0; } /*************************************************************************** ***************************************************************************/ static uint64_t parseInt(const char *str) { uint64_t result = 0; while (*str && isdigit(*str & 0xFF)) { result = result * 10 + (*str - '0'); str++; } return result; } /** * a stricter function for determining if something is boolean. */ static bool isBoolean(const char *str) { size_t length = str?strlen(str):0; if (length == 0) return false; /* "0" or "1" is boolean */ if (isdigit(str[0])) { if (strtoul(str,0,0) == 0) return true; else if (strtoul(str,0,0) == 1) return true; else return false; } switch (str[0]) { case 'e': case 'E': if (memcasecmp("enable", str, length)==0) return true; if (memcasecmp("enabled", str, length)==0) return true; return false; case 'd': case 'D': if (memcasecmp("disable", str, length)==0) return true; if (memcasecmp("disabled", str, length)==0) return true; return false; case 't': case 'T': if (memcasecmp("true", str, length)==0) return true; return false; case 'f': case 'F': if (memcasecmp("false", str, length)==0) return true; return false; case 'o': case 'O': if (memcasecmp("on", str, length)==0) return true; if (memcasecmp("off", str, length)==0) return true; return false; case 'Y': case 'y': if (memcasecmp("yes", str, length)==0) return true; return false; case 'n': case 'N': if (memcasecmp("no", str, length)==0) return true; return false; default: return false; } } static unsigned parseBoolean(const char *str) { if (str == NULL || str[0] == 0) return 1; if (isdigit(str[0])) { if (strtoul(str,0,0) == 0) return 0; else return 1; } switch (str[0]) { case 'e': /* enable */ case 'E': return 1; case 'd': /* disable */ case 'D': return 0; case 't': /* true */ case 'T': return 1; case 'f': /* false */ case 'F': return 0; case 'o': /* on or off */ case 'O': if (str[1] == 'f' || str[1] == 'F') return 0; else return 1; break; case 'Y': /* yes */ case 'y': return 1; case 'n': /* no */ case 'N': return 0; } return 1; } /*************************************************************************** * Parses the number of seconds (for rotating files mostly). We do a little * more than just parse an integer. We support strings like: * * hourly * daily * Week * 5days * 10-months * 3600 ***************************************************************************/ static uint64_t parseTime(const char *value) { uint64_t num = 0; unsigned is_negative = 0; while (*value == '-') { is_negative = 1; value++; } while (isdigit(value[0]&0xFF)) { num = num*10 + (value[0] - '0'); value++; } while (ispunct(value[0]) || isspace(value[0])) value++; if (isalpha(value[0]) && num == 0) num = 1; if (value[0] == '\0') return num; switch (tolower(value[0])) { case 's': num *= 1; break; case 'm': num *= 60; break; case 'h': num *= 60*60; break; case 'd': num *= 24*60*60; break; case 'w': num *= 24*60*60*7; break; default: fprintf(stderr, "--rotate-offset: unknown character\n"); exit(1); } if (num >= 24*60*60) { fprintf(stderr, "--rotate-offset: value is greater than 1 day\n"); exit(1); } if (is_negative) num = 24*60*60 - num; return num; } /*************************************************************************** * Parses a size integer, which can be suffixed with "tera", "giga", * "mega", and "kilo". These numbers are in units of 1024 so suck it. ***************************************************************************/ static uint64_t parseSize(const char *value) { uint64_t num = 0; while (isdigit(value[0]&0xFF)) { num = num*10 + (value[0] - '0'); value++; } while (ispunct(value[0]) || isspace(value[0])) value++; if (isalpha(value[0]) && num == 0) num = 1; if (value[0] == '\0') return num; switch (tolower(value[0])) { case 'k': /* kilobyte */ num *= 1024ULL; break; case 'm': /* megabyte */ num *= 1024ULL * 1024ULL; break; case 'g': /* gigabyte */ num *= 1024ULL * 1024ULL * 1024ULL; break; case 't': /* terabyte, 'cause we roll that way */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; case 'p': /* petabyte, 'cause we are awesome */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; case 'e': /* exabyte, now that's just silly */ num *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break; default: fprintf(stderr, "--rotate-size: unknown character\n"); exit(1); } return num; } /*************************************************************************** ***************************************************************************/ static int is_power_of_two(uint64_t x) { while ((x&1) == 0) x >>= 1; return x == 1; } /*************************************************************************** * Tests if the named parameter on the command-line. We do a little * more than a straight string compare, because I get confused * whether parameter have punctuation. Is it "--excludefile" or * "--exclude-file"? I don't know if it's got that dash. Screw it, * I'll just make the code so it don't care. ***************************************************************************/ static int EQUALS(const char *lhs, const char *rhs) { for (;;) { while (*lhs == '-' || *lhs == '.' || *lhs == '_') lhs++; while (*rhs == '-' || *rhs == '.' || *rhs == '_') rhs++; if (*lhs == '\0' && *rhs == '[') return 1; /*arrays*/ if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF)) return 0; if (*lhs == '\0') return 1; lhs++; rhs++; } } static int EQUALSx(const char *lhs, const char *rhs, size_t rhs_length) { for (;;) { while (*lhs == '-' || *lhs == '.' || *lhs == '_') lhs++; while (*rhs == '-' || *rhs == '.' || *rhs == '_') rhs++; if (*lhs == '\0' && *rhs == '[') return 1; /*arrays*/ if (tolower(*lhs & 0xFF) != tolower(*rhs & 0xFF)) return 0; if (*lhs == '\0') return 1; lhs++; rhs++; if (--rhs_length == 0) return 1; } } static unsigned INDEX_OF(const char *str, char c) { unsigned i; for (i=0; str[i] && str[i] != c; i++) ; return i; } static unsigned ARRAY(const char *rhs) { const char *p = strchr(rhs, '['); if (p == NULL) return 0; else p++; return (unsigned)parseInt(p); } /** * Called if user specified `--top-ports` on the command-line. */ static void config_top_ports(struct Masscan *masscan, unsigned maxports) { unsigned i; static const unsigned short top_udp_ports[] = { 161, /* SNMP - should be found on all network equipment */ 135, /* MS-RPC - should be found on all modern Windows */ 500, /* ISAKMP - for establishing IPsec tunnels */ 137, /* NetBIOS-NameService - should be found on old Windows */ 138, /* NetBIOS-Datagram - should be found on old Windows */ 445, /* SMB datagram service */ 67, /* DHCP */ 53, /* DNS */ 1900, /* UPnP - Microsoft-focused local discovery */ 5353, /* mDNS - Apple-focused local discovery */ 4500, /* nat-t-ike - IPsec NAT traversal */ 514, /* syslog - all Unix machiens */ 69, /* TFTP */ 49152, /* first of modern ephemeral ports */ 631, /* IPP - printing protocol for Linux */ 123, /* NTP network time protocol */ 1434, /* MS-SQL server*/ 520, /* RIP - routers use this protocol sometimes */ 7, /* Echo */ 111, /* SunRPC portmapper */ 2049, /* SunRPC NFS */ 5683, /* COAP */ 11211, /* memcached */ 1701, /* L2TP */ 27960, /* quaked amplifier */ 1645, /* RADIUS */ 1812, /* RADIUS */ 1646, /* RADIUS */ 1813, /* RADIUS */ 3343, /* Microsoft Cluster Services */ 2535, /* MADCAP rfc2730 TODO FIXME */ }; static const unsigned short top_tcp_ports[] = { 80, 443, 8080, /* also web */ 21, 990, /* FTP, oldie but goodie */ 22, /* SSH, so much infrastructure */ 23, 992, /* Telnet, oldie but still around*/ 24, /* people put things here instead of TelnetSSH*/ 25, 465, 587, 2525, /* SMTP email*/ 5800, 5900, 5901, /* VNC */ 111, /* SunRPC */ 139, 445, /* Microsoft Windows networking */ 135, /* DCEPRC, more Microsoft Windows */ 3389, /* Microsoft Windows RDP */ 88, /* Kerberos, also Microsoft windows */ 389, 636, /* LDAP and MS Win */ 1433, /* MS SQL */ 53, /* DNS */ 2083, 2096, /* cPanel */ 9050, /* ToR */ 8140, /* Puppet */ 11211, /* memcached */ 1098, 1099, /* Java RMI */ 6000, 6001, /* XWindows */ 5060, 5061, /* SIP - session initiation protocool */ 554, /* RTSP */ 548, /* AFP */ 1,3,4,6,7,9,13,17,19,20,26,30,32,33,37,42,43,49,70, 79,81,82,83,84,85,89,90,99,100,106,109,110,113,119,125, 143,144,146,161,163,179,199,211,212,222,254,255,256,259,264,280, 301,306,311,340,366,406,407,416,417,425,427,444,458,464, 465,481,497,500,512,513,514,515,524,541,543,544,545,554,555,563, 593,616,617,625,631,646,648,666,667,668,683,687,691,700,705, 711,714,720,722,726,749,765,777,783,787,800,801,808,843,873,880,888, 898,900,901,902,903,911,912,981,987,993,995,999,1000,1001, 1002,1007,1009,1010,1011,1021,1022,1023,1024,1025,1026,1027,1028, 1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041, 1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054, 1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067, 1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080, 1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093, 1094,1095,1096,1097,1100,1102,1104,1105,1106,1107,1108, 1110,1111,1112,1113,1114,1117,1119,1121,1122,1123,1124,1126,1130, 1131,1132,1137,1138,1141,1145,1147,1148,1149,1151,1152,1154,1163, 1164,1165,1166,1169,1174,1175,1183,1185,1186,1187,1192,1198,1199, 1201,1213,1216,1217,1218,1233,1234,1236,1244,1247,1248,1259,1271, 1272,1277,1287,1296,1300,1301,1309,1310,1311,1322,1328,1334,1352, 1417,1434,1443,1455,1461,1494,1500,1501,1503,1521,1524,1533, 1556,1580,1583,1594,1600,1641,1658,1666,1687,1688,1700,1717,1718, 1719,1720,1721,1723,1755,1761,1782,1783,1801,1805,1812,1839,1840, 1862,1863,1864,1875,1900,1914,1935,1947,1971,1972,1974,1984,1998, 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2013, 2020,2021,2022,2030,2033,2034,2035,2038,2040,2041,2042,2043,2045, 2046,2047,2048,2049,2065,2068,2099,2100,2103,2105,2106,2107,2111, 2119,2121,2126,2135,2144,2160,2161,2170,2179,2190,2191,2196,2200, 2222,2251,2260,2288,2301,2323,2366,2381,2382,2383,2393,2394,2399, 2401,2492,2500,2522,2557,2601,2602,2604,2605,2607,2608,2638, 2701,2702,2710,2717,2718,2725,2800,2809,2811,2869,2875,2909,2910, 2920,2967,2968,2998,3000,3001,3003,3005,3006,3007,3011,3013,3017, 3030,3031,3052,3071,3077,3128,3168,3211,3221,3260,3261,3268,3269, 3283,3300,3301,3306,3322,3323,3324,3325,3333,3351,3367,3369,3370, 3371,3372,3389,3390,3404,3476,3493,3517,3527,3546,3551,3580,3659, 3689,3690,3703,3737,3766,3784,3800,3801,3809,3814,3826,3827,3828, 3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986, 3995,3998,4000,4001,4002,4003,4004,4005,4006,4045,4111,4125,4126, 4129,4224,4242,4279,4321,4343,4443,4444,4445,4446,4449,4550,4567, 4662,4848,4899,4900,4998,5000,5001,5002,5003,5004,5009,5030,5033, 5050,5051,5054,5080,5087,5100,5101,5102,5120,5190,5200, 5214,5221,5222,5225,5226,5269,5280,5298,5357,5405,5414,5431,5432, 5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678,5679, 5718,5730,5801,5802,5810,5811,5815,5822,5825,5850,5859,5862, 5877,5902,5903,5904,5906,5907,5910,5911,5915,5922,5925, 5950,5952,5959,5960,5961,5962,5963,5987,5988,5989,5998,5999, 6002,6003,6004,6005,6006,6007,6009,6025,6059,6100,6101,6106, 6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565,6566,6567, 6580,6646,6666,6667,6668,6669,6689,6692,6699,6779,6788,6789,6792, 6839,6881,6901,6969,7000,7001,7002,7004,7007,7019,7025,7070,7100, 7103,7106,7200,7201,7402,7435,7443,7496,7512,7625,7627,7676,7741, 7777,7778,7800,7911,7920,7921,7937,7938,7999,8000,8001,8002,8007, 8008,8009,8010,8011,8021,8022,8031,8042,8045,8080,8081,8082,8083, 8084,8085,8086,8087,8088,8089,8090,8093,8099,8100,8180,8181,8192, 8193,8194,8200,8222,8254,8290,8291,8292,8300,8333,8383,8400,8402, 8443,8500,8600,8649,8651,8652,8654,8701,8800,8873,8888,8899,8994, 9000,9001,9002,9003,9009,9010,9011,9040,9071,9080,9081,9090, 9091,9099,9100,9101,9102,9103,9110,9111,9200,9207,9220,9290,9415, 9418,9485,9500,9502,9503,9535,9575,9593,9594,9595,9618,9666,9876, 9877,9878,9898,9900,9917,9929,9943,9944,9968,9998,9999,10000,10001, 10002,10003,10004,10009,10010,10012,10024,10025,10082,10180,10215, 10243,10566,10616,10617,10621,10626,10628,10629,10778,11110,11111, 11967,12000,12174,12265,12345,13456,13722,13782,13783,14000,14238, 14441,14442,15000,15002,15003,15004,15660,15742,16000,16001,16012, 16016,16018,16080,16113,16992,16993,17877,17988,18040,18101,18988, 19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221, 20222,20828,21571,22939,23502,24444,24800,25734,25735,26214,27000, 27352,27353,27355,27356,27715,28201,30000,30718,30951,31038,31337, 32768,32769,32770,32771,32772,32773,32774,32775,32776,32777,32778, 32779,32780,32781,32782,32783,32784,32785,33354,33899,34571,34572, 34573,35500,38292,40193,40911,41511,42510,44176,44442,44443,44501, 45100,48080,49152,49153,49154,49155,49156,49157,49158,49159,49160, 49161,49163,49165,49167,49175,49176,49400,49999,50000,50001,50002, 50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822, 52848,52869,54045,54328,55055,55056,55555,55600,56737,56738,57294, 57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000, 65129,65389}; struct RangeList *ports = &masscan->targets.ports; static const unsigned max_tcp_ports = sizeof(top_tcp_ports)/sizeof(top_tcp_ports[0]); static const unsigned max_udp_ports = sizeof(top_udp_ports)/sizeof(top_udp_ports[0]); if (masscan->scan_type.tcp) { LOG(2, "[+] adding TCP top-ports = %u\n", maxports); for (i=0; iscan_type.udp) { LOG(2, "[+] adding UDP top-ports = %u\n", maxports); for (i=0; iecho) { 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->targets.ports, range.begin, range.end); rangelist_sort(&masscan->targets.ports); 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) { UNUSEDPARM(name); 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_banners_rawudp(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_banners_rawudp || masscan->echo_all) fprintf(masscan->echo, "rawudp = %s\n", masscan->is_banners_rawudp?"true":"false"); return 0; } masscan->is_banners_rawudp = parseBoolean(value); if (masscan->is_banners_rawudp) masscan->is_banners = true; return CONF_OK; } static int SET_capture(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { if (!masscan->is_capture_cert || masscan->echo_all) fprintf(masscan->echo, "%scapture = cert\n", masscan->is_capture_cert?"":"no"); if (!masscan->is_capture_servername || masscan->echo_all) fprintf(masscan->echo, "%scapture = servername\n", masscan->is_capture_servername?"":"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("servername", value)) masscan->is_capture_servername = 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("servername", value)) masscan->is_capture_servername = 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) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_hello_ssl) { fprintf(masscan->echo, "hello = ssl\n"); } else if (masscan->is_hello_smbv1) { fprintf(masscan->echo, "hello = smbv1\n"); } else if (masscan->is_hello_http) { fprintf(masscan->echo, "hello = http\n"); } return 0; } if (EQUALS("ssl", value)) masscan->is_hello_ssl = 1; else if (EQUALS("smbv1", value)) masscan->is_hello_smbv1 = 1; else if (EQUALS("http", value)) masscan->is_hello_http = 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; 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 */ fp = fopen(value, "rb"); if (fp == NULL) { LOG(0, "[-] [FAILED] --hello-file\n"); LOG(0, "[-] %s: %s\n", value, strerror(errno)); 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'; snprintf(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->payloads.tcp; 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 = STRDUP(value); pay = MALLOC(sizeof(*pay)); pay->payload_base64 = value2; pay->port = index; pay->next = masscan->payloads.tcp; masscan->payloads.tcp = pay; return CONF_OK; } static int SET_hello_timeout(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); 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_http_cookie(struct Masscan *masscan, const char *name, const char *value) { unsigned char *newvalue; size_t value_length; UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.cookies_count || masscan->echo_all) { size_t i; for (i=0; ihttp.cookies_count; i++) { fprintf(masscan->echo, "http-cookie = %.*s\n", (unsigned)masscan->http.cookies[i].value_length, masscan->http.cookies[i].value); } } return 0; } /* allocate new value */ value_length = strlen(value); newvalue = MALLOC(value_length+1); memcpy(newvalue, value, value_length+1); newvalue[value_length] = '\0'; /* Add to our list of headers */ if (masscan->http.cookies_count < sizeof(masscan->http.cookies)/sizeof(masscan->http.cookies[0])) { size_t x = masscan->http.cookies_count; masscan->http.cookies[x].value = newvalue; masscan->http.cookies[x].value_length = value_length; masscan->http.cookies_count++; } return CONF_OK; } static int SET_http_header(struct Masscan *masscan, const char *name, const char *value) { unsigned name_length; char *newname; unsigned char *newvalue; size_t value_length; if (masscan->echo) { if (masscan->http.headers_count || masscan->echo_all) { size_t i; for (i=0; ihttp.headers_count; i++) { if (masscan->http.headers[i].name == 0) continue; fprintf(masscan->echo, "http-header = %s:%.*s\n", masscan->http.headers[i].name, (unsigned)masscan->http.headers[i].value_length, masscan->http.headers[i].value); } } return 0; } /* * allocate a new name */ name += 11; if (*name == '[') { /* Specified as: "--http-header[name] value" */ while (ispunct(*name)) name++; name_length = (unsigned)strlen(name); while (name_length && ispunct(name[name_length-1])) name_length--; newname = MALLOC(name_length+1); memcpy(newname, name, name_length+1); newname[name_length] = '\0'; } else if (strchr(value, ':')) { /* Specified as: "--http-header Name:value" */ name_length = INDEX_OF(value, ':'); newname = MALLOC(name_length + 1); memcpy(newname, value, name_length + 1); /* Trim the value */ value = value + name_length + 1; while (*value && isspace(*value & 0xFF)) value++; /* Trim the name */ while (name_length && isspace(newname[name_length-1]&0xFF)) name_length--; newname[name_length] = '\0'; } else { fprintf(stderr, "[-] --http-header needs both a name and value\n"); fprintf(stderr, " hint: \"--http-header Name:value\"\n"); exit(1); } /* allocate new value */ value_length = strlen(value); newvalue = MALLOC(value_length+1); memcpy(newvalue, value, value_length+1); newvalue[value_length] = '\0'; /* Add to our list of headers */ if (masscan->http.headers_count < sizeof(masscan->http.headers)/sizeof(masscan->http.headers[0])) { size_t x = masscan->http.headers_count; masscan->http.headers[x].name = newname; masscan->http.headers[x].value = newvalue; masscan->http.headers[x].value_length = value_length; masscan->http.headers_count++; } return CONF_OK; } static int SET_http_method(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.method || masscan->echo_all) fprintf(masscan->echo, "http-method = %.*s\n", (unsigned)masscan->http.method_length, masscan->http.method); return 0; } if (masscan->http.method) free(masscan->http.method); masscan->http.method_length = strlen(value); masscan->http.method = MALLOC(masscan->http.method_length+1); memcpy(masscan->http.method, value, masscan->http.method_length+1); return CONF_OK; } static int SET_http_url(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.url || masscan->echo_all) fprintf(masscan->echo, "http-url = %.*s\n", (unsigned)masscan->http.url_length, masscan->http.url); return 0; } if (masscan->http.url) free(masscan->http.url); masscan->http.url_length = strlen(value); masscan->http.url = MALLOC(masscan->http.url_length+1); memcpy(masscan->http.url, value, masscan->http.url_length+1); return CONF_OK; } static int SET_http_version(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.version || masscan->echo_all) fprintf(masscan->echo, "http-version = %.*s\n", (unsigned)masscan->http.version_length, masscan->http.version); return 0; } if (masscan->http.version) free(masscan->http.version); masscan->http.version_length = strlen(value); masscan->http.version = MALLOC(masscan->http.version_length+1); memcpy(masscan->http.version, value, masscan->http.version_length+1); return CONF_OK; } static int SET_http_host(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.host || masscan->echo_all) fprintf(masscan->echo, "http-host = %.*s\n", (unsigned)masscan->http.host_length, masscan->http.host); return 0; } if (masscan->http.host) free(masscan->http.host); masscan->http.host_length = strlen(value); masscan->http.host = MALLOC(masscan->http.host_length+1); memcpy(masscan->http.host, value, masscan->http.host_length+1); return CONF_OK; } static int SET_http_user_agent(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.user_agent || masscan->echo_all) fprintf(masscan->echo, "http-user-agent = %.*s\n", (unsigned)masscan->http.user_agent_length, masscan->http.user_agent); return 0; } if (masscan->http.user_agent) free(masscan->http.user_agent); masscan->http.user_agent_length = strlen(value); masscan->http.user_agent = MALLOC(masscan->http.user_agent_length+1); memcpy( masscan->http.user_agent, value, masscan->http.user_agent_length+1 ); return CONF_OK; } static int SET_http_payload(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->http.payload || masscan->echo_all) fprintf(masscan->echo, "http-payload = %.*s\n", (unsigned)masscan->http.payload_length, masscan->http.payload); return 0; } masscan->http.payload_length = strlen(value); masscan->http.payload = REALLOC(masscan->http.payload, masscan->http.payload_length+1); memcpy( masscan->http.payload, value, masscan->http.payload_length+1 ); return CONF_OK; } static int SET_status_ndjson(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.is_status_ndjson || masscan->echo_all) fprintf(masscan->echo, "ndjson-status = %s\n", masscan->output.is_status_ndjson?"true":"false"); return 0; } masscan->output.is_status_ndjson = parseBoolean(value); return CONF_OK; } static int SET_status_json(struct Masscan *masscan, const char *name, const char *value) { /* NOTE: this is here just to warn people they mistyped it */ UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { return 0; } fprintf(stderr, "[-] FAIL: %s not supported, use --status-ndjson\n", name); fprintf(stderr, " hint: new-line delimited JSON status is what we use\n"); return CONF_ERR; } static int SET_min_packet(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); 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_nmap_payloads(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.nmap_payloads_filename && masscan->payloads.nmap_payloads_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "nmap-payloads = %s\n", masscan->payloads.nmap_payloads_filename); return 0; } if (masscan->payloads.nmap_payloads_filename) free(masscan->payloads.nmap_payloads_filename); masscan->payloads.nmap_payloads_filename = strdup(value); return CONF_OK; } static int SET_nmap_service_probes(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.nmap_service_probes_filename && masscan->payloads.nmap_service_probes_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "nmap-service-probes = %s\n", masscan->payloads.nmap_service_probes_filename); return 0; } if (masscan->payloads.nmap_service_probes_filename) free(masscan->payloads.nmap_service_probes_filename); masscan->payloads.nmap_service_probes_filename = strdup(value); return CONF_OK; } static int SET_offline(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->is_offline || masscan->echo_all) fprintf(masscan->echo, "offline = %s\n", masscan->is_offline?"true":"false"); return 0; } masscan->is_offline = 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) { UNUSEDPARM(name); 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?*/ safe_strcpy(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; UNUSEDPARM(name); if (masscan->echo) { FILE *fp = masscan->echo; ipaddress_formatted_t fmt; 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_Hostonly: fprintf(fp, "output-format = hostonly\n"); break; case Output_Redis: fmt = ipaddress_fmt(masscan->redis.ip); fprintf(fp, "output-format = redis\n"); fprintf(fp, "redis = %s %u\n", fmt.string, 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 if (EQUALS("hostonly", value)) x = Output_Hostonly; 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) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); UNUSEDPARM(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; } /* Specifies a 'libpcap' file where the received packets will be written. * This is useful while debugging so that we can see what exactly is * going on. It's also an alternate mode for getting output from this * program. Instead of relying upon this program's determination of what * ports are open or closed, you can instead simply parse this capture * file yourself and make your own determination */ static int SET_pcap_filename(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->pcap_filename[0]) fprintf(masscan->echo, "pcap-filename = %s\n", masscan->pcap_filename); return 0; } if (value) safe_strcpy(masscan->pcap_filename, sizeof(masscan->pcap_filename), value); return CONF_OK; } /* Specifies a 'libpcap' file from which to read packet-payloads. The payloads found * in this file will serve as the template for spewing out custom packets. There are * other options that can set payloads as well, like "--nmap-payloads" for reading * their custom payload file, as well as the various "hello" options for specifying * the string sent to the server once a TCP connection has been established. */ static int SET_pcap_payloads(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->payloads.pcap_payloads_filename && masscan->payloads.pcap_payloads_filename[0]) || masscan->echo_all) fprintf(masscan->echo, "pcap-payloads = %s\n", masscan->payloads.pcap_payloads_filename); return 0; } if (masscan->payloads.pcap_payloads_filename) free(masscan->payloads.pcap_payloads_filename); masscan->payloads.pcap_payloads_filename = strdup(value); /* file will be loaded in "masscan_load_database_files()" */ return CONF_OK; } static int SET_randomize_hosts(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); UNUSEDPARM(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) { if ((unsigned)(masscan->max_rate * 100000) % 100000) { /* print as floating point number, which is rare */ fprintf(masscan->echo, "rate = %f\n", masscan->max_rate); } else { /* pretty print as just an integer, which is what most people * expect */ fprintf(masscan->echo, "rate = %-10.0f\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) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); 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; UNUSEDPARM(name); 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=: 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) { UNUSEDPARM(name); 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; UNUSEDPARM(name); 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; } safe_strcpy( 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] == '\\')) /* Fix for #561 */ p[strlen(p)-1] = '\0'; return CONF_OK; } static int SET_rotate_offset(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); /* 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) { UNUSEDPARM(name); 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_script(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if ((masscan->scripting.name && masscan->scripting.name[0]) || masscan->echo_all) fprintf(masscan->echo, "script = %s\n", masscan->scripting.name); return 0; } if (value && value[0]) masscan->is_scripting = 1; else masscan->is_scripting = 0; if (masscan->scripting.name) free(masscan->scripting.name); masscan->scripting.name = strdup(value); return CONF_OK; } static int SET_seed(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); 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) { UNUSEDPARM(name); UNUSEDPARM(value); if (masscan->echo) { fprintf(masscan->echo, "\n"); return 0; } return CONF_OK; } static int SET_shard(struct Masscan *masscan, const char *name, const char *value) { unsigned one = 0; unsigned of = 0; UNUSEDPARM(name); if (masscan->echo) { if (masscan->shard.of > 0 || 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; } static int SET_output_stylesheet(struct Masscan *masscan, const char *name, const char *value) { UNUSEDPARM(name); if (masscan->echo) { if (masscan->output.stylesheet[0] || masscan->echo_all) fprintf(masscan->echo, "stylesheet = %s\n", masscan->output.stylesheet); return 0; } if (masscan->output.format == 0) masscan->output.format = Output_XML; safe_strcpy(masscan->output.stylesheet, sizeof(masscan->output.stylesheet), value); return CONF_OK; } static int SET_topports(struct Masscan *masscan, const char *name, const char *value) { unsigned default_value = 20; if (masscan->echo) { /* don't echo: this instead triggers filling the `--port` * list, so the ports themselves will be echoed, not this * parameter */ return 0; } if (value == 0 || value[0] == '\0') { /* can be specified by itself on the command-line, alone * without a following parameter */ /* ex: `--top-ports` */ masscan->top_ports = default_value; } else if (isBoolean(value)) { /* ex: `--top-ports enable` */ if (parseBoolean(value)) masscan->top_ports = default_value; else masscan->top_ports = 0; } else if (isInteger(value)) { /* ex: `--top-ports 5` */ uint64_t num = parseInt(value); masscan->top_ports = (unsigned)num; } else { fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } return CONF_OK; } static int SET_tcp_mss(struct Masscan *masscan, const char *name, const char *value) { /* h/t @IvreRocks */ static const unsigned default_mss = 1460; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_mss) { case Default: break; case Add: if (masscan->templ_opts->tcp.mss == default_mss) fprintf(masscan->echo, "tcp-mss = %s\n", "enable"); else fprintf(masscan->echo, "tcp-mss = %u\n", masscan->templ_opts->tcp.mss); break; case Remove: fprintf(masscan->echo, "tcp-mss = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { /* no following parameter, so interpret this to mean "enable" */ masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = default_mss; /* 1460 */ } else if (isBoolean(value)) { /* looking for "enable" or "disable", but any boolean works, * like "true/false" or "off/on" */ if (parseBoolean(value)) { masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = default_mss; /* 1460 */ } else masscan->templ_opts->tcp.is_mss = Remove; } else if (isInteger(value)) { /* A specific number was specified */ uint64_t num = parseInt(value); if (num >= 0x10000) goto fail; masscan->templ_opts->tcp.is_mss = Add; masscan->templ_opts->tcp.mss = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_wscale(struct Masscan *masscan, const char *name, const char *value) { static const unsigned default_value = 3; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_wscale) { case Default: break; case Add: if (masscan->templ_opts->tcp.wscale == default_value) fprintf(masscan->echo, "tcp-wscale = %s\n", "enable"); else fprintf(masscan->echo, "tcp-wscale = %u\n", masscan->templ_opts->tcp.wscale); break; case Remove: fprintf(masscan->echo, "tcp-wscale = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = default_value; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = default_value; } else masscan->templ_opts->tcp.is_wscale = Remove; } else if (isInteger(value)) { uint64_t num = parseInt(value); if (num >= 255) goto fail; masscan->templ_opts->tcp.is_wscale = Add; masscan->templ_opts->tcp.wscale = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_tsecho(struct Masscan *masscan, const char *name, const char *value) { static const unsigned default_value = 0x12345678; if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_tsecho) { case Default: break; case Add: if (masscan->templ_opts->tcp.tsecho == default_value) fprintf(masscan->echo, "tcp-tsecho = %s\n", "enable"); else fprintf(masscan->echo, "tcp-tsecho = %u\n", masscan->templ_opts->tcp.tsecho); break; case Remove: fprintf(masscan->echo, "tcp-tsecho = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = default_value; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = default_value; } else masscan->templ_opts->tcp.is_tsecho = Remove; } else if (isInteger(value)) { uint64_t num = parseInt(value); if (num >= 255) goto fail; masscan->templ_opts->tcp.is_tsecho = Add; masscan->templ_opts->tcp.tsecho = (unsigned)num; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_tcp_sackok(struct Masscan *masscan, const char *name, const char *value) { if (masscan->echo) { if (masscan->templ_opts) { switch (masscan->templ_opts->tcp.is_sackok) { case Default: break; case Add: fprintf(masscan->echo, "tcp-sackok = %s\n", "enable"); break; case Remove: fprintf(masscan->echo, "tcp-sackok = %s\n", "disable"); break; default: break; } } return 0; } if (masscan->templ_opts == NULL) masscan->templ_opts = calloc(1, sizeof(*masscan->templ_opts)); if (value == 0 || value[0] == '\0') { masscan->templ_opts->tcp.is_sackok = Add; } else if (isBoolean(value)) { if (parseBoolean(value)) { masscan->templ_opts->tcp.is_sackok = Add; } else masscan->templ_opts->tcp.is_sackok = Remove; } else if (isInteger(value)) { if (parseInt(value) != 0) masscan->templ_opts->tcp.is_sackok = Add; } else goto fail; return CONF_OK; fail: fprintf(stderr, "[-] %s: bad value: %s\n", name, value); return CONF_ERR; } static int SET_debug_tcp(struct Masscan *masscan, const char *name, const char *value) { extern int is_tcp_debug; /* global */ UNUSEDPARM(name); UNUSEDPARM(masscan); if (value == 0 || value[0] == '\0') is_tcp_debug = 1; else is_tcp_debug = parseBoolean(value); return CONF_OK; } struct ConfigParameter { const char *name; SET_PARAMETER set; unsigned flags; const char *alts[6]; }; enum {F_NONE, F_BOOL=1, F_NUMABLE=2}; struct ConfigParameter config_parameters[] = { {"resume-index", SET_resume_index, 0, {0}}, {"resume-count", SET_resume_count, 0, {0}}, {"seed", SET_seed, 0, {0}}, {"arpscan", SET_arpscan, F_BOOL, {"arp",0}}, {"randomize-hosts", SET_randomize_hosts, F_BOOL, {0}}, {"rate", SET_rate, 0, {"max-rate",0}}, {"shard", SET_shard, 0, {"shards",0}}, {"banners", SET_banners, F_BOOL, {"banner",0}}, /* --banners */ {"rawudp", SET_banners_rawudp, F_BOOL, {"rawudp",0}}, /* --rawudp */ {"nobanners", SET_nobanners, F_BOOL, {"nobanner",0}}, {"retries", SET_retries, 0, {"retry", "max-retries", "max-retry", 0}}, {"noreset", SET_noreset, F_BOOL, {0}}, {"nmap-payloads", SET_nmap_payloads, 0, {"nmap-payload",0}}, {"nmap-service-probes",SET_nmap_service_probes, 0, {"nmap-service-probe",0}}, {"offline", SET_offline, F_BOOL, {"notransmit", "nosend", "dry-run", 0}}, {"pcap-filename", SET_pcap_filename, 0, {"pcap",0}}, {"pcap-payloads", SET_pcap_payloads, 0, {"pcap-payload",0}}, {"hello", SET_hello, 0, {0}}, {"hello-file", SET_hello_file, 0, {"hello-filename",0}}, {"hello-string", SET_hello_string, 0, {0}}, {"hello-timeout", SET_hello_timeout, 0, {0}}, {"http-cookie", SET_http_cookie, 0, {0}}, {"http-header", SET_http_header, 0, {"http-field", 0}}, {"http-method", SET_http_method, 0, {0}}, {"http-version", SET_http_version, 0, {0}}, {"http-url", SET_http_url, 0, {"http-uri",0}}, {"http-user-agent", SET_http_user_agent, 0, {"http-useragent",0}}, {"http-host", SET_http_host, 0, {0}}, {"http-payload", SET_http_payload, 0, {0}}, {"ndjson-status", SET_status_ndjson, F_BOOL, {"status-ndjson", 0}}, {"json-status", SET_status_json, F_BOOL, {"status-json", 0}}, {"min-packet", SET_min_packet, 0, {"min-pkt",0}}, {"capture", SET_capture, 0, {0}}, {"nocapture", SET_capture, 0, {"no-capture", 0}}, {"SPACE", SET_space, 0, {0}}, {"output-filename", SET_output_filename, 0, {"output-file",0}}, {"output-format", SET_output_format, 0, {0}}, {"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}}, {"stylesheet", SET_output_stylesheet, 0, {0}}, {"script", SET_script, 0, {0}}, {"SPACE", SET_space, 0, {0}}, {"tcp-mss", SET_tcp_mss, F_NUMABLE, {"tcpmss",0}}, {"tcp-wscale", SET_tcp_wscale, F_NUMABLE, {0}}, {"tcp-tsecho", SET_tcp_tsecho, F_NUMABLE, {0}}, {"tcp-sackok", SET_tcp_sackok, F_BOOL, {0}}, {"top-ports", SET_topports, F_NUMABLE, {"top-port",0}}, {"debug-tcp", SET_debug_tcp, F_BOOL, {"tcp-debug", 0}}, {0} }; /*************************************************************************** * Called either from the "command-line" parser when it sees a --param, * or from the "config-file" parser for normal options. ***************************************************************************/ void masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value) { unsigned index = ARRAY(name); if (index >= 65536) { 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)) { if (masscan->nic[index].ifname[0]) { fprintf(stderr, "CONF: overwriting \"adapter=%s\"\n", masscan->nic[index].ifname); } if (masscan->nic_count < index + 1) masscan->nic_count = index + 1; snprintf( masscan->nic[index].ifname, sizeof(masscan->nic[index].ifname), "%s", value); } else if (EQUALS("adapter-ip", name) || EQUALS("source-ip", name) || EQUALS("source-address", name) || EQUALS("spoof-ip", name) || EQUALS("spoof-address", name) || EQUALS("src-ip", name)) { /* Send packets FROM this IP address */ struct Range range; struct Range6 range6; int err; /* Grab the next IPv4 or IPv6 range */ err = massip_parse_range(value, 0, 0, &range, &range6); switch (err) { case Ipv4_Address: /* If more than one IP address given, make the range is * an even power of two (1, 2, 4, 8, 16, ...) */ if (!is_power_of_two((uint64_t)range.end - range.begin + 1)) { LOG(0, "FAIL: range must be even power of two: %s=%s\n", name, value); exit(1); } masscan->nic[index].src.ipv4.first = range.begin; masscan->nic[index].src.ipv4.last = range.end; masscan->nic[index].src.ipv4.range = range.end - range.begin + 1; break; case Ipv6_Address: masscan->nic[index].src.ipv6.first = range6.begin; masscan->nic[index].src.ipv6.last = range6.end; masscan->nic[index].src.ipv6.range = 1; /* TODO: add support for more than one source */ break; default: LOG(0, "FAIL: bad source IP address: %s=%s\n", name, value); LOG(0, "hint addresses look like \"192.168.1.23\" or \"2001:db8:1::1ce9\".\n"); exit(1); } } else if (EQUALS("adapter-port", name) || EQUALS("source-port", name) || EQUALS("src-port", name)) { /* Send packets FROM this port number */ unsigned is_error = 0; struct RangeList ports = {0}; memset(&ports, 0, sizeof(ports)); rangelist_parse_ports(&ports, value, &is_error, 0); /* Check if there was an error in parsing */ if (is_error) { LOG(0, "FAIL: bad source port specification: %s\n", name); exit(1); } /* Only allow one range of ports */ if (ports.count != 1) { LOG(0, "FAIL: only one '%s' range may be specified, found %u ranges\n", name, ports.count); exit(1); } /* verify range is even power of 2 (1, 2, 4, 8, 16, ...) */ if (!is_power_of_two(ports.list[0].end - ports.list[0].begin + 1)) { LOG(0, "FAIL: source port range must be even power of two: %s=%s\n", name, value); exit(1); } masscan->nic[index].src.port.first = ports.list[0].begin; masscan->nic[index].src.port.last = ports.list[0].end; masscan->nic[index].src.port.range = ports.list[0].end - ports.list[0].begin + 1; } else if (EQUALS("adapter-mac", name) || EQUALS("spoof-mac", name) || EQUALS("source-mac", name) || EQUALS("src-mac", name)) { /* Send packets FROM this MAC address */ macaddress_t source_mac; int err; err = parse_mac_address(value, &source_mac); if (err) { LOG(0, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } /* Check for duplicates */ if (macaddress_is_equal(masscan->nic[index].source_mac, source_mac)) { /* suppresses warning message about duplicate MAC addresses if * they are in fact the same */ return; } /* Warn if we are overwriting a Mac address */ if (masscan->nic[index].my_mac_count != 0) { ipaddress_formatted_t fmt1 = macaddress_fmt(masscan->nic[index].source_mac); ipaddress_formatted_t fmt2 = macaddress_fmt(source_mac); LOG(0, "[-] WARNING: overwriting MAC address, was %s, now %s\n", fmt1.string, fmt2.string); } masscan->nic[index].source_mac = source_mac; masscan->nic[index].my_mac_count = 1; } else if (EQUALS("router-mac", name) || EQUALS("router", name) || EQUALS("dest-mac", name) || EQUALS("destination-mac", name) || EQUALS("dst-mac", name) || EQUALS("target-mac", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv4 = router_mac; masscan->nic[index].router_mac_ipv6 = router_mac; } else if (EQUALS("router-mac-ipv4", name) || EQUALS("router-ipv4", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv4 = router_mac; } else if (EQUALS("router-mac-ipv6", name) || EQUALS("router-ipv6", name)) { macaddress_t router_mac; int err; err = parse_mac_address(value, &router_mac); if (err) { fprintf(stderr, "[-] CONF: bad MAC address: %s = %s\n", name, value); return; } masscan->nic[index].router_mac_ipv6 = router_mac; } else if (EQUALS("router-ip", name)) { /* Send packets FROM this IP address */ struct Range range; range = range_parse_ipv4(value, 0, 0); /* Check for bad format */ if (range.begin != range.end) { LOG(0, "FAIL: bad source IPv4 address: %s=%s\n", name, value); LOG(0, "hint addresses look like \"19.168.1.23\"\n"); exit(1); } masscan->nic[index].router_ip = range.begin; } else if (EQUALS("udp-ports", name) || EQUALS("udp-port", name)) { unsigned is_error = 0; masscan->scan_type.udp = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_UDP); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("oprotos", name) || EQUALS("oproto", name)) { unsigned is_error = 0; masscan->scan_type.oproto = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_Oproto_first); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("tcp-ports", name) || EQUALS("tcp-port", name)) { unsigned is_error = 0; masscan->scan_type.tcp = 1; rangelist_parse_ports(&masscan->targets.ports, value, &is_error, Templ_TCP); if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("ports", name) || EQUALS("port", name) || EQUALS("dst-port", name) || EQUALS("dest-port", name) || EQUALS("destination-port", name) || EQUALS("target-port", name)) { unsigned defaultrange = 0; int err; if (masscan->scan_type.udp) defaultrange = Templ_UDP; else if (masscan->scan_type.sctp) defaultrange = Templ_SCTP; err = massip_add_port_string(&masscan->targets, value, defaultrange); if (err) { fprintf(stderr, "[-] FAIL: bad target port: %s\n", value); fprintf(stderr, " Hint: a port is a number [0..65535]\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("banner-types", name) || EQUALS("banner-type", name) || EQUALS("banner-apps", name) || EQUALS("banner-app", name) ) { enum ApplicationProtocol app; app = masscan_string_to_app(value); if (app) { rangelist_add_range(&masscan->banner_types, app, app); rangelist_sort(&masscan->banner_types); } else { LOG(0, "FAIL: bad banner app: %s\n", value); fprintf(stderr, "err\n"); exit(1); } } else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) { unsigned defaultrange = 0; int err; if (masscan->scan_type.udp) defaultrange = Templ_UDP; else if (masscan->scan_type.sctp) defaultrange = Templ_SCTP; err = massip_add_port_string(&masscan->exclude, value, defaultrange); if (err) { fprintf(stderr, "[-] FAIL: bad exclude port: %s\n", value); fprintf(stderr, " Hint: a port is a number [0..65535]\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("bpf", name)) { size_t len = strlen(value) + 1; if (masscan->bpf_filter) free(masscan->bpf_filter); masscan->bpf_filter = MALLOC(len); memcpy(masscan->bpf_filter, value, len); } 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->targets.ports, range.begin, range.end); rangelist_sort(&masscan->targets.ports); masscan->scan_type.ping = 1; LOG(5, "--ping\n"); } else if (EQUALS("range", name) || EQUALS("ranges", name) || EQUALS("ip", name) || EQUALS("ipv4", name) || EQUALS("dst-ip", name) || EQUALS("dest-ip", name) || EQUALS("destination-ip", name) || EQUALS("target-ip", name)) { int err; err = massip_add_target_string(&masscan->targets, value); if (err) { fprintf(stderr, "ERROR: bad IP address/range: %s\n", value); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if ( EQUALS("exclude", name) || EQUALS("exclude-range", name) || EQUALS("exclude-ranges", name) || EQUALS("exclude-ip", name) || EQUALS("exclude-ipv4", name) ) { int err; err = massip_add_target_string(&masscan->exclude, value); if (err) { fprintf(stderr, "ERROR: bad exclude address/range: %s\n", value); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("badsum", name)) { masscan->nmap.badsum = 1; } else if (EQUALS("banner1", name)) { banner1_test(value); exit(1); } 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 */ masscan->tcp_connection_timeout = (unsigned)parseInt(value); } else if (EQUALS("datadir", name)) { safe_strcpy(masscan->nmap.datadir, sizeof(masscan->nmap.datadir), value); } else if (EQUALS("data-length", name)) { unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 1514 - 14 - 40) { fprintf(stderr, "error: %s=: expected number less than 1500\n", name); } else { masscan->nmap.data_length = x; } } else if (EQUALS("debug", name)) { if (EQUALS("if", value)) { masscan->op = Operation_DebugIF; } } else if (EQUALS("dns-servers", name)) { fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n", name); exit(1); } else if (EQUALS("echo", name)) { masscan->op = Operation_Echo; } else if (EQUALS("echo-all", name)) { masscan->op = Operation_EchoAll; } else if (EQUALS("echo-cidr", name)) { masscan->op = Operation_EchoCidr; } else if (EQUALS("excludefile", name)) { unsigned count1 = masscan->exclude.ipv4.count; unsigned count2; int err; const char *filename = value; LOG(1, "EXCLUDING: %s\n", value); err = massip_parse_file(&masscan->exclude, filename); if (err) { LOG(0, "[-] FAIL: error reading from exclude file\n"); exit(1); } /* Detect if this file has made any change, otherwise don't print * a message */ count2 = masscan->exclude.ipv4.count; if (count2 - count1) fprintf(stderr, "%s: excluding %u ranges from file\n", value, count2 - count1); } else if (EQUALS("heartbleed", name)) { masscan->is_heartbleed = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "no-capture", "heartbleed"); masscan_set_parameter(masscan, "banners", "true"); } else if (EQUALS("ticketbleed", name)) { masscan->is_ticketbleed = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "no-capture", "ticketbleed"); masscan_set_parameter(masscan, "banners", "true"); } else if (EQUALS("host-timeout", name)) { fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name); exit(1); } else if (EQUALS("iflist", name)) { masscan->op = Operation_List_Adapters; } else if (EQUALS("includefile", name)) { int err; const char *filename = value; err = massip_parse_file(&masscan->targets, filename); if (err) { LOG(0, "[-] FAIL: error reading from include file\n"); exit(1); } if (masscan->op == 0) masscan->op = Operation_Scan; } else if (EQUALS("infinite", name)) { masscan->is_infinite = 1; } else if (EQUALS("interactive", name)) { masscan->output.is_interactive = 1; } else if (EQUALS("nointeractive", name)) { masscan->output.is_interactive = 0; } else if (EQUALS("status", name)) { masscan->output.is_status_updates = 1; } else if (EQUALS("nostatus", name)) { masscan->output.is_status_updates = 0; } else if (EQUALS("ip-options", name)) { fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name); exit(1); } else if (EQUALS("log-errors", name)) { fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name); exit(1); } else if (EQUALS("min-hostgroup", name) || EQUALS("max-hostgroup", name)) { fprintf(stderr, "nmap(%s): unsupported: we randomize all the groups!\n", name); exit(1); } else if (EQUALS("min-parallelism", name) || EQUALS("max-parallelism", name)) { fprintf(stderr, "nmap(%s): unsupported: we all the parallel!\n", name); exit(1); } else if (EQUALS("min-rtt-timeout", name) || EQUALS("max-rtt-timeout", name) || EQUALS("initial-rtt-timeout", name)) { fprintf(stderr, "nmap(%s): unsupported: we are asynchronous, so no timeouts, no RTT tracking!\n", name); exit(1); } else if (EQUALS("min-rate", name)) { fprintf(stderr, "nmap(%s): unsupported, we go as fast as --max-rate allows\n", name); /* no exit here, since it's just info */ } else if (EQUALS("mtu", name)) { fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", name); exit(1); } else if (EQUALS("nmap", name)) { print_nmap_help(); exit(1); } 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("packet-trace", name) || EQUALS("trace-packet", name)) { masscan->nmap.packet_trace = 1; } else if (EQUALS("privileged", name) || EQUALS("unprivileged", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("pfring", name)) { masscan->is_pfring = 1; } else if (EQUALS("port-ratio", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("readrange", name) || EQUALS("readranges", name)) { masscan->op = Operation_ReadRange; } else if (EQUALS("reason", name)) { masscan->output.is_reason = 1; } else if (EQUALS("redis", name)) { struct Range range; unsigned offset = 0; unsigned max_offset = (unsigned)strlen(value); unsigned port = 6379; range = range_parse_ipv4(value, &offset, max_offset); if ((range.begin == 0 && range.end == 0) || range.begin != range.end) { LOG(0, "FAIL: bad redis IP address: %s\n", value); exit(1); } if (offset < max_offset) { while (offset < max_offset && isspace(value[offset])) offset++; if (offset+1 < max_offset && value[offset] == ':' && isdigit(value[offset+1]&0xFF)) { 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); } } } /* TODO: add support for connecting to IPv6 addresses here */ masscan->redis.ip.ipv4 = range.begin; masscan->redis.ip.version = 4; masscan->redis.port = port; masscan->output.format = Output_Redis; safe_strcpy(masscan->output.filename, sizeof(masscan->output.filename), ""); } else if(EQUALS("redis-pwd", name)) { masscan->redis.password = strdup(value); } else if (EQUALS("release-memory", name)) { fprintf(stderr, "nmap(%s): this is our default option\n", name); } else if (EQUALS("resume", name)) { masscan_read_config_file(masscan, value); masscan_set_parameter(masscan, "output-append", "true"); } else if (EQUALS("vuln", name)) { if (EQUALS("heartbleed", value)) { masscan_set_parameter(masscan, "heartbleed", "true"); return; } else if (EQUALS("ticketbleed", value)) { masscan_set_parameter(masscan, "ticketbleed", "true"); return; } else if (EQUALS("poodle", value) || EQUALS("sslv3", value)) { masscan->is_poodle_sslv3 = 1; masscan_set_parameter(masscan, "no-capture", "cert"); masscan_set_parameter(masscan, "banners", "true"); return; } if (!vulncheck_lookup(value)) { fprintf(stderr, "FAIL: vuln check '%s' does not exist\n", value); fprintf(stderr, " hint: use '--vuln list' to list available scripts\n"); exit(1); } if (masscan->vuln_name != NULL) { if (strcmp(masscan->vuln_name, value) == 0) return; /* ok */ else { fprintf(stderr, "FAIL: only one vuln check supported at a time\n"); fprintf(stderr, " hint: '%s' is existing vuln check, '%s' is new vuln check\n", masscan->vuln_name, value); exit(1); } } masscan->vuln_name = vulncheck_lookup(value)->name; } else if (EQUALS("scan-delay", name) || EQUALS("max-scan-delay", name)) { fprintf(stderr, "nmap(%s): unsupported: we do timing VASTLY differently!\n", name); exit(1); } else if (EQUALS("scanflags", name)) { fprintf(stderr, "nmap(%s): TCP scan flags not yet supported\n", name); exit(1); } else if (EQUALS("sendq", name) || EQUALS("sendqueue", name)) { masscan->is_sendq = 1; } else if (EQUALS("send-eth", name)) { fprintf(stderr, "nmap(%s): unnecessary, we always do --send-eth\n", name); } else if (EQUALS("send-ip", name)) { fprintf(stderr, "nmap(%s): unsupported, we only do --send-eth\n", name); exit(1); } else if (EQUALS("selftest", name) || EQUALS("self-test", name) || EQUALS("regress", name)) { masscan->op = Operation_Selftest; return; } else if (EQUALS("benchmark", name)) { masscan->op = Operation_Benchmark; return; } else if (EQUALS("source-port", name) || EQUALS("sourceport", name)) { masscan_set_parameter(masscan, "adapter-port", value); } else if (EQUALS("nobacktrace", name) || EQUALS("backtrace", name)) { ; } else if (EQUALS("no-stylesheet", name)) { masscan->output.stylesheet[0] = '\0'; } else if (EQUALS("system-dns", name)) { fprintf(stderr, "nmap(%s): DNS lookups will never be supported by this code\n", name); exit(1); } else if (EQUALS("top-ports", name)) { unsigned n = (unsigned)parseInt(value); 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); } else if (EQUALS("test", name)) { if (EQUALS("csv", value)) masscan->is_test_csv = 1; } else if (EQUALS("notest", name)) { if (EQUALS("csv", value)) masscan->is_test_csv = 0; } else if (EQUALS("ttl", name)) { unsigned x = (unsigned)strtoul(value, 0, 0); if (x >= 256) { fprintf(stderr, "error: %s=: expected number less than 256\n", name); } else { masscan->nmap.ttl = x; } } else if (EQUALS("version", name)) { print_version(); exit(1); } else if (EQUALS("version-intensity", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-light", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-all", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("version-trace", name)) { fprintf(stderr, "nmap(%s): unsupported\n", name); exit(1); } else if (EQUALS("vlan", name) || EQUALS("adapter-vlan", name)) { masscan->nic[index].is_vlan = 1; masscan->nic[index].vlan_id = (unsigned)parseInt(value); } else if (EQUALS("wait", name)) { if (EQUALS("forever", value)) masscan->wait = INT_MAX; else masscan->wait = (unsigned)parseInt(value); } else if (EQUALS("webxml", name)) { 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); } } static bool is_numable(const char *name) { size_t i; for (i=0; config_parameters[i].name; i++) { if (EQUALS(config_parameters[i].name, name)) { return (config_parameters[i].flags & F_NUMABLE) == F_NUMABLE; } 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_NUMABLE) == F_NUMABLE; } } } } return false; } /*************************************************************************** * Command-line parsing code assumes every --parm is followed by a value. * This is a list of the parameters that don't follow the default. ***************************************************************************/ static int is_singleton(const char *name) { static const char *singletons[] = { "echo", "echo-all", "echo-cidr", "selftest", "self-test", "regress", "benchmark", "system-dns", "traceroute", "version", "version-light", "version-all", "version-trace", "osscan-limit", "osscan-guess", "badsum", "reason", "open", "open-only", "packet-trace", "release-memory", "log-errors", "append-output", "webxml", "no-stylesheet", "heartbleed", "ticketbleed", "send-eth", "send-ip", "iflist", "nmap", "trace-packet", "pfring", "sendq", "ping", "ping-sweep", "nobacktrace", "backtrace", "infinite", "nointeractive", "interactive", "status", "nostatus", "read-range", "read-ranges", "readrange", "read-ranges", 0}; size_t i; for (i=0; singletons[i]; i++) { 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; } /***************************************************************************** *****************************************************************************/ static void masscan_help() { printf( "usage: masscan [options] [... -pPORT[,PORT...]]\n" "MASSCAN is a fast port scanner. The primary input parameters are the\n" "IP addresses/ranges you want to scan, and the port numbers. An example\n" "is the following, which scans the 10.x.x.x network for web servers:\n" "\n" " masscan 10.0.0.0/8 -p80\n" "\n" "The program auto-detects network interface/adapter settings. If this\n" "fails, you'll have to set these manually. The following is an\n" "example of all the parameters that are needed:\n" "\n" " --adapter-ip 192.168.10.123\n" " --adapter-mac 00-11-22-33-44-55\n" " --router-mac 66-55-44-33-22-11\n" "\n" "Parameters can be set either via the command-line or config-file. The\n" "names are the same for both. Thus, the above adapter settings would\n" "appear as follows in a configuration file:\n" "\n" " adapter-ip = 192.168.10.123\n" " adapter-mac = 00-11-22-33-44-55\n" " router-mac = 66-55-44-33-22-11\n" "\n" "All single-dash parameters have a spelled out double-dash equivalent,\n" "so '-p80' is the same as '--ports 80' (or 'ports = 80' in config file).\n" "To use the config file, type:\n" "\n" " masscan -c \n" "\n" "To generate a config-file from the current settings, use the --echo\n" "option. This stops the program from actually running, and just echoes\n" "the current configuration instead. This is a useful way to generate\n" "your first config file, or see a list of parameters you didn't know\n" "about. I suggest you try it now:\n" "\n" " masscan -p1234 --echo\n" "\n"); exit(1); } /*************************************************************************** ***************************************************************************/ void masscan_load_database_files(struct Masscan *masscan) { const char *filename; /* * "pcap-payloads" */ filename = masscan->payloads.pcap_payloads_filename; if (filename) { if (masscan->payloads.udp == NULL) masscan->payloads.udp = payloads_udp_create(); if (masscan->payloads.oproto == NULL) masscan->payloads.oproto = payloads_udp_create(); payloads_read_pcap(filename, masscan->payloads.udp, masscan->payloads.oproto); } /* * `--nmap-payloads` */ filename = masscan->payloads.nmap_payloads_filename; if (filename) { FILE *fp; fp = fopen(filename, "rt"); if (fp == NULL) { fprintf(stderr, "[-] FAIL: --nmap-payloads\n"); fprintf(stderr, "[-] %s:%s\n", filename, strerror(errno)); } else { if (masscan->payloads.udp == NULL) masscan->payloads.udp = payloads_udp_create(); payloads_udp_readfile(fp, filename, masscan->payloads.udp); fclose(fp); } } /* * "nmap-service-probes" */ filename = masscan->payloads.nmap_service_probes_filename; if (filename) { if (masscan->payloads.probes) nmapserviceprobes_free(masscan->payloads.probes); masscan->payloads.probes = nmapserviceprobes_read_file(filename); } } /*************************************************************************** * Read the configuration from the command-line. * Called by 'main()' when starting up. ***************************************************************************/ void masscan_command_line(struct Masscan *masscan, int argc, char *argv[]) { int i; for (i=1; i sizeof(name2) - 1) { fprintf(stderr, "%.*s: name too long\n", name_length, name); name_length = sizeof(name2) - 1; } memcpy(name2, name, name_length); name2[name_length] = '\0'; masscan_set_parameter(masscan, name2, value); } else if (EQUALS("readscan", argv[i]+2)) { /* Read in a binary file instead of scanning the network*/ masscan->op = Operation_ReadScan; /* Default to reading banners */ masscan->is_banners = true; masscan->is_banners_rawudp = true; /* This option may be followed by many filenames, therefore, * skip forward in the argument list until the next * argument */ while (i+1 < argc && argv[i+1][0] != '-') i++; continue; } else { char name2[64]; char *name = argv[i] + 2; unsigned name_length; const char *value; value = strchr(&argv[i][2], '='); if (value == NULL) value = strchr(&argv[i][2], ':'); if (value == NULL) { if (is_singleton(name)) value = ""; else value = argv[++i]; name_length = (unsigned)strlen(name); } else { name_length = (unsigned)(value - name); value++; } if (i >= argc) { fprintf(stderr, "%.*s: empty parameter\n", name_length, name); break; } if (name_length > sizeof(name2) - 1) { fprintf(stderr, "%.*s: name too long\n", name_length, name); name_length = sizeof(name2) - 1; } memcpy(name2, name, name_length); name2[name_length] = '\0'; masscan_set_parameter(masscan, name2, value); } continue; } /* For for a single-dash parameter */ if (argv[i][0] == '-') { const char *arg; switch (argv[i][1]) { case '6': /* Silently ignore this: IPv6 features enabled all the time */ break; case 'A': fprintf(stderr, "nmap(%s): unsupported: this tool only does SYN scan\n", argv[i]); exit(1); case 'b': fprintf(stderr, "nmap(%s): FTP bounce scans will never be supported\n", argv[i]); exit(1); case 'c': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_read_config_file(masscan, arg); break; case 'd': /* just do same as verbosity level */ { int v; for (v=1; argv[i][v] == 'd'; v++) { LOG_add_level(1); } } break; case 'e': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter", arg); break; case 'f': fprintf(stderr, "nmap(%s): fragmentation not yet supported\n", argv[i]); exit(1); case 'F': fprintf(stderr, "nmap(%s): unsupported, no slow/fast mode\n", argv[i]); exit(1); case 'g': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter-port", arg); break; case 'h': case '?': masscan_usage(); break; case 'i': if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) { /* This looks like an nmap option*/ switch (argv[i][2]) { case 'L': masscan_set_parameter(masscan, "includefile", argv[++i]); break; case 'R': /* -iR in nmap makes it randomize addresses completely. Thus, * it's nearest equivalent is scanning the entire Internet range */ masscan_set_parameter(masscan, "include", "0.0.0.0/0"); break; default: fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]); exit(1); } } else { if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter", arg); } break; case 'n': /* This looks like an nmap option*/ /* Do nothing: this code never does DNS lookups anyway */ break; case 'o': /* nmap output format */ switch (argv[i][2]) { case 'A': masscan->output.format = Output_All; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'B': masscan->output.format = Output_Binary; break; case 'D': masscan->output.format = Output_NDJSON; break; case 'J': masscan->output.format = Output_JSON; break; case 'N': masscan->output.format = Output_Nmap; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'X': masscan->output.format = Output_XML; break; case 'R': masscan->output.format = Output_Redis; if (i+1 < argc && argv[i+1][0] != '-') masscan_set_parameter(masscan, "redis", argv[i+1]); break; case 'S': masscan->output.format = Output_ScriptKiddie; fprintf(stderr, "nmap(%s): unsupported output format\n", argv[i]); exit(1); break; case 'G': masscan->output.format = Output_Grepable; break; case 'L': masscan_set_parameter(masscan, "output-format", "list"); break; case 'U': masscan_set_parameter(masscan, "output-format", "unicornscan"); break; case 'H': masscan_set_parameter(masscan, "output-format", "hostonly"); break; default: fprintf(stderr, "nmap(%s): unknown output format\n", argv[i]); exit(1); } ++i; if (i >= argc || (argv[i][0] == '-' && argv[i][1] != '\0')) { fprintf(stderr, "missing output filename\n"); exit(1); } masscan_set_parameter(masscan, "output-filename", argv[i]); break; case 'O': fprintf(stderr, "nmap(%s): unsupported, OS detection is too complex\n", argv[i]); exit(1); break; case 'p': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; if (i >= argc || arg[0] == 0) { // if string is empty fprintf(stderr, "%s: empty parameter\n", argv[i]); } else masscan_set_parameter(masscan, "ports", arg); break; case 'P': switch (argv[i][2]) { case 'n': /* we already do this */ break; default: fprintf(stderr, "nmap(%s): unsupported option, maybe in future\n", argv[i]); exit(1); } break; case 'r': /* This looks like an nmap option*/ fprintf(stderr, "nmap(%s): wat? randomization is our raison d'etre!! rethink prease\n", argv[i]); exit(1); break; case 'R': /* This looks like an nmap option*/ fprintf(stderr, "nmap(%s): unsupported. This code will never do DNS lookups.\n", argv[i]); exit(1); break; case 's': /* NMAP: scan type */ if (argv[i][3] == '\0' && !isdigit(argv[i][2]&0xFF)) { unsigned j; for (j=2; argv[i][j]; j++) switch (argv[i][j]) { case 'A': fprintf(stderr, "nmap(%s): ACK scan not yet supported\n", argv[i]); exit(1); case 'C': fprintf(stderr, "nmap(%s): unsupported\n", argv[i]); exit(1); case 'F': fprintf(stderr, "nmap(%s): FIN scan not yet supported\n", argv[i]); exit(1); case 'I': fprintf(stderr, "nmap(%s): Zombie scans will never be supported\n", argv[i]); exit(1); case 'L': /* List Scan - simply list targets to scan */ masscan->op = Operation_ListScan; break; case 'M': fprintf(stderr, "nmap(%s): Maimon scan not yet supported\n", argv[i]); exit(1); case 'n': /* Ping Scan - disable port scan */ fprintf(stderr, "nmap(%s): ping-sweeps not yet supported\n", argv[i]); exit(1); case 'N': fprintf(stderr, "nmap(%s): NULL scan not yet supported\n", argv[i]); exit(1); case 'O': /* Other IP protocols (not ICMP, UDP, TCP, or SCTP) */ masscan->scan_type.oproto = 1; break; case 'S': /* TCP SYN scan - THIS IS WHAT WE DO! */ masscan->scan_type.tcp = 1; break; case 'T': /* TCP connect scan */ fprintf(stderr, "nmap(%s): connect() is too synchronous for cool kids\n", argv[i]); fprintf(stderr, "WARNING: doing SYN scan (-sS) anyway, ignoring (-sT)\n"); break; case 'U': /* UDP scan */ masscan->scan_type.udp = 1; break; case 'V': fprintf(stderr, "nmap(%s): unlikely this will be supported\n", argv[i]); exit(1); case 'W': fprintf(stderr, "nmap(%s): Windows scan not yet supported\n", argv[i]); exit(1); case 'X': fprintf(stderr, "nmap(%s): Xmas scan not yet supported\n", argv[i]); exit(1); case 'Y': break; case 'Z': masscan->scan_type.sctp = 1; break; default: fprintf(stderr, "nmap(%s): unsupported option\n", argv[i]); exit(1); } } else { fprintf(stderr, "%s: unknown parameter\n", argv[i]); exit(1); } break; case 'S': if (argv[i][2]) arg = argv[i]+2; else arg = argv[++i]; masscan_set_parameter(masscan, "adapter-ip", arg); break; case 'v': { int v; for (v=1; argv[i][v] == 'v'; v++) LOG_add_level(1); } break; case 'V': /* print version and exit */ masscan_set_parameter(masscan, "version", ""); break; case 'W': masscan->op = Operation_List_Adapters; return; case 'T': fprintf(stderr, "nmap(%s): unsupported, we do timing WAY different than nmap\n", argv[i]); exit(1); return; default: LOG(0, "FAIL: unknown option: -%s\n", argv[i]); LOG(0, " [hint] try \"--help\"\n"); LOG(0, " [hint] ...or, to list nmap-compatible options, try \"--nmap\"\n"); exit(1); } continue; } if (!isdigit(argv[i][0]) && argv[i][0] != ':' && argv[i][0] != '[') { fprintf(stderr, "FAIL: unknown command-line parameter \"%s\"\n", argv[i]); fprintf(stderr, " [hint] did you want \"--%s\"?\n", argv[i]); exit(1); } /* If parameter doesn't start with '-', assume it's an * IPv4 range */ 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.oproto == 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); } if (masscan->shard.of < masscan->shard.one) { fprintf(stderr, "[-] WARNING: the shard number must be less than the total shard count: %u/%u\n", masscan->shard.one, masscan->shard.of); } if (masscan->shard.of > 1 && masscan->seed == 0) { fprintf(stderr, "[-] WARNING: --seed is not specified\n HINT: all shards must share the same seed\n"); } } /*************************************************************************** * Prints the current configuration to the command-line then exits. * Use#1: create a template file of all settable parameters. * Use#2: make sure your configuration was interpreted correctly. ***************************************************************************/ 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; inic_count; i++) masscan_echo_nic(masscan, fp, i); } /** * Fix for #737, save adapter-port/source-port value or range */ if (masscan->nic[0].src.port.first != 0) { fprintf(fp, "adapter-port = %d", masscan->nic[0].src.port.first); if (masscan->nic[0].src.port.first != masscan->nic[0].src.port.last) { /* --adapter-port - */ fprintf(fp, "-%d", masscan->nic[0].src.port.last); } fprintf(fp, "\n"); } /* * Targets */ fprintf(fp, "# TARGET SELECTION (IP, PORTS, EXCLUDES)\n"); fprintf(fp, "ports = "); /* Disable comma generation for the first element */ l = 0; for (i=0; itargets.ports.count; i++) { struct Range range = masscan->targets.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 if (Templ_Oproto_first <= rrange.begin && rrange.begin <= Templ_Oproto_last) { rrange.begin -= Templ_Oproto_first; rrange.end -= Templ_Oproto_first; fprintf(fp, "O:"); range.begin = Templ_Oproto_first; } 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"); /* * IPv4 address targets */ for (i=0; itargets.ipv4.count; i++) { unsigned prefix_bits; struct Range range = masscan->targets.ipv4.list[i]; if (range.begin == range.end) { fprintf(fp, "range = %u.%u.%u.%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF ); } else if (range_is_cidr(range, &prefix_bits)) { fprintf(fp, "range = %u.%u.%u.%u/%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, prefix_bits ); } else { fprintf(fp, "range = %u.%u.%u.%u-%u.%u.%u.%u", (range.begin>>24)&0xFF, (range.begin>>16)&0xFF, (range.begin>> 8)&0xFF, (range.begin>> 0)&0xFF, (range.end>>24)&0xFF, (range.end>>16)&0xFF, (range.end>> 8)&0xFF, (range.end>> 0)&0xFF ); } fprintf(fp, "\n"); } for (i=0; itargets.ipv6.count; i++) { bool exact = false; struct Range6 range = masscan->targets.ipv6.list[i]; ipaddress_formatted_t fmt = ipv6address_fmt(range.begin); fprintf(fp, "range = %s", fmt.string); if (!ipv6address_is_equal(range.begin, range.end)) { unsigned cidr_bits = count_cidr6_bits(&range, &exact); if (exact && cidr_bits) { fprintf(fp, "/%u", cidr_bits); } else { fmt = ipv6address_fmt(range.end); fprintf(fp, "-%s", fmt.string); } } fprintf(fp, "\n"); } } /*************************************************************************** * Prints the list of CIDR to scan to the command-line then exits. * Use: provide this list to other tools. Unlike masscan -sL, it keeps * the CIDR aggretated format, and does not randomize the order of output. * For example, given the starting range of [10.0.0.1-10.0.0.255], this will * print all the CIDR ranges that make this up: * 10.0.0.1/32 * 10.0.0.2/31 * 10.0.0.4/30 * 10.0.0.8/29 * 10.0.0.16/28 * 10.0.0.32/27 * 10.0.0.64/26 * 10.0.0.128/25 ***************************************************************************/ void masscan_echo_cidr(struct Masscan *masscan, FILE *fp, unsigned is_echo_all) { unsigned i; UNUSEDPARM(is_echo_all); masscan->echo = fp; /* * For all IPv4 ranges ... */ for (i=0; itargets.ipv4.count; i++) { /* Get the next range in the list */ struct Range range = masscan->targets.ipv4.list[i]; /* If not a single CIDR range, print all the CIDR ranges * needed to completely represent this addres */ for (;;) { unsigned prefix_length; struct Range cidr; /* Find the largest CIDR range (one that can be specified * with a /prefix) at the start of this range. */ cidr = range_first_cidr(range, &prefix_length); fprintf(fp, "%u.%u.%u.%u/%u\n", (cidr.begin>>24)&0xFF, (cidr.begin>>16)&0xFF, (cidr.begin>> 8)&0xFF, (cidr.begin>> 0)&0xFF, prefix_length ); /* If this is the last range, then stop. There are multiple * ways to gets to see if we get to the end, but I think * this is the best. */ if (cidr.end >= range.end) break; /* If the CIDR range didn't cover the entire range, * then remove it from the beginning of the range * and process the remainder */ range.begin = cidr.end+1; } } /* * For all IPv6 ranges... */ for (i=0; itargets.ipv6.count; i++) { struct Range6 range = masscan->targets.ipv6.list[i]; bool exact = false; while (!exact) { ipaddress_formatted_t fmt = ipv6address_fmt(range.begin); fprintf(fp, "%s", fmt.string); if (range.begin.hi == range.end.hi && range.begin.lo == range.end.lo) { fprintf(fp, "/128"); exact = true; } else { unsigned cidr_bits = count_cidr6_bits(&range, &exact); fprintf(fp, "/%u", cidr_bits); } fprintf(fp, "\n"); } } } /*************************************************************************** * remove leading/trailing whitespace ***************************************************************************/ static void trim(char *line, size_t sizeof_line) { if (sizeof_line > strlen(line)) sizeof_line = strlen(line); while (isspace(*line & 0xFF)) memmove(line, line+1, sizeof_line--); while (*line && isspace(line[sizeof_line-1] & 0xFF)) line[--sizeof_line] = '\0'; } /*************************************************************************** ***************************************************************************/ void masscan_read_config_file(struct Masscan *masscan, const char *filename) { FILE *fp; char line[65536]; fp = fopen(filename, "rt"); if (fp == NULL) { char dir[512]; char *x; fprintf(stderr, "[-] FAIL: reading configuration file\n"); fprintf(stderr, "[-] %s: %s\n", filename, strerror(errno)); x = getcwd(dir, sizeof(dir)); if (x) fprintf(stderr, "[-] cwd = %s\n", dir); return; } while (fgets(line, sizeof(line), fp)) { char *name; char *value; trim(line, sizeof(line)); if (ispunct(line[0] & 0xFF) || line[0] == '\0') continue; name = line; value = strchr(line, '='); if (value == NULL) continue; *value = '\0'; value++; trim(name, sizeof(line)); trim(value, sizeof(line)); masscan_set_parameter(masscan, name, value); } fclose(fp); } /*************************************************************************** ***************************************************************************/ int masscan_conf_contains(const char *x, int argc, char **argv) { int i; for (i=0; i