Loading VULNINFO.md +3 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,9 @@ scriptable service, this becomes a potential vulnerability. Unsafe functions like `strcpy()` are banned. The code contains an automated regression test by running with the `--regress` option. `--regress` option. However, currently the regression only tests a small percentage of the code. Loading src/main-dedup.c 0 → 100644 +78 −0 Original line number Diff line number Diff line /* Filters duplicate responses This is an asynchronous scanner. Therefore, there is no easy way to correlate probes with responses. We must therefore suffer the fact that sometimes we get repeat responses, and therefore, repeat records. We can mimimize this with a table remembering recent responses. Occassional duplicates still leak through, but it'll be less of a problem. */ #include <stdlib.h> #include <string.h> #define DEDUP_ENTRIES 1024 struct DedupEntry { unsigned ip; unsigned port; }; struct DedupTable { struct DedupEntry entries[DEDUP_ENTRIES][4]; }; /*************************************************************************** ***************************************************************************/ struct DedupTable * dedup_create() { struct DedupTable *result; result = (struct DedupTable *)malloc(sizeof(*result)); memset(result, 0, sizeof(*result)); return result; } /*************************************************************************** ***************************************************************************/ void dedup_destroy(struct DedupTable *table) { if (table) free(table); } /*************************************************************************** ***************************************************************************/ unsigned dedup_is_duplicate(struct DedupTable *dedup, unsigned ip, unsigned port) { unsigned hash; struct DedupEntry *bucket; unsigned i; /* THREAT: We've already validated resonses via SYN-cookies, so * therefore we don't need a robust hash for duplicate detection */ hash = ip + port ^ (ip>>8) + (ip>>16) ^ (ip>>24); hash &= DEDUP_ENTRIES-1; /* Search in this bucket */ bucket = dedup->entries[hash]; for (i = 0; i < 4; i++) { if (bucket[i].ip == ip && bucket[i].port == port) return 1; } /* We didn't find it, so add it to our list. This will push * older entries at this bucket off the list */ memmove(bucket, bucket+1, 3*sizeof(*bucket)); bucket[0].ip = ip; bucket[0].port = port; return 0; } src/main-dedup.h 0 → 100644 +9 −0 Original line number Diff line number Diff line #ifndef MAIN_DEDUP_H #define MAIN_DEDUP_H struct DedupTable *dedup_create(); void dedup_destroy(struct DedupTable *table); unsigned dedup_is_duplicate(struct DedupTable *dedup, unsigned ip, unsigned port); #endif src/main.c +61 −35 Original line number Diff line number Diff line Loading @@ -2,8 +2,11 @@ main This includes the main() function, as well as the inner loop of the scan function 'scanning_thread()'. This includes: * main() * scanning_thread() - transmits packets * main_scan() - launch and receive packets */ #include "masscan.h" Loading @@ -13,6 +16,7 @@ #include "logger.h" /* adjust with -v command-line opt */ #include "main-status.h" /* printf() regular status updates */ #include "main-throttle.h" /* rate limit */ #include "main-dedup.h" /* ignore duplicate responses */ #include "pixie-timer.h" /* portable time functions */ #include "pixie-threads.h" /* portable threads */ Loading Loading @@ -147,7 +151,7 @@ scanning_thread(void *v) * in the configuration file. ***************************************************************************/ static int initialize(struct Masscan *masscan, initialize_adapter(struct Masscan *masscan, unsigned *r_adapter_ip, unsigned char *adapter_mac, unsigned char *router_mac) Loading Loading @@ -292,6 +296,17 @@ initialize(struct Masscan *masscan, return 0; } /*************************************************************************** ***************************************************************************/ const char *status_string(int x) { switch (x) { case Port_Open: return "open"; case Port_Closed: return "closed"; default: return "unknown"; } } /*************************************************************************** * Start the scan. This is the main function of the program. * Called from main() Loading @@ -308,11 +323,12 @@ main_scan(struct Masscan *masscan) unsigned char router_mac[6]; int err; FILE *fpout = stdout; struct DedupTable *dedup; /* * Turn the adapter on, and get the running configuration */ err = initialize( masscan, err = initialize_adapter( masscan, &adapter_ip, adapter_mac, router_mac); Loading Loading @@ -352,17 +368,15 @@ main_scan(struct Masscan *masscan) if (count_ips == 0) { fprintf(stderr, "FAIL: no IPv4 ranges were specified\n"); return 1; } else LOG(2, "range = %u IP addresses\n", count_ips); } count_ports = rangelist_count(&masscan->ports); if (count_ports == 0) { fprintf(stderr, "FAIL: no ports were specified, use \"-p<port>\"\n"); return 1; } else LOG(2, "range = %u ports\n", count_ports); } fprintf(stderr, "Scanning %u hosts [%u ports/host]\n", (unsigned)count_ips, (unsigned)count_ports); fprintf(stderr, "Scanning %u hosts [%u port%s/host]\n", (unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s"); /* * Initialize LCG translator Loading Loading @@ -405,6 +419,7 @@ main_scan(struct Masscan *masscan) exit(1); } } dedup = dedup_create(); /* * Start the scanning thread. Loading @@ -418,6 +433,7 @@ main_scan(struct Masscan *masscan) * them to the terminal. */ while (!masscan->is_done) { int status; unsigned length; unsigned secs; unsigned usecs; Loading Loading @@ -446,10 +462,12 @@ main_scan(struct Masscan *masscan) x = preprocess_frame(px, length, 1, &parsed); if (!x) continue; /* corrupt packet */ /* verify: my IP address */ dst = parsed.ip_dst[0]<<24 | parsed.ip_dst[1]<<16 | parsed.ip_dst[2]<< 8 | parsed.ip_dst[3]<<0; src = parsed.ip_src[0]<<24 | parsed.ip_src[1]<<16 | parsed.ip_src[2]<< 8 | parsed.ip_src[3]<<0; /* verify: my IP address */ if (adapter_ip != dst) continue; Loading @@ -464,10 +482,6 @@ main_scan(struct Masscan *masscan) if (parsed.found != FOUND_TCP) continue; /* verify: SYN-ACK */ if ((px[parsed.transport_offset+13] & 0x12) != 0x12) continue; /* verify: my IP address */ dst = parsed.ip_dst[0]<<24 | parsed.ip_dst[1]<<16 | parsed.ip_dst[2]<< 8 | parsed.ip_dst[3]<<0; Loading @@ -478,17 +492,28 @@ main_scan(struct Masscan *masscan) if (adapter_port != parsed.port_dst) continue; /* verify: ignore duplicates */ if (dedup_is_duplicate(dedup, src, parsed.port_src)) continue; /* figure out the status */ status = Port_Unknown; if ((px[parsed.transport_offset+13] & 0x2) == 0x2) status = Port_Open; if ((px[parsed.transport_offset+13] & 0x4) == 0x4) status = Port_Closed; /* * XXXX * TODO: add lots more verification, such as coming from one of * our sending port numbers, and having the right seqno/ackno * fields set. */ src = parsed.ip_src[0]<<24 | parsed.ip_src[1]<<16 | parsed.ip_src[2]<< 8 | parsed.ip_src[3]<<0; switch (masscan->nmap.format) { case Output_Interactive: fprintf(fpout, "Discovered open port %u/tcp on %u.%u.%u.%u \n", fprintf(fpout, "Discovered %s port %u/tcp on %u.%u.%u.%u \n", status_string(status), parsed.port_src, (src>>24)&0xFF, (src>>16)&0xFF, Loading @@ -497,7 +522,8 @@ main_scan(struct Masscan *masscan) ); break; case Output_List: fprintf(fpout, "open tcp %u %u.%u.%u.%u\n", fprintf(fpout, "%s tcp %u %u.%u.%u.%u\n", status_string(status), parsed.port_src, (src>>24)&0xFF, (src>>16)&0xFF, Loading src/masscan.h +6 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,12 @@ enum OutpuFormat { Output_List /* specific to Masscan */ }; enum PortStatus { Port_Unknown, Port_Open, Port_Closed, }; struct Masscan { int op; Loading Loading
VULNINFO.md +3 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,9 @@ scriptable service, this becomes a potential vulnerability. Unsafe functions like `strcpy()` are banned. The code contains an automated regression test by running with the `--regress` option. `--regress` option. However, currently the regression only tests a small percentage of the code. Loading
src/main-dedup.c 0 → 100644 +78 −0 Original line number Diff line number Diff line /* Filters duplicate responses This is an asynchronous scanner. Therefore, there is no easy way to correlate probes with responses. We must therefore suffer the fact that sometimes we get repeat responses, and therefore, repeat records. We can mimimize this with a table remembering recent responses. Occassional duplicates still leak through, but it'll be less of a problem. */ #include <stdlib.h> #include <string.h> #define DEDUP_ENTRIES 1024 struct DedupEntry { unsigned ip; unsigned port; }; struct DedupTable { struct DedupEntry entries[DEDUP_ENTRIES][4]; }; /*************************************************************************** ***************************************************************************/ struct DedupTable * dedup_create() { struct DedupTable *result; result = (struct DedupTable *)malloc(sizeof(*result)); memset(result, 0, sizeof(*result)); return result; } /*************************************************************************** ***************************************************************************/ void dedup_destroy(struct DedupTable *table) { if (table) free(table); } /*************************************************************************** ***************************************************************************/ unsigned dedup_is_duplicate(struct DedupTable *dedup, unsigned ip, unsigned port) { unsigned hash; struct DedupEntry *bucket; unsigned i; /* THREAT: We've already validated resonses via SYN-cookies, so * therefore we don't need a robust hash for duplicate detection */ hash = ip + port ^ (ip>>8) + (ip>>16) ^ (ip>>24); hash &= DEDUP_ENTRIES-1; /* Search in this bucket */ bucket = dedup->entries[hash]; for (i = 0; i < 4; i++) { if (bucket[i].ip == ip && bucket[i].port == port) return 1; } /* We didn't find it, so add it to our list. This will push * older entries at this bucket off the list */ memmove(bucket, bucket+1, 3*sizeof(*bucket)); bucket[0].ip = ip; bucket[0].port = port; return 0; }
src/main-dedup.h 0 → 100644 +9 −0 Original line number Diff line number Diff line #ifndef MAIN_DEDUP_H #define MAIN_DEDUP_H struct DedupTable *dedup_create(); void dedup_destroy(struct DedupTable *table); unsigned dedup_is_duplicate(struct DedupTable *dedup, unsigned ip, unsigned port); #endif
src/main.c +61 −35 Original line number Diff line number Diff line Loading @@ -2,8 +2,11 @@ main This includes the main() function, as well as the inner loop of the scan function 'scanning_thread()'. This includes: * main() * scanning_thread() - transmits packets * main_scan() - launch and receive packets */ #include "masscan.h" Loading @@ -13,6 +16,7 @@ #include "logger.h" /* adjust with -v command-line opt */ #include "main-status.h" /* printf() regular status updates */ #include "main-throttle.h" /* rate limit */ #include "main-dedup.h" /* ignore duplicate responses */ #include "pixie-timer.h" /* portable time functions */ #include "pixie-threads.h" /* portable threads */ Loading Loading @@ -147,7 +151,7 @@ scanning_thread(void *v) * in the configuration file. ***************************************************************************/ static int initialize(struct Masscan *masscan, initialize_adapter(struct Masscan *masscan, unsigned *r_adapter_ip, unsigned char *adapter_mac, unsigned char *router_mac) Loading Loading @@ -292,6 +296,17 @@ initialize(struct Masscan *masscan, return 0; } /*************************************************************************** ***************************************************************************/ const char *status_string(int x) { switch (x) { case Port_Open: return "open"; case Port_Closed: return "closed"; default: return "unknown"; } } /*************************************************************************** * Start the scan. This is the main function of the program. * Called from main() Loading @@ -308,11 +323,12 @@ main_scan(struct Masscan *masscan) unsigned char router_mac[6]; int err; FILE *fpout = stdout; struct DedupTable *dedup; /* * Turn the adapter on, and get the running configuration */ err = initialize( masscan, err = initialize_adapter( masscan, &adapter_ip, adapter_mac, router_mac); Loading Loading @@ -352,17 +368,15 @@ main_scan(struct Masscan *masscan) if (count_ips == 0) { fprintf(stderr, "FAIL: no IPv4 ranges were specified\n"); return 1; } else LOG(2, "range = %u IP addresses\n", count_ips); } count_ports = rangelist_count(&masscan->ports); if (count_ports == 0) { fprintf(stderr, "FAIL: no ports were specified, use \"-p<port>\"\n"); return 1; } else LOG(2, "range = %u ports\n", count_ports); } fprintf(stderr, "Scanning %u hosts [%u ports/host]\n", (unsigned)count_ips, (unsigned)count_ports); fprintf(stderr, "Scanning %u hosts [%u port%s/host]\n", (unsigned)count_ips, (unsigned)count_ports, (count_ports==1)?"":"s"); /* * Initialize LCG translator Loading Loading @@ -405,6 +419,7 @@ main_scan(struct Masscan *masscan) exit(1); } } dedup = dedup_create(); /* * Start the scanning thread. Loading @@ -418,6 +433,7 @@ main_scan(struct Masscan *masscan) * them to the terminal. */ while (!masscan->is_done) { int status; unsigned length; unsigned secs; unsigned usecs; Loading Loading @@ -446,10 +462,12 @@ main_scan(struct Masscan *masscan) x = preprocess_frame(px, length, 1, &parsed); if (!x) continue; /* corrupt packet */ /* verify: my IP address */ dst = parsed.ip_dst[0]<<24 | parsed.ip_dst[1]<<16 | parsed.ip_dst[2]<< 8 | parsed.ip_dst[3]<<0; src = parsed.ip_src[0]<<24 | parsed.ip_src[1]<<16 | parsed.ip_src[2]<< 8 | parsed.ip_src[3]<<0; /* verify: my IP address */ if (adapter_ip != dst) continue; Loading @@ -464,10 +482,6 @@ main_scan(struct Masscan *masscan) if (parsed.found != FOUND_TCP) continue; /* verify: SYN-ACK */ if ((px[parsed.transport_offset+13] & 0x12) != 0x12) continue; /* verify: my IP address */ dst = parsed.ip_dst[0]<<24 | parsed.ip_dst[1]<<16 | parsed.ip_dst[2]<< 8 | parsed.ip_dst[3]<<0; Loading @@ -478,17 +492,28 @@ main_scan(struct Masscan *masscan) if (adapter_port != parsed.port_dst) continue; /* verify: ignore duplicates */ if (dedup_is_duplicate(dedup, src, parsed.port_src)) continue; /* figure out the status */ status = Port_Unknown; if ((px[parsed.transport_offset+13] & 0x2) == 0x2) status = Port_Open; if ((px[parsed.transport_offset+13] & 0x4) == 0x4) status = Port_Closed; /* * XXXX * TODO: add lots more verification, such as coming from one of * our sending port numbers, and having the right seqno/ackno * fields set. */ src = parsed.ip_src[0]<<24 | parsed.ip_src[1]<<16 | parsed.ip_src[2]<< 8 | parsed.ip_src[3]<<0; switch (masscan->nmap.format) { case Output_Interactive: fprintf(fpout, "Discovered open port %u/tcp on %u.%u.%u.%u \n", fprintf(fpout, "Discovered %s port %u/tcp on %u.%u.%u.%u \n", status_string(status), parsed.port_src, (src>>24)&0xFF, (src>>16)&0xFF, Loading @@ -497,7 +522,8 @@ main_scan(struct Masscan *masscan) ); break; case Output_List: fprintf(fpout, "open tcp %u %u.%u.%u.%u\n", fprintf(fpout, "%s tcp %u %u.%u.%u.%u\n", status_string(status), parsed.port_src, (src>>24)&0xFF, (src>>16)&0xFF, Loading
src/masscan.h +6 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,12 @@ enum OutpuFormat { Output_List /* specific to Masscan */ }; enum PortStatus { Port_Unknown, Port_Open, Port_Closed, }; struct Masscan { int op; Loading