From e45898289294c5cf8f7bcd283a93316e8e903c0c Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Sep 2013 17:55:06 -0400 Subject: [PATCH 1/6] Add fs_modify_word to fieldset You may modify an existing word to be a different value; useful in UDP module for ICMP unreachable responses, so we can update the "source" address --- src/fieldset.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/fieldset.h | 14 ++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/fieldset.c b/src/fieldset.c index 34c9fdf..eb5d27e 100644 --- a/src/fieldset.c +++ b/src/fieldset.c @@ -50,6 +50,26 @@ static inline void fs_add_word(fieldset_t *fs, const char *name, int type, f->free_ = free_; } +static void fs_modify_word(fieldset_t *fs, const char *name, int type, + int free_, size_t len, void *value) +{ + int i; + for (i=0; ilen; i++) { + if (!strcmp(fs->fields[i].name, name)) { + if (fs->fields[i].free_) { + free((void*)fs->fields[i].value); + fs->fields[i].value = 0; + } + fs->fields[i].type = type; + fs->fields[i].free_ = free_; + fs->fields[i].len = len; + fs->fields[i].value = (uint64_t)value; + return; + } + } + fs_add_word(fs, name, type, free_, len, value); +} + void fs_add_null(fieldset_t *fs, const char *name) { fs_add_word(fs, name, FS_NULL, 0, 0, NULL); @@ -71,6 +91,28 @@ void fs_add_binary(fieldset_t *fs, const char *name, size_t len, fs_add_word(fs, name, FS_BINARY, free_, len, value); } +// Modify +void fs_modify_null(fieldset_t *fs, const char *name) +{ + fs_modify_word(fs, name, FS_NULL, 0, 0, NULL); +} + +void fs_modify_string(fieldset_t *fs, const char *name, char *value, int free_) +{ + fs_modify_word(fs, name, FS_STRING, free_, strlen(value), (void*) value); +} + +void fs_modify_uint64(fieldset_t *fs, const char *name, uint64_t value) +{ + fs_modify_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), (void*) value); +} + +void fs_modify_binary(fieldset_t *fs, const char *name, size_t len, + void *value, int free_) +{ + fs_modify_word(fs, name, FS_BINARY, free_, len, value); +} + uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index) { return (uint64_t) fs->fields[index].value; diff --git a/src/fieldset.h b/src/fieldset.h index 9d49ba3..66de892 100644 --- a/src/fieldset.h +++ b/src/fieldset.h @@ -74,6 +74,8 @@ int fds_get_index_by_name(fielddefset_t *fds, char *name); void gen_fielddef_set(fielddefset_t *fds, fielddef_t fs[], int len); +void fs_add_null(fieldset_t *fs, const char *name); + void fs_add_uint64(fieldset_t *fs, const char *name, uint64_t value); void fs_add_string(fieldset_t *fs, const char *name, char *value, int free_); @@ -81,9 +83,17 @@ void fs_add_string(fieldset_t *fs, const char *name, char *value, int free_); void fs_add_binary(fieldset_t *fs, const char *name, size_t len, void *value, int free_); -uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index); +// Modify +void fs_modify_null(fieldset_t *fs, const char *name); -void fs_add_null(fieldset_t *fs, const char *name); +void fs_modify_uint64(fieldset_t *fs, const char *name, uint64_t value); + +void fs_modify_string(fieldset_t *fs, const char *name, char *value, int free_); + +void fs_modify_binary(fieldset_t *fs, const char *name, size_t len, + void *value, int free_); + +uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index); void fs_free(fieldset_t *fs); From ee9593938ba3277931ddd3d622d5769343a97bdc Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Sep 2013 17:57:32 -0400 Subject: [PATCH 2/6] Move make_ip_str to probe_modules/packet.c This is a utility function, mostly useful for probe modules --- src/probe_modules/packet.c | 14 ++++++++++++++ src/probe_modules/packet.h | 3 +++ src/probe_modules/probe_modules.c | 12 +----------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/probe_modules/packet.c b/src/probe_modules/packet.c index 0ff752a..47d251f 100644 --- a/src/probe_modules/packet.c +++ b/src/probe_modules/packet.c @@ -20,6 +20,7 @@ #include #include +#include #include "state.h" @@ -121,3 +122,16 @@ void make_udp_header(struct udphdr *udp_header, port_h_t dest_port, udp_header->check = 0; } +// Note: caller must free return value +char *make_ip_str(uint32_t ip) +{ + struct in_addr t; + t.s_addr = ip; + const char *temp = inet_ntoa(t); + char *retv = malloc(strlen(temp)+1); + assert (retv); + strcpy(retv, temp); + return retv; +} + + diff --git a/src/probe_modules/packet.h b/src/probe_modules/packet.h index 1db5d6d..77f3827 100644 --- a/src/probe_modules/packet.h +++ b/src/probe_modules/packet.h @@ -102,4 +102,7 @@ static __attribute__((unused)) inline uint16_t get_src_port(int num_ports, return zconf.source_port_first + ((validation[1] + probe_num) % num_ports); } +// Note: caller must free return value +char *make_ip_str(uint32_t ip); + #endif diff --git a/src/probe_modules/probe_modules.c b/src/probe_modules/probe_modules.c index fdab124..f28d9bf 100644 --- a/src/probe_modules/probe_modules.c +++ b/src/probe_modules/probe_modules.c @@ -21,6 +21,7 @@ #include "../../lib/logger.h" #include "../fieldset.h" #include "probe_modules.h" +#include "packet.h" extern probe_module_t module_tcp_synscan; extern probe_module_t module_icmp_echo; @@ -53,17 +54,6 @@ void print_probe_modules(void) } } -char *make_ip_str(uint32_t ip) -{ - struct in_addr t; - t.s_addr = ip; - const char *temp = inet_ntoa(t); - char *retv = malloc(strlen(temp)+1); - assert (retv); - strcpy(retv, temp); - return retv; -} - void fs_add_ip_fields(fieldset_t *fs, struct iphdr *ip) { fs_add_string(fs, "saddr", make_ip_str(ip->saddr), 1); From d694fe69a65052c2039518aa29678c70b737e050 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Sep 2013 17:58:20 -0400 Subject: [PATCH 3/6] UDP probe module updates saddr fieldset ICMP responses (e.g. DEST_UNREACH) to UDP probes (generally) contain the IP/UDP packet that ellicited the response, though they do not have to come from the host we sent them to (e.g. NETWORK_UNREACH). We will "correct" this by switching the saddr fieldset to be the host we WOULD have received a response from --- src/probe_modules/module_udp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/probe_modules/module_udp.c b/src/probe_modules/module_udp.c index 3f78819..19ab6dd 100644 --- a/src/probe_modules/module_udp.c +++ b/src/probe_modules/module_udp.c @@ -207,6 +207,10 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_binary(fs, "data", (ntohs(udp->len) - sizeof(struct udphdr)), (void*) &udp[1], 0); } else if (ip_hdr->protocol == IPPROTO_ICMP) { struct icmphdr *icmp = (struct icmphdr *)((char *)ip_hdr + ip_hdr->ihl * 4); + struct iphdr *ip_inner = (struct iphdr*)&icmp[1]; + // ICMP unreach comes from another server (not the one we sent a probe to); + // But we will fix up saddr to be who we sent the probe to, in case you care. + fs_modify_string(fs, "saddr", make_ip_str(ip_inner->daddr), 1); fs_add_string(fs, "classification", (char*) "icmp-unreach", 0); fs_add_uint64(fs, "success", 0); fs_add_null(fs, "sport"); From 754e2dc1f7d7ccf74f6680dcbca78d14068985bd Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Sep 2013 18:14:58 -0400 Subject: [PATCH 4/6] Add icmp_responder to UDP fieldset icmp_responder is the source address of the DEST_UNREACH ICMP packet, which is not necessarily the same as the saddr (e.g. NETWORK_UNREACH) --- src/probe_modules/module_udp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/probe_modules/module_udp.c b/src/probe_modules/module_udp.c index 19ab6dd..873e275 100644 --- a/src/probe_modules/module_udp.c +++ b/src/probe_modules/module_udp.c @@ -202,6 +202,7 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_uint64(fs, "success", 1); fs_add_uint64(fs, "sport", ntohs(udp->source)); fs_add_uint64(fs, "dport", ntohs(udp->dest)); + fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); fs_add_binary(fs, "data", (ntohs(udp->len) - sizeof(struct udphdr)), (void*) &udp[1], 0); @@ -215,6 +216,7 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_uint64(fs, "success", 0); fs_add_null(fs, "sport"); fs_add_null(fs, "dport"); + fs_add_string(fs, "icmp_responder", make_ip_str(ip_hdr->saddr), 1); fs_add_uint64(fs, "icmp_type", icmp->type); fs_add_uint64(fs, "icmp_code", icmp->code); fs_add_null(fs, "data"); @@ -223,6 +225,7 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_uint64(fs, "success", 0); fs_add_null(fs, "sport"); fs_add_null(fs, "dport"); + fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); fs_add_null(fs, "data"); @@ -256,7 +259,7 @@ int udp_validate_packet(const struct iphdr *ip_hdr, uint32_t len, if (icmp->type != ICMP_DEST_UNREACH) { return 0; } - + struct iphdr *ip_inner = (struct iphdr*)&icmp[1]; // Now we know the actual inner ip length, we should recheck the buffer if (len < 4*ip_inner->ihl - sizeof(struct iphdr) + min_len) { @@ -266,7 +269,7 @@ int udp_validate_packet(const struct iphdr *ip_hdr, uint32_t len, struct udphdr *udp = (struct udphdr *)((char*)ip_inner + 4*ip_inner->ihl); sport = ntohs(udp->source); - dport = ntohs(udp->dest); + dport = ntohs(udp->dest); } else { return 0; } @@ -284,6 +287,7 @@ static fielddef_t fields[] = { {.name = "success", .type="int", .desc = "is response considered success"}, {.name = "sport", .type = "int", .desc = "UDP source port"}, {.name = "dport", .type = "int", .desc = "UDP destination port"}, + {.name = "icmp_responder", .type = "string", .desc = "Source IP of ICMP_UNREACH message"}, {.name = "icmp_type", .type = "int", .desc = "icmp message type"}, {.name = "icmp_code", .type = "int", .desc = "icmp message sub type code"}, {.name = "data", .type="binary", .desc = "UDP payload"} From 2198295c7130b0f6fe8111ceeb337a9697f55bc5 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Sep 2013 19:00:22 -0400 Subject: [PATCH 5/6] Adding icmp unreachable strings (udp_unreach_str) --- src/probe_modules/module_udp.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/probe_modules/module_udp.c b/src/probe_modules/module_udp.c index 873e275..8bb8b99 100644 --- a/src/probe_modules/module_udp.c +++ b/src/probe_modules/module_udp.c @@ -36,6 +36,24 @@ int udp_send_msg_len = 0; const char *udp_send_msg_default = "GET / HTTP/1.1\r\nHost: www\r\n\r\n"; +const char *udp_unreach_strings[] = { +"network unreachable", +"host unreachable", +"protocol unreachable", +"port unreachable", +"fragments required", +"source route failed", +"network unknown", +"host unknown", +"source host isolated", +"network admin. prohibited", +"host admin. prohibited", +"network unreachable TOS", +"host unreachable TOS", +"communication admin. prohibited", +"host presdence violation", +"precedence cutoff"}; + static int num_ports; probe_module_t module_udp; @@ -205,6 +223,7 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); + fs_add_null(fs, "icmp_unreach_str"); fs_add_binary(fs, "data", (ntohs(udp->len) - sizeof(struct udphdr)), (void*) &udp[1], 0); } else if (ip_hdr->protocol == IPPROTO_ICMP) { struct icmphdr *icmp = (struct icmphdr *)((char *)ip_hdr + ip_hdr->ihl * 4); @@ -219,6 +238,11 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_string(fs, "icmp_responder", make_ip_str(ip_hdr->saddr), 1); fs_add_uint64(fs, "icmp_type", icmp->type); fs_add_uint64(fs, "icmp_code", icmp->code); + if (icmp->code <= ICMP_PREC_CUTOFF) { + fs_add_string(fs, "icmp_unreach_str", (char *)udp_unreach_strings[icmp->code], 0); + } else { + fs_add_string(fs, "icmp_unreach_str", (char *)"unknown", 0); + } fs_add_null(fs, "data"); } else { fs_add_string(fs, "classification", (char*) "other", 0); @@ -228,6 +252,7 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); + fs_add_null(fs, "icmp_unreach_str"); fs_add_null(fs, "data"); } } @@ -290,6 +315,7 @@ static fielddef_t fields[] = { {.name = "icmp_responder", .type = "string", .desc = "Source IP of ICMP_UNREACH message"}, {.name = "icmp_type", .type = "int", .desc = "icmp message type"}, {.name = "icmp_code", .type = "int", .desc = "icmp message sub type code"}, + {.name = "icmp_unreach_str", .type = "string", .desc = "for icmp_unreach responses, the string version of icmp_code (e.g. network-unreach)"}, {.name = "data", .type="binary", .desc = "UDP payload"} }; @@ -307,6 +333,6 @@ probe_module_t module_udp = { .process_packet = &udp_process_packet, .close = &udp_global_cleanup, .fields = fields, - .numfields = sizeof(fields)/sizeof(fields[0]) + .numfields = sizeof(fields)/sizeof(fields[0]) }; From 6c74ce683733c1398677ca8a77edb90d80498189 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 3 Sep 2013 16:02:27 -0400 Subject: [PATCH 6/6] Update man page to include fields and also update file paths --- src/zmap.1 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/zmap.1 b/src/zmap.1 index 0ab49af..3582c07 100644 --- a/src/zmap.1 +++ b/src/zmap.1 @@ -24,7 +24,7 @@ File of subnets to exclude, in CIDR notation (e.g. 192.168.0.0/16), one-per line. It is recommended you use this to exclude RFC 1918 addresses, multicast, IANA reserved space, and other IANA special-purpose addresses. An example blacklist file is provided in -.B conf/blacklist.example +.B conf/blacklist.conf for this purpose. .TP .B -w, --whitelist-file=path @@ -98,6 +98,11 @@ Select probe module (default=tcp_synscan) .TP .B \-O, --output-module=name Select output module (default=simple_file) +.TP +.B \-f, --output-fields=fields +Fields that should be output in result set; see +.B --list-output-fields + .TP .B --probe-args=args Arguments to pass to probe module @@ -110,12 +115,18 @@ List available output modules .TP .B --list-probe-modules List available probe modules +.TP +.B --list-output-fields +List all fields that can be output (using +.B --output-fields +) +by selected probe module .SS "Additional options" .TP .B \-C, --config=filename Read a configuration file, which can specify -any of these options (default=zmap.conf) +any of these options (default=/etc/zmap/zmap.conf) .TP .B \-q, --quiet Do not print status updates