added Eddie Bell's traceroute patch, with only minor changes

This commit is contained in:
fyodor 2007-03-13 08:02:19 +00:00
parent 68c94c5cfd
commit e882edf484
12 changed files with 238 additions and 22 deletions

View file

@ -14,6 +14,14 @@ o Integrated all of your Q32006 service fingerprint submissions. The
thanks to version detection czar Doug Hoyte for doing this. Notable
changes are described at http://hcsw.org/blog.pl?a=20&b=20 .
o Nmap now has traceroute support, thanks to an excellent patch by
Eddie Bell. The new system uses Nmap data to determine which sort of
packets are most likely to slip through the target network and
produce useful results. The system is well optimized for speed and
bandwidth efficiency, and the clever output system avoids repeating
the same initial hops for each target system. Enable this
functionality by specifying --traceroute.
o Nmap now has a public Subversion (SVN) source code repository. See
the announcement at http://seclists.org/nmap-dev/2006/q4/0253.html
and then the updated usage instructions at

View file

@ -50,11 +50,11 @@ TARGETNMAPFE=@TARGETNMAPFE@
INSTALLNMAPFE=@INSTALLNMAPFE@
INSTALLNSE=@INSTALLNSE@
export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc osscan2.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_tty.cc nmap_dns.cc nse_main.cc nse_auxiliar.cc nse_nsock.cc nse_init.cc nse_nmaplib.cc nse_debug.cc nse_bitlib.cc nse_pcrelib.cc nse_string.cc @COMPAT_SRCS@
export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc osscan2.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_tty.cc nmap_dns.cc nse_main.cc nse_auxiliar.cc nse_nsock.cc nse_init.cc nse_nmaplib.cc nse_debug.cc nse_bitlib.cc nse_pcrelib.cc nse_string.cc traceroute.cc @COMPAT_SRCS@
export HDRS = charpool.h FingerPrintResults.h global_structures.h idle_scan.h MACLookup.h nmap_amigaos.h nmap_dns.h nmap_error.h nmap.h NmapOps.h NmapOutputTable.h nmap_rpc.h nmap_tty.h nmap_winconfig.h osscan.h osscan2.h output.h portlist.h protocols.h scan_engine.h service_scan.h services.h TargetGroup.h Target.h targets.h tcpip.h timing.h utils.h nse_main.h nse_auxiliar.h nse_nsock.h nse_init.h nse_nmaplib.h nse_debug.h nse_macros.h nse_bitlib.h nse_pcrelib.h nse_string.h
export HDRS = charpool.h FingerPrintResults.h global_structures.h idle_scan.h MACLookup.h nmap_amigaos.h nmap_dns.h nmap_error.h nmap.h NmapOps.h NmapOutputTable.h nmap_rpc.h nmap_tty.h nmap_winconfig.h osscan.h osscan2.h output.h portlist.h protocols.h scan_engine.h service_scan.h services.h TargetGroup.h Target.h targets.h tcpip.h timing.h utils.h nse_main.h nse_auxiliar.h nse_nsock.h nse_init.h nse_nmaplib.h nse_debug.h nse_macros.h nse_bitlib.h nse_pcrelib.h nse_string.h traceroute.h
OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o osscan2.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o nmap_tty.o nmap_dns.o nse_main.o nse_auxiliar.o nse_nsock.o nse_init.o nse_nmaplib.o nse_debug.o nse_bitlib.o nse_pcrelib.o nse_string.o @COMPAT_OBJS@
OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o osscan2.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o nmap_tty.o nmap_dns.o nse_main.o nse_auxiliar.o nse_nsock.o nse_init.o nse_nmaplib.o nse_debug.o nse_bitlib.o nse_pcrelib.o nse_string.o traceroute.o @COMPAT_OBJS@
# %.o : %.cc -- nope this is a GNU extension
.cc.o:
@ -110,11 +110,11 @@ debug:
# Make the Nmap tarball
distro:
cd dev && $(MAKE) distro
cd ../nmap-dev && $(MAKE) distro
# Update the web site
web:
cd dev && $(MAKE) web
cd ../nmap-dev && $(MAKE) web
clean: @LUA_CLEAN@ @PCAP_CLEAN@ @PCRE_CLEAN@ @DNET_CLEAN@ nmapfe_clean nsock_clean nbase_clean my_clean

View file

@ -301,6 +301,7 @@ class NmapOps {
int resolve_all;
char *dns_servers;
bool log_errors;
bool traceroute;
#ifndef NOLUA
int script;

View file

@ -231,11 +231,11 @@ class Target {
int wierd_responses; /* echo responses from other addresses, Ie a network broadcast address */
unsigned int flags; /* HOST_UP, HOST_DOWN, HOST_FIREWALLED, HOST_BROADCAST (instead of HOST_BROADCAST use wierd_responses */
struct timeout_info to;
char *hostname; // Null if unable to resolve or unset
ScriptResults scriptResults;
private:
char *hostname; // Null if unable to resolve or unset
void Initialize();
void FreeInternal(); // Free memory allocated inside this object
// Creates a "presentation" formatted string out of the IPv4/IPv6 address

View file

@ -2,7 +2,7 @@
.\" It was generated using the DocBook XSL Stylesheets (version 1.69.1).
.\" Instead of manually editing it, you probably should edit the DocBook XML
.\" source for it and then use the DocBook XSL Stylesheets to regenerate it.
.TH "NMAP" "1" "03/11/2007" "" "Nmap Reference Guide"
.TH "NMAP" "1" "03/12/2007" "" "Nmap Reference Guide"
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
@ -669,6 +669,25 @@ option.
By default, Nmap randomizes the scanned port order (except that certain commonly accessible ports are moved near the beginning for efficiency reasons). This randomization is normally desirable, but you can specify
\fB\-r\fR
for sequential port scanning instead.
.TP
\fB\-\-traceroute\fR (Trace path to host)
Traceroutes are performed post\-scan and they uses information from the scan results to determines a valid responsive port and protocol to trace to. It works with all scan types apart from connect scans (\-sT) and idle scans (\-sI). All traces use nmap's dynamic timing model and are performed in parallel.
.sp
Traceroute works by sending packets with a low TTL (time\-to\-live) in an attempt to illicit ICMP TTL_EXCCEDED messages from intermediate hops between the scanner and the target host. Standard traceroute implementation start with a TTL of 1 and increment the TTL until the destination host is reached. Nmap's traceroute starts with a high TTL and then decrements the TTL until it reaches 0. Doing it backwards means nmap can employ clever caching algorithms to speed up traces over multiple hosts. On average nmap sends 5\-10 less packet per host, depending on network condition. If a related subnet is being scanned (I.E 192.168.0.0/24) nmap may only have to send a single packet to the majority of hosts.
.sp
The output from each trace is consolidated to save space, There are two type of consolidation timed out and reference trace. A reference trace can be considered a cache of hops nmap has seen so far.
Example\ 14.2.\ Timeout consolidation.sp
.nf
1 ... Hop 1 timed out
1 ... 6 Hop 1 to 6 timed out
.fi
Example\ 14.3.\ Reference consolidation (hop cache).sp
.nf
1 \-\- Hop 1 has been taken from the reference trace
1 \-\-> 6 Hop 1 to 6 have been taken from the reference trace
.fi
.SH "SERVICE AND VERSION DETECTION"
.PP
Point Nmap at a remote machine and it might tell you that ports 25/tcp, 80/tcp, and 53/udp are open. Using its

View file

@ -131,7 +131,8 @@
-->
<!ELEMENT host ( status, address , (address | hostnames |
smurf | ports | os | distance | uptime |
tcpsequence | ipidsequence | tcptssequence | hostscript)* ) >
tcpsequence | ipidsequence | tcptssequence |
hostscript | trace)* ) >
<!-- these elements are written by output.c:write_xml_initial_hostinfo() -->
<!ELEMENT status EMPTY >
@ -264,6 +265,25 @@
values CDATA #IMPLIED
>
<!ELEMENT trace (hop*, error?) >
<!ATTLIST trace
proto CDATA #REQUIRED
port CDATA #REQUIRED
>
<!ELEMENT hop EMPTY>
<!ATTLIST hop
ttl CDATA #REQUIRED
rtt CDATA #IMPLIED
ipaddr CDATA #IMPLIED
host CDATA #IMPLIED
>
<!ELEMENT error EMPTY>
<!ATTLIST error
errorstr CDATA #IMPLIED
>
<!-- these elements are generated in output.c:printfinaloutput() -->
<!ELEMENT runstats (finished, hosts) >

View file

@ -690,6 +690,57 @@
</xsl:template>
<!-- ............................................................ -->
<!-- traceroute -->
<!-- ............................................................ -->
<xsl:template match="trace">
<h3>traceroute</h3>
<ul>
<li>port: <xsl:value-of select="@port" /></li>
<li>proto: <xsl:value-of select="@proto" /></li>
<xsl:for-each select="error">
<li>error: <xsl:value-of select="@errorstr"/></li>
</xsl:for-each>
</ul>
<table cellspacing="1">
<tr class="head">
<td>Hop</td>
<td>Rtt</td>
<td>IP</td>
<td>Host</td>
</tr>
<xsl:for-each select="hop">
<xsl:choose>
<xsl:when test="@rtt = '--'">
<tr class="filtered">
<td><xsl:value-of select="@ttl" /></td>
<td>--</td>
<td><xsl:value-of select="@ipaddr" /></td>
<td><xsl:value-of select="@host" /></td>
</tr>
</xsl:when>
<xsl:when test="@rtt > 0">
<tr class="open">
<td><xsl:value-of select="@ttl" /></td>
<td><xsl:value-of select="@rtt" /></td>
<td><xsl:value-of select="@ipaddr" /></td>
<td><xsl:value-of select="@host" /></td>
</tr>
</xsl:when>
<xsl:otherwise>
<tr class="closed">
<td><xsl:value-of select="@ttl" /></td>
<td></td><td></td><td></td>
</tr>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</xsl:template>
<!-- ............................................................ -->
<!-- ipidsequence -->
<!-- ............................................................ -->
<xsl:template match="ipidsequence">

View file

@ -242,6 +242,6 @@ struct scan_lists {
int prot_count;
};
typedef enum { STYPE_UNKNOWN, HOST_DISCOVERY, ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN, PING_SCAN, PING_SCAN_ARP, IDLE_SCAN, BOUNCE_SCAN, SERVICE_SCAN, OS_SCAN, SCRIPT_SCAN} stype;
typedef enum { STYPE_UNKNOWN, HOST_DISCOVERY, ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN, PING_SCAN, PING_SCAN_ARP, IDLE_SCAN, BOUNCE_SCAN, SERVICE_SCAN, OS_SCAN, SCRIPT_SCAN, TRACEROUTE, REF_TRACEROUTE}stype;
#endif /*GLOBAL_STRUCTURES_H */

60
nmap.cc
View file

@ -107,6 +107,7 @@
#include "timing.h"
#include "NmapOps.h"
#include "MACLookup.h"
#include "Traceroute.h"
#include "nmap_tty.h"
#include "nmap_dns.h"
#include "nse_main.h"
@ -225,6 +226,7 @@ printf("%s %s ( %s )\n"
" -sI <zombie host[:probeport]>: Idlescan\n"
" -sO: IP protocol scan\n"
" -b <ftp relay host>: FTP bounce scan\n"
" --traceroute: Trace hop path to each host\n"
"PORT SPECIFICATION AND SCAN ORDER:\n"
" -p <port ranges>: Only scan specified ports\n"
" Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080\n"
@ -461,6 +463,7 @@ int nmap_main(int argc, char *argv[]) {
char *endptr = NULL;
struct scan_lists *ports = NULL;
TargetGroup *exclude_group = NULL;
Traceroute *troute = NULL;
char myname[MAXHOSTNAMELEN + 1];
#if (defined(IN_ADDR_DEEPSTRUCT) || defined( SOLARIS))
/* Note that struct in_addr in solaris is 3 levels deep just to store an
@ -584,6 +587,7 @@ int nmap_main(int argc, char *argv[]) {
{"thc", no_argument, 0, 0},
{"badsum", no_argument, 0, 0},
{"ttl", required_argument, 0, 0}, /* Time to live */
{"traceroute", no_argument, 0, 0},
{"allports", no_argument, 0, 0},
{"version_intensity", required_argument, 0, 0},
{"version-intensity", required_argument, 0, 0},
@ -833,6 +837,8 @@ int nmap_main(int argc, char *argv[]) {
fatal("Ip options can't be more than 40 bytes long");
if(o.ipoptionslen %4 != 0)
fatal("Ip options must be multiple of 4 (read length is %i bytes)", o.ipoptionslen);
} else if(strcmp(long_options[option_index].name, "traceroute") == 0) {
o.traceroute = true;
} else {
fatal("Unknown long option (%s) given@#!$#$", long_options[option_index].name);
}
@ -1186,6 +1192,17 @@ int nmap_main(int argc, char *argv[]) {
}
}
if(o.traceroute && (o.idlescan || o.connectscan)) {
error("Warning: Traceroute does not support idle or connect scan, disabling...\n");
o.traceroute = 0;
}
if(o.traceroute && !o.isr00t) {
error("Warning: Traceroute has to be run as root, disabling...\n");
o.traceroute = 0;
}
if ((o.pingscan || o.listscan) && (portlist || fastscan)) {
fatal("You cannot use -F (fast scan) or -p (explicit port selection) with PING scan or LIST scan");
}
@ -1496,7 +1513,7 @@ int nmap_main(int argc, char *argv[]) {
if (currenths->flags & HOST_UP && !o.listscan)
o.numhosts_up++;
if (o.pingscan || o.listscan) {
if ((o.pingscan && !o.traceroute) || o.listscan) {
/* We're done with the hosts */
log_write(LOG_XML, "<host>");
write_host_status(currenths, o.resolve_all);
@ -1569,6 +1586,33 @@ int nmap_main(int argc, char *argv[]) {
if (o.af() == AF_INET && o.RawScan())
o.decoys[o.decoyturn] = Targets[0]->v4source();
/* ping scan traceroutes */
if(o.traceroute && o.pingscan) {
/* Assume that all targets in a group use the same device */
troute = new Traceroute(Targets[0]->deviceName(), Targets[0]->ifType());
troute->trace(Targets);
troute->resolveHops();
/* print ping traceroutes, making sure the reference
* trace is first */
while(!Targets.empty()) {
currenths = *Targets.begin();
log_write(LOG_XML, "<host>");
write_host_status(currenths, o.resolve_all);
printmacinfo(currenths);
troute->outputTarget(currenths);
log_write(LOG_XML, "</host>\n");
log_write(LOG_STDOUT|LOG_SKID|LOG_PLAIN,"\n");
delete currenths;
Targets.erase(Targets.begin());
}
o.numhosts_scanning = 0;
log_flush_all();
delete troute;
troute = NULL;
continue;
}
/* I now have the group for scanning in the Targets vector */
// Ultra_scan sets o.scantype for us so we don't have to worry
@ -1633,6 +1677,12 @@ int nmap_main(int argc, char *argv[]) {
if (o.osscan == OS_SCAN_DEFAULT || o.osscan == OS_SCAN_SYS_2_ONLY)
os_scan2(Targets);
if(o.traceroute) {
troute = new Traceroute(Targets[0]->deviceName(), Targets[0]->ifType());
troute->trace(Targets);
troute->resolveHops();
}
for(targetno = 0; targetno < Targets.size(); targetno++) {
currenths = Targets[targetno];
@ -1673,6 +1723,9 @@ int nmap_main(int argc, char *argv[]) {
#endif
}
if(troute)
troute->outputTarget(currenths);
if (o.debugging)
log_write(LOG_STDOUT, "Final times for host: srtt: %d rttvar: %d to: %d\n",
currenths->to.srtt, currenths->to.rttvar, currenths->to.timeout);
@ -1690,6 +1743,9 @@ int nmap_main(int argc, char *argv[]) {
o.numhosts_scanning = 0;
} while(!o.max_ips_to_scan || o.max_ips_to_scan > o.numhosts_scanned);
if(troute)
delete troute;
delete hstate;
if (exclude_group)
delete[] exclude_group;
@ -2171,6 +2227,8 @@ char *scantype2str(stype scantype) {
case SERVICE_SCAN: return "Service Scan"; break;
case OS_SCAN: return "OS Scan"; break;
case SCRIPT_SCAN: return "Script Scan"; break;
case TRACEROUTE: return "Traceroute" ; break;
case REF_TRACEROUTE: return "Reference Traceroute"; break;
default: assert(0); break;
}

View file

@ -142,6 +142,9 @@
// doug at hcsw.org
// http://www.hcsw.org
/*
* DNS Caching and ageing added by Eddie Bell ejlbell@gmail.com 2007
*/
// TODO:
//
@ -158,6 +161,7 @@
#include <limits.h>
#include <list>
#include <vector>
#include <algorithm>
#include "nmap.h"
#include "NmapOps.h"
@ -267,6 +271,7 @@ struct request_s {
struct host_elem_s {
char *name;
u32 addr;
u8 cache_hits;
};
@ -292,13 +297,13 @@ static ScanProgressMeter *SPM;
//------------------- Prototypes and macros ---------------------
static void put_dns_packet_on_wire(request *req);
static char *lookup_etchosts(u32 ip);
static void addto_etchosts(u32 ip, const char *hname);
#define ACTION_FINISHED 0
#define ACTION_CNAME_LIST 1
#define ACTION_TIMEOUT 2
//------------------- Misc code ---------------------
static void output_summary() {
@ -370,7 +375,8 @@ static void do_possible_writes() {
}
if (tpreq) {
if (o.debugging >= TRACE_DEBUG_LEVEL) log_write(LOG_STDOUT, "mass_rdns: TRANSMITTING for <%s> (server <%s>)\n", tpreq->targ->targetipstr() , tpserv->hostname);
if (o.debugging >= TRACE_DEBUG_LEVEL)
log_write(LOG_STDOUT, "mass_rdns: TRANSMITTING for <%s> (server <%s>)\n", tpreq->targ->targetipstr() , tpserv->hostname);
stat_trans++;
put_dns_packet_on_wire(tpreq);
}
@ -398,7 +404,6 @@ static void put_dns_packet_on_wire(request *req) {
struct timeval now, timeout;
ip = (u32) ntohl(req->targ->v4host().s_addr);
packet[0] = (req->id >> 8) & 0xFF;
packet[1] = req->id & 0xFF;
plen += 2;
@ -527,13 +532,18 @@ static int process_result(u32 ia, char *result, int action, u16 id) {
if (id == tpreq->id) {
if (ia != 0 && (u32) (tpreq->targ->v4host().s_addr) != ia) continue;
if (ia != 0 && tpreq->targ->v4host().s_addr != ia)
continue;
if (action == ACTION_CNAME_LIST || action == ACTION_FINISHED) {
tpserv->capacity += CAPACITY_UP_STEP;
check_capacities(tpserv);
if (result) tpreq->targ->setHostName(result);
if (result) {
tpreq->targ->setHostName(result);
addto_etchosts(tpreq->targ->v4hostip()->s_addr, result);
}
tpserv->in_process.remove(tpreq);
tpserv->reqs_on_wire--;
@ -977,6 +987,7 @@ static void parse_etchosts(char *fname) {
he = new host_elem;
he->name = strdup(hname);
he->addr = (u32) ia.s_addr;
he->cache_hits = 0;
etchosts[he->addr % HASH_TABLE_SIZE].push_front(he);
}
}
@ -1002,19 +1013,66 @@ void free_etchosts() {
}
}
/* Executed when the DNS cache is full, ages entries
* and removes any with a cache hit of 0 (the least used) */
bool remove_and_age(host_elem *host) {
if(host->cache_hits) {
host->cache_hits /=2;
return false;
} else
return true;
}
/* Add to the dns cache. If there is no space we age and
* remove the least frequently used entries until there
* is space */
static void addto_etchosts(u32 ip, const char *hname) {
static u16 total_size = 0;
std::list<host_elem*>::iterator it;
host_elem *he;
int i;
if(lookup_etchosts(ip) != NULL)
return;
while(total_size >= HASH_TABLE_SIZE) {
for(i = 0; i < HASH_TABLE_SIZE; i++) {
while((it = find_if(etchosts[i].begin(), etchosts[i].end(), remove_and_age)) != etchosts[i].end()) {
etchosts[i].erase(it);
total_size--;
}
}
}
he = new host_elem;
he->name = strdup(hname);
he->addr = ip;
he->cache_hits = 0;
etchosts[ip % HASH_TABLE_SIZE].push_back(he);
total_size++;
}
/* Search for a hostname in the cache and increment
* its cache hit counter if found */
static char *lookup_etchosts(u32 ip) {
std::list<host_elem *>::iterator hostI;
host_elem *tpelem;
for(hostI = etchosts[ip % HASH_TABLE_SIZE].begin(); hostI != etchosts[ip % HASH_TABLE_SIZE].end(); hostI++) {
tpelem = *hostI;
if (tpelem->addr == ip) return tpelem->name;
if (tpelem->addr == ip) {
if(tpelem->cache_hits < UCHAR_MAX)
tpelem->cache_hits++;
return tpelem->name;
}
}
return NULL;
}
/* External interface to dns cache */
const char *lookup_cached_host(u32 ip) {
const char *tmp = lookup_etchosts(ip);
return tmp==NULL?"":tmp;
}
static void etchosts_init(void) {
static int initialized = 0;
@ -1119,7 +1177,7 @@ static void nmap_mass_rdns_core(Target **targets, int num_targets) {
for(hostI = targets; hostI < targets+num_targets; hostI++) {
if (!((*hostI)->flags & HOST_UP) && !o.resolve_all) continue;
// See if it's in /etc/hosts
// See if it's in /etc/hosts or cached
tpname = lookup_etchosts((u32) (*hostI)->v4hostip()->s_addr);
if (tpname) {
(*hostI)->setHostName(tpname);

View file

@ -98,7 +98,8 @@
#include "Target.h"
void nmap_mass_rdns(Target ** targets, int num_targets);
void free_dns_servers();
const char *lookup_cached_host(u32 ip);
void free_dns_servers();
void free_etchosts();

View file

@ -1417,11 +1417,11 @@ char *ping = (char *) &pingpkt;
icmplen = 20;
memset(datastart, 0, 12);
datastart += 12;
datalen -= 12;
//datalen -= 12;
} else if (ptype == 17 && pcode == 0) /* icmp netmask req */ {
icmplen = 12;
*datastart++ = 0;
datalen -= 4;
//datalen -= 4;
} else
fatal("Unknown icmp type/code (%d/%d) in build_icmp_raw", ptype, pcode);