mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
Merge r26341:26417 from /nmap-exp/david/nmap-cpe.
This adds CPE output support.
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o Added Common Platform Enumeration (CPE, http://cpe.mitre.org/)
|
||||||
|
output for OS and service versions. These show up in normal output
|
||||||
|
with the headings "OS CPE:" and "Service Info:":
|
||||||
|
OS CPE: cpe:/o:linux:kernel:2.6.39
|
||||||
|
Service Info: OS: Linux; CPE: cpe:/o:linux:kernel
|
||||||
|
These also appear in XML output, which additionally has CPE entries
|
||||||
|
for service versions. [David, Henri]
|
||||||
|
|
||||||
o [NSE] Added new default credential list for Oracle and modified the
|
o [NSE] Added new default credential list for Oracle and modified the
|
||||||
oracle-brute script to make use of it. [Patrik]
|
oracle-brute script to make use of it. [Patrik]
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
<!ELEMENT owner EMPTY >
|
<!ELEMENT owner EMPTY >
|
||||||
<!ATTLIST owner name CDATA #REQUIRED >
|
<!ATTLIST owner name CDATA #REQUIRED >
|
||||||
|
|
||||||
<!ELEMENT service EMPTY >
|
<!ELEMENT service (cpe*) >
|
||||||
<!ATTLIST service
|
<!ATTLIST service
|
||||||
name CDATA #REQUIRED
|
name CDATA #REQUIRED
|
||||||
conf %service_confs; #REQUIRED
|
conf %service_confs; #REQUIRED
|
||||||
@@ -221,6 +221,8 @@
|
|||||||
servicefp CDATA #IMPLIED
|
servicefp CDATA #IMPLIED
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<!ELEMENT cpe (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT script EMPTY >
|
<!ELEMENT script EMPTY >
|
||||||
<!ATTLIST script
|
<!ATTLIST script
|
||||||
id CDATA #REQUIRED
|
id CDATA #REQUIRED
|
||||||
@@ -235,7 +237,7 @@
|
|||||||
proto %port_protocols; #REQUIRED
|
proto %port_protocols; #REQUIRED
|
||||||
portid %attr_numeric; #REQUIRED
|
portid %attr_numeric; #REQUIRED
|
||||||
>
|
>
|
||||||
<!ELEMENT osclass EMPTY >
|
<!ELEMENT osclass (cpe*) >
|
||||||
<!ATTLIST osclass
|
<!ATTLIST osclass
|
||||||
vendor CDATA #REQUIRED
|
vendor CDATA #REQUIRED
|
||||||
osgen CDATA #IMPLIED
|
osgen CDATA #IMPLIED
|
||||||
|
|||||||
@@ -98,35 +98,32 @@
|
|||||||
<screen>
|
<screen>
|
||||||
# <userinput>nmap -A -T4 scanme.nmap.org</userinput>
|
# <userinput>nmap -A -T4 scanme.nmap.org</userinput>
|
||||||
|
|
||||||
Nmap scan report for scanme.nmap.org (64.13.134.52)
|
Nmap scan report for scanme.nmap.org (74.207.244.221)
|
||||||
Host is up (0.045s latency).
|
Host is up (0.029s latency).
|
||||||
Not shown: 993 filtered ports
|
rDNS record for 74.207.244.221: li86-221.members.linode.com
|
||||||
PORT STATE SERVICE VERSION
|
Not shown: 995 closed ports
|
||||||
22/tcp open ssh OpenSSH 4.3 (protocol 2.0)
|
PORT STATE SERVICE VERSION
|
||||||
| ssh-hostkey: 1024 60:ac:4d:51:b1:cd:85:09:12:16:92:76:1d:5d:27:6e (DSA)
|
22/tcp open ssh OpenSSH 5.3p1 Debian 3ubuntu7 (protocol 2.0)
|
||||||
|_2048 2c:22:75:60:4b:c3:3b:18:a2:97:2c:96:7e:28:dc:dd (RSA)
|
| ssh-hostkey: 1024 8d:60:f1:7c:ca:b7:3d:0a:d6:67:54:9d:69:d9:b9:dd (DSA)
|
||||||
25/tcp closed smtp
|
|_2048 79:f8:09:ac:d4:e2:32:42:10:49:d3:bd:20:82:85:ec (RSA)
|
||||||
53/tcp open domain
|
80/tcp open http Apache httpd 2.2.14 ((Ubuntu))
|
||||||
70/tcp closed gopher
|
|_http-title: Go ahead and ScanMe!
|
||||||
80/tcp open http Apache httpd 2.2.3 ((CentOS))
|
646/tcp filtered ldp
|
||||||
|_html-title: Go ahead and ScanMe!
|
1720/tcp filtered H.323/Q.931
|
||||||
| http-methods: Potentially risky methods: TRACE
|
9929/tcp open nping-echo Nping echo
|
||||||
|_See http://nmap.org/nsedoc/scripts/http-methods.html
|
|
||||||
113/tcp closed auth
|
|
||||||
31337/tcp closed Elite
|
|
||||||
Device type: general purpose
|
Device type: general purpose
|
||||||
Running: Linux 2.6.X
|
Running: Linux 2.6.X
|
||||||
OS details: Linux 2.6.13 - 2.6.31, Linux 2.6.18
|
OS CPE: cpe:/o:linux:kernel:2.6.39
|
||||||
Network Distance: 13 hops
|
OS details: Linux 2.6.39
|
||||||
|
Network Distance: 11 hops
|
||||||
|
Service Info: OS: Linux; CPE: cpe:/o:linux:kernel
|
||||||
|
|
||||||
TRACEROUTE (using port 80/tcp)
|
TRACEROUTE (using port 53/tcp)
|
||||||
HOP RTT ADDRESS
|
HOP RTT ADDRESS
|
||||||
[Cut first 10 hops for brevity]
|
[Cut first 10 hops for brevity]
|
||||||
11 80.33 ms layer42.car2.sanjose2.level3.net (4.59.4.78)
|
11 17.65 ms li86-221.members.linode.com (74.207.244.221)
|
||||||
12 137.52 ms xe6-2.core1.svk.layer42.net (69.36.239.221)
|
|
||||||
13 44.15 ms scanme.nmap.org (64.13.134.52)
|
|
||||||
|
|
||||||
Nmap done: 1 IP address (1 host up) scanned in 22.19 seconds
|
Nmap done: 1 IP address (1 host up) scanned in 14.40 seconds
|
||||||
</screen>
|
</screen>
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
@@ -1859,9 +1856,11 @@ way.</para>
|
|||||||
(e.g. FTP, SSH, Telnet, HTTP), the application name (e.g. ISC
|
(e.g. FTP, SSH, Telnet, HTTP), the application name (e.g. ISC
|
||||||
BIND, Apache httpd, Solaris telnetd), the version number,
|
BIND, Apache httpd, Solaris telnetd), the version number,
|
||||||
hostname, device type (e.g. printer, router), the OS family
|
hostname, device type (e.g. printer, router), the OS family
|
||||||
(e.g. Windows, Linux) and sometimes miscellaneous details like
|
(e.g. Windows, Linux). When possible, Nmap also gets the
|
||||||
|
Common Platform Enumeration (CPE)<indexterm><primary>Common Platform Enumeration</primary><secondary>service</secondary></indexterm>
|
||||||
|
representation of this information. Sometimes miscellaneous details like
|
||||||
whether an X server is open to connections, the SSH protocol
|
whether an X server is open to connections, the SSH protocol
|
||||||
version, or the KaZaA user name). Of course, most services don't
|
version, or the KaZaA user name, are available. Of course, most services don't
|
||||||
provide all of this information. If Nmap was compiled with
|
provide all of this information. If Nmap was compiled with
|
||||||
OpenSSL support, it will connect to SSL servers to deduce the
|
OpenSSL support, it will connect to SSL servers to deduce the
|
||||||
service listening behind that encryption layer.<indexterm><primary>SSL</primary><secondary>in version detection</secondary></indexterm>
|
service listening behind that encryption layer.<indexterm><primary>SSL</primary><secondary>in version detection</secondary></indexterm>
|
||||||
@@ -2027,6 +2026,7 @@ way.</para>
|
|||||||
<title>OS Detection</title>
|
<title>OS Detection</title>
|
||||||
<indexterm class="startofrange" id="man-os-detection-indexterm"><primary>OS detection</primary></indexterm>
|
<indexterm class="startofrange" id="man-os-detection-indexterm"><primary>OS detection</primary></indexterm>
|
||||||
|
|
||||||
|
<indexterm><primary>CPE</primary><see>Common Platform Enumeration</see></indexterm>
|
||||||
<para>One of Nmap's best-known features is remote OS detection
|
<para>One of Nmap's best-known features is remote OS detection
|
||||||
using TCP/IP stack fingerprinting. Nmap sends a series of TCP and
|
using TCP/IP stack fingerprinting. Nmap sends a series of TCP and
|
||||||
UDP packets to the remote host and examines practically every bit
|
UDP packets to the remote host and examines practically every bit
|
||||||
@@ -2040,7 +2040,10 @@ way.</para>
|
|||||||
OS, and a classification which provides the vendor name
|
OS, and a classification which provides the vendor name
|
||||||
(e.g. Sun), underlying OS (e.g. Solaris), OS generation (e.g. 10),
|
(e.g. Sun), underlying OS (e.g. Solaris), OS generation (e.g. 10),
|
||||||
and device type (general purpose, router, switch, game console,
|
and device type (general purpose, router, switch, game console,
|
||||||
etc).</para>
|
etc). Most fingerprints also have a Common Platform Enumeration
|
||||||
|
(CPE)<indexterm><primary>Common Platform Enumeration</primary><secondary>operating system</secondary></indexterm>
|
||||||
|
representation, like
|
||||||
|
<literal>cpe:/o:linux:kernel:2.6</literal>.</para>
|
||||||
|
|
||||||
<para>If Nmap is unable to guess the OS of a machine, and
|
<para>If Nmap is unable to guess the OS of a machine, and
|
||||||
conditions are good (e.g. at least one open port and one closed
|
conditions are good (e.g. at least one open port and one closed
|
||||||
|
|||||||
@@ -166,6 +166,14 @@ struct OS_Classification {
|
|||||||
const char *OS_Family;
|
const char *OS_Family;
|
||||||
const char *OS_Generation; /* Can be NULL if unclassified */
|
const char *OS_Generation; /* Can be NULL if unclassified */
|
||||||
const char *Device_Type;
|
const char *Device_Type;
|
||||||
|
std::vector<const char *> cpe;
|
||||||
|
|
||||||
|
OS_Classification() {
|
||||||
|
OS_Vendor = NULL;
|
||||||
|
OS_Family = NULL;
|
||||||
|
OS_Generation = NULL;
|
||||||
|
Device_Type = NULL;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FingerTest {
|
struct FingerTest {
|
||||||
|
|||||||
2491
nmap-os-db
2491
nmap-os-db
File diff suppressed because it is too large
Load Diff
4180
nmap-service-probes
4180
nmap-service-probes
File diff suppressed because it is too large
Load Diff
@@ -540,11 +540,11 @@ static int l_set_port_version (lua_State *L)
|
|||||||
if (o.servicescan)
|
if (o.servicescan)
|
||||||
target->ports.setServiceProbeResults(p->portno, p->proto,
|
target->ports.setServiceProbeResults(p->portno, p->proto,
|
||||||
probestate, name, tunnel, product,
|
probestate, name, tunnel, product,
|
||||||
version, extrainfo, hostname, ostype, devicetype, NULL);
|
version, extrainfo, hostname, ostype, devicetype, NULL, NULL, NULL, NULL);
|
||||||
else
|
else
|
||||||
target->ports.setServiceProbeResults(p->portno, p->proto,
|
target->ports.setServiceProbeResults(p->portno, p->proto,
|
||||||
probestate, name, tunnel, NULL, NULL,
|
probestate, name, tunnel, NULL, NULL,
|
||||||
NULL, NULL, NULL, NULL, NULL);
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
41
osscan.cc
41
osscan.cc
@@ -145,6 +145,23 @@ const char *string_pool_substr_strip(const char *s, const char *t) {
|
|||||||
return string_pool_substr(s, t);
|
return string_pool_substr(s, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Skip over whitespace to find the beginning of a word, then read until the
|
||||||
|
next whilespace character. Returns NULL if only whitespace is found. */
|
||||||
|
static const char *string_pool_strip_word(const char *s) {
|
||||||
|
const char *t;
|
||||||
|
|
||||||
|
while (isspace((int) (unsigned char) *s))
|
||||||
|
s++;
|
||||||
|
t = s;
|
||||||
|
while (*t != '\0' && !isspace((int) (unsigned char) *t))
|
||||||
|
t++;
|
||||||
|
|
||||||
|
if (s == t)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return string_pool_substr(s, t);
|
||||||
|
}
|
||||||
|
|
||||||
/* Format a string with sprintf and insert it with string_pool_insert. */
|
/* Format a string with sprintf and insert it with string_pool_insert. */
|
||||||
const char *string_pool_sprintf(const char *fmt, ...)
|
const char *string_pool_sprintf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@@ -983,6 +1000,24 @@ static void parse_classline(FingerPrint *FP, char *thisline, int lineno) {
|
|||||||
FP->OS_class.push_back(os_class);
|
FP->OS_class.push_back(os_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_cpeline(FingerPrint *FP, char *thisline, int lineno) {
|
||||||
|
const char *cpe;
|
||||||
|
|
||||||
|
if (FP->OS_class.empty())
|
||||||
|
fatal("\"CPE\" line without preceding \"Class\" at line %d", lineno);
|
||||||
|
|
||||||
|
OS_Classification& osc = FP->OS_class.back();
|
||||||
|
|
||||||
|
if (thisline == NULL || strncmp(thisline, "CPE ", 4) != 0)
|
||||||
|
fatal("Bogus line #%d (%s) passed to %s()", lineno, thisline, __func__);
|
||||||
|
|
||||||
|
/* The cpe part may be followed by whitespace-separated flags (like "auto"),
|
||||||
|
which we ignore. */
|
||||||
|
cpe = string_pool_strip_word(thisline + 4);
|
||||||
|
assert(cpe != NULL);
|
||||||
|
osc.cpe.push_back(cpe);
|
||||||
|
}
|
||||||
|
|
||||||
/* Parses a single fingerprint from the memory region given. If a
|
/* Parses a single fingerprint from the memory region given. If a
|
||||||
non-null fingerprint is returned, the user is in charge of freeing it
|
non-null fingerprint is returned, the user is in charge of freeing it
|
||||||
when done. This function does not require the fingerprint to be 100%
|
when done. This function does not require the fingerprint to be 100%
|
||||||
@@ -1042,6 +1077,10 @@ FingerPrint *parse_single_fingerprint(char *fprint_orig) {
|
|||||||
|
|
||||||
parse_classline(FP, thisline, lineno);
|
parse_classline(FP, thisline, lineno);
|
||||||
|
|
||||||
|
} else if (strncmp(thisline, "CPE ", 4) == 0) {
|
||||||
|
|
||||||
|
parse_cpeline(FP, thisline, lineno);
|
||||||
|
|
||||||
} else if ((q = strchr(thisline, '('))) {
|
} else if ((q = strchr(thisline, '('))) {
|
||||||
FingerTest test;
|
FingerTest test;
|
||||||
*q = '\0';
|
*q = '\0';
|
||||||
@@ -1148,6 +1187,8 @@ fparse:
|
|||||||
goto fparse;
|
goto fparse;
|
||||||
} else if (strncmp(line, "Class ", 6) == 0) {
|
} else if (strncmp(line, "Class ", 6) == 0) {
|
||||||
parse_classline(current, line, lineno);
|
parse_classline(current, line, lineno);
|
||||||
|
} else if (strncmp(line, "CPE ", 4) == 0) {
|
||||||
|
parse_cpeline(current, line, lineno);
|
||||||
} else {
|
} else {
|
||||||
FingerTest test;
|
FingerTest test;
|
||||||
p = line;
|
p = line;
|
||||||
|
|||||||
83
output.cc
83
output.cc
@@ -237,7 +237,19 @@ static void print_xml_service(const struct serviceDeductions *sd) {
|
|||||||
xml_attribute("proto", "rpc");
|
xml_attribute("proto", "rpc");
|
||||||
}
|
}
|
||||||
|
|
||||||
xml_close_empty_tag();
|
if (sd->cpe.empty()) {
|
||||||
|
xml_close_empty_tag();
|
||||||
|
} else {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
xml_close_start_tag();
|
||||||
|
for (i = 0; i < sd->cpe.size(); i++) {
|
||||||
|
xml_start_tag("cpe");
|
||||||
|
xml_write_escaped("%s", sd->cpe[i]);
|
||||||
|
xml_end_tag();
|
||||||
|
}
|
||||||
|
xml_end_tag();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
@@ -1442,13 +1454,15 @@ static int addtochararrayifnew(const char *arr[], int *numentries, int arrsize,
|
|||||||
static void printosclassificationoutput(const struct
|
static void printosclassificationoutput(const struct
|
||||||
OS_Classification_Results *OSR,
|
OS_Classification_Results *OSR,
|
||||||
bool guess) {
|
bool guess) {
|
||||||
int classno, i, familyno;
|
int classno, cpeno, familyno;
|
||||||
|
unsigned int i;
|
||||||
int overflow = 0; /* Whether we have too many devices to list */
|
int overflow = 0; /* Whether we have too many devices to list */
|
||||||
const char *types[MAX_OS_CLASSMEMBERS];
|
const char *types[MAX_OS_CLASSMEMBERS];
|
||||||
|
const char *cpes[MAX_OS_CLASSMEMBERS];
|
||||||
char fullfamily[MAX_OS_CLASSMEMBERS][128]; // "[vendor] [os family]"
|
char fullfamily[MAX_OS_CLASSMEMBERS][128]; // "[vendor] [os family]"
|
||||||
double familyaccuracy[MAX_OS_CLASSMEMBERS]; // highest accuracy for this fullfamily
|
double familyaccuracy[MAX_OS_CLASSMEMBERS]; // highest accuracy for this fullfamily
|
||||||
char familygenerations[MAX_OS_CLASSMEMBERS][48]; // example: "4.X|5.X|6.X"
|
char familygenerations[MAX_OS_CLASSMEMBERS][48]; // example: "4.X|5.X|6.X"
|
||||||
int numtypes = 0, numfamilies = 0;
|
int numtypes = 0, numcpes = 0, numfamilies = 0;
|
||||||
char tmpbuf[1024];
|
char tmpbuf[1024];
|
||||||
|
|
||||||
for (i = 0; i < MAX_OS_CLASSMEMBERS; i++) {
|
for (i = 0; i < MAX_OS_CLASSMEMBERS; i++) {
|
||||||
@@ -1468,7 +1482,17 @@ static void printosclassificationoutput(const struct
|
|||||||
if (OSR->OSC[classno]->OS_Generation)
|
if (OSR->OSC[classno]->OS_Generation)
|
||||||
xml_attribute("osgen", "%s", OSR->OSC[classno]->OS_Generation);
|
xml_attribute("osgen", "%s", OSR->OSC[classno]->OS_Generation);
|
||||||
xml_attribute("accuracy", "%d", (int) (OSR->OSC_Accuracy[classno] * 100));
|
xml_attribute("accuracy", "%d", (int) (OSR->OSC_Accuracy[classno] * 100));
|
||||||
xml_close_empty_tag();
|
if (OSR->OSC[classno]->cpe.empty()) {
|
||||||
|
xml_close_empty_tag();
|
||||||
|
} else {
|
||||||
|
xml_close_start_tag();
|
||||||
|
for (i = 0; i < OSR->OSC[classno]->cpe.size(); i++) {
|
||||||
|
xml_start_tag("cpe");
|
||||||
|
xml_write_escaped("%s", OSR->OSC[classno]->cpe[i]);
|
||||||
|
xml_end_tag();
|
||||||
|
}
|
||||||
|
xml_end_tag();
|
||||||
|
}
|
||||||
xml_newline();
|
xml_newline();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1483,6 +1507,12 @@ static void printosclassificationoutput(const struct
|
|||||||
OSR->OSC[classno]->Device_Type) == -1) {
|
OSR->OSC[classno]->Device_Type) == -1) {
|
||||||
overflow = 1;
|
overflow = 1;
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < OSR->OSC[classno]->cpe.size(); i++) {
|
||||||
|
if (addtochararrayifnew(cpes, &numcpes, MAX_OS_CLASSMEMBERS,
|
||||||
|
OSR->OSC[classno]->cpe[i]) == -1) {
|
||||||
|
overflow = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If family and vendor names are the same, no point being redundant
|
// If family and vendor names are the same, no point being redundant
|
||||||
if (strcmp(OSR->OSC[classno]->OS_Vendor, OSR->OSC[classno]->OS_Family) == 0)
|
if (strcmp(OSR->OSC[classno]->OS_Vendor, OSR->OSC[classno]->OS_Family) == 0)
|
||||||
@@ -1545,6 +1575,13 @@ static void printosclassificationoutput(const struct
|
|||||||
floor(familyaccuracy[familyno] * 100));
|
floor(familyaccuracy[familyno] * 100));
|
||||||
}
|
}
|
||||||
log_write(LOG_PLAIN, "\n");
|
log_write(LOG_PLAIN, "\n");
|
||||||
|
|
||||||
|
if (numcpes > 0) {
|
||||||
|
log_write(LOG_PLAIN, "OS CPE:");
|
||||||
|
for (cpeno = 0; cpeno < numcpes; cpeno++)
|
||||||
|
log_write(LOG_PLAIN, " %s", cpes[cpeno]);
|
||||||
|
log_write(LOG_PLAIN, "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_flush_all();
|
log_flush_all();
|
||||||
@@ -1846,16 +1883,19 @@ void printserviceinfooutput(Target *currenths) {
|
|||||||
Port *p = NULL;
|
Port *p = NULL;
|
||||||
Port port;
|
Port port;
|
||||||
struct serviceDeductions sd;
|
struct serviceDeductions sd;
|
||||||
int i, numhostnames = 0, numostypes = 0, numdevicetypes = 0;
|
int i, numhostnames = 0, numostypes = 0, numdevicetypes = 0, numcpes = 0;
|
||||||
char hostname_tbl[MAX_SERVICE_INFO_FIELDS][MAXHOSTNAMELEN];
|
char hostname_tbl[MAX_SERVICE_INFO_FIELDS][MAXHOSTNAMELEN];
|
||||||
char ostype_tbl[MAX_SERVICE_INFO_FIELDS][64];
|
char ostype_tbl[MAX_SERVICE_INFO_FIELDS][64];
|
||||||
char devicetype_tbl[MAX_SERVICE_INFO_FIELDS][64];
|
char devicetype_tbl[MAX_SERVICE_INFO_FIELDS][64];
|
||||||
|
char cpe_tbl[MAX_SERVICE_INFO_FIELDS][80];
|
||||||
const char *delim;
|
const char *delim;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVICE_INFO_FIELDS; i++)
|
for (i = 0; i < MAX_SERVICE_INFO_FIELDS; i++)
|
||||||
hostname_tbl[i][0] = ostype_tbl[i][0] = devicetype_tbl[i][0] = '\0';
|
hostname_tbl[i][0] = ostype_tbl[i][0] = devicetype_tbl[i][0] = cpe_tbl[i][0] = '\0';
|
||||||
|
|
||||||
while ((p = currenths->ports.nextPort(p, &port, TCPANDUDPANDSCTP, PORT_OPEN))) {
|
while ((p = currenths->ports.nextPort(p, &port, TCPANDUDPANDSCTP, PORT_OPEN))) {
|
||||||
|
std::vector<char *>::iterator it;
|
||||||
|
|
||||||
// The following 2 lines (from portlist.h) tell us that we don't need to
|
// The following 2 lines (from portlist.h) tell us that we don't need to
|
||||||
// worry about free()ing anything in the serviceDeductions struct. pass in
|
// worry about free()ing anything in the serviceDeductions struct. pass in
|
||||||
// an allocated struct serviceDeductions (don't wory about initializing, and
|
// an allocated struct serviceDeductions (don't wory about initializing, and
|
||||||
@@ -1901,9 +1941,29 @@ void printserviceinfooutput(Target *currenths) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (it = sd.cpe.begin(); it != sd.cpe.end(); it++) {
|
||||||
|
for (i = 0; i < MAX_SERVICE_INFO_FIELDS; i++) {
|
||||||
|
if (cpe_tbl[i][0] && !strcmp(&cpe_tbl[i][0], *it))
|
||||||
|
break;
|
||||||
|
/* Applications (CPE part "a") aren't shown in this summary list in
|
||||||
|
normal output. "a" classifications belong to an individual port, not
|
||||||
|
the entire host, unlike "h" (hardware) and "o" (operating system).
|
||||||
|
There isn't a good place to put the "a" classifications, so they are
|
||||||
|
written to XML only. */
|
||||||
|
if (cpe_get_part(*it) == 'a')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!cpe_tbl[i][0]) {
|
||||||
|
numcpes++;
|
||||||
|
strncpy(&cpe_tbl[i][0], *it, sizeof(cpe_tbl[i]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!numhostnames && !numostypes && !numdevicetypes)
|
if (!numhostnames && !numostypes && !numdevicetypes && !numcpes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
log_write(LOG_PLAIN, "Service Info:");
|
log_write(LOG_PLAIN, "Service Info:");
|
||||||
@@ -1938,6 +1998,15 @@ void printserviceinfooutput(Target *currenths) {
|
|||||||
delim = "; ";
|
delim = "; ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (numcpes > 0) {
|
||||||
|
log_write(LOG_PLAIN, "%sCPE: %s", delim, &cpe_tbl[0][0]);
|
||||||
|
for (i = 1; i < MAX_SERVICE_INFO_FIELDS; i++) {
|
||||||
|
if (cpe_tbl[i][0])
|
||||||
|
log_write(LOG_PLAIN, ", %s", &cpe_tbl[i][0]);
|
||||||
|
}
|
||||||
|
delim = "; ";
|
||||||
|
}
|
||||||
|
|
||||||
log_write(LOG_PLAIN, "\n");
|
log_write(LOG_PLAIN, "\n");
|
||||||
log_flush_all();
|
log_flush_all();
|
||||||
}
|
}
|
||||||
|
|||||||
18
portlist.cc
18
portlist.cc
@@ -117,6 +117,8 @@ Port::Port() {
|
|||||||
|
|
||||||
void Port::freeService() {
|
void Port::freeService() {
|
||||||
if (service != NULL) {
|
if (service != NULL) {
|
||||||
|
std::vector<char *>::iterator it;
|
||||||
|
|
||||||
if (service->name)
|
if (service->name)
|
||||||
free(service->name);
|
free(service->name);
|
||||||
if (service->product)
|
if (service->product)
|
||||||
@@ -133,6 +135,8 @@ void Port::freeService() {
|
|||||||
free(service->devicetype);
|
free(service->devicetype);
|
||||||
if (service->service_fp)
|
if (service->service_fp)
|
||||||
free(service->service_fp);
|
free(service->service_fp);
|
||||||
|
for (it = service->cpe.begin(); it != service->cpe.end(); it++)
|
||||||
|
free(*it);
|
||||||
delete service;
|
delete service;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,8 +336,10 @@ void PortList::setServiceProbeResults(u16 portno, int protocol,
|
|||||||
enum serviceprobestate sres, const char *sname,
|
enum serviceprobestate sres, const char *sname,
|
||||||
enum service_tunnel_type tunnel, const char *product, const char *version,
|
enum service_tunnel_type tunnel, const char *product, const char *version,
|
||||||
const char *extrainfo, const char *hostname, const char *ostype,
|
const char *extrainfo, const char *hostname, const char *ostype,
|
||||||
const char *devicetype, const char *fingerprint) {
|
const char *devicetype, const char *cpe_a, const char *cpe_h, const char *cpe_o,
|
||||||
|
const char *fingerprint) {
|
||||||
Port *port;
|
Port *port;
|
||||||
|
char *p;
|
||||||
|
|
||||||
port = createPort(portno, protocol);
|
port = createPort(portno, protocol);
|
||||||
if (port->service == NULL)
|
if (port->service == NULL)
|
||||||
@@ -380,6 +386,16 @@ void PortList::setServiceProbeResults(u16 portno, int protocol,
|
|||||||
port->service->hostname = cstringSanityCheck(hostname, 80);
|
port->service->hostname = cstringSanityCheck(hostname, 80);
|
||||||
port->service->ostype = cstringSanityCheck(ostype, 32);
|
port->service->ostype = cstringSanityCheck(ostype, 32);
|
||||||
port->service->devicetype = cstringSanityCheck(devicetype, 32);
|
port->service->devicetype = cstringSanityCheck(devicetype, 32);
|
||||||
|
|
||||||
|
p = cstringSanityCheck(cpe_a, 80);
|
||||||
|
if (p != NULL)
|
||||||
|
port->service->cpe.push_back(p);
|
||||||
|
p = cstringSanityCheck(cpe_h, 80);
|
||||||
|
if (p != NULL)
|
||||||
|
port->service->cpe.push_back(p);
|
||||||
|
p = cstringSanityCheck(cpe_o, 80);
|
||||||
|
if (p != NULL)
|
||||||
|
port->service->cpe.push_back(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets the results of an RPC scan. if rpc_status is not
|
/* Sets the results of an RPC scan. if rpc_status is not
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ struct serviceDeductions {
|
|||||||
char *hostname;
|
char *hostname;
|
||||||
char *ostype;
|
char *ostype;
|
||||||
char *devicetype;
|
char *devicetype;
|
||||||
|
std::vector<char *> cpe;
|
||||||
// SERVICE_TUNNEL_NONE or SERVICE_TUNNEL_SSL
|
// SERVICE_TUNNEL_NONE or SERVICE_TUNNEL_SSL
|
||||||
enum service_tunnel_type service_tunnel;
|
enum service_tunnel_type service_tunnel;
|
||||||
// if we should give the user a service fingerprint to submit, here it is. Otherwise NULL.
|
// if we should give the user a service fingerprint to submit, here it is. Otherwise NULL.
|
||||||
@@ -265,7 +266,9 @@ class PortList {
|
|||||||
enum service_tunnel_type tunnel, const char *product,
|
enum service_tunnel_type tunnel, const char *product,
|
||||||
const char *version, const char *hostname,
|
const char *version, const char *hostname,
|
||||||
const char *ostype, const char *devicetype,
|
const char *ostype, const char *devicetype,
|
||||||
const char *extrainfo, const char *fingerprint);
|
const char *extrainfo,
|
||||||
|
const char *cpe_a, const char *cpe_h, const char *cpe_o,
|
||||||
|
const char *fingerprint);
|
||||||
|
|
||||||
// pass in an allocated struct serviceDeductions (don't worry about initializing, and
|
// pass in an allocated struct serviceDeductions (don't worry about initializing, and
|
||||||
// you don't have to free any internal ptrs. See the serviceDeductions definition for
|
// you don't have to free any internal ptrs. See the serviceDeductions definition for
|
||||||
|
|||||||
488
service_scan.cc
488
service_scan.cc
@@ -166,6 +166,9 @@ public:
|
|||||||
char hostname_matched[80];
|
char hostname_matched[80];
|
||||||
char ostype_matched[32];
|
char ostype_matched[32];
|
||||||
char devicetype_matched[32];
|
char devicetype_matched[32];
|
||||||
|
char cpe_a_matched[80];
|
||||||
|
char cpe_h_matched[80];
|
||||||
|
char cpe_o_matched[80];
|
||||||
enum service_tunnel_type tunnel; /* SERVICE_TUNNEL_NONE, SERVICE_TUNNEL_SSL */
|
enum service_tunnel_type tunnel; /* SERVICE_TUNNEL_NONE, SERVICE_TUNNEL_SSL */
|
||||||
// This stores our SSL session id, which will help speed up subsequent
|
// This stores our SSL session id, which will help speed up subsequent
|
||||||
// SSL connections. It's overwritten each time. void* is used so we don't
|
// SSL connections. It's overwritten each time. void* is used so we don't
|
||||||
@@ -270,6 +273,7 @@ ServiceProbeMatch::ServiceProbeMatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServiceProbeMatch::~ServiceProbeMatch() {
|
ServiceProbeMatch::~ServiceProbeMatch() {
|
||||||
|
std::vector<char *>::iterator it;
|
||||||
if (!isInitialized) return;
|
if (!isInitialized) return;
|
||||||
if (servicename) free(servicename);
|
if (servicename) free(servicename);
|
||||||
if (matchstr) free(matchstr);
|
if (matchstr) free(matchstr);
|
||||||
@@ -279,6 +283,8 @@ ServiceProbeMatch::~ServiceProbeMatch() {
|
|||||||
if (hostname_template) free(hostname_template);
|
if (hostname_template) free(hostname_template);
|
||||||
if (ostype_template) free(ostype_template);
|
if (ostype_template) free(ostype_template);
|
||||||
if (devicetype_template) free(devicetype_template);
|
if (devicetype_template) free(devicetype_template);
|
||||||
|
for (it = cpe_templates.begin(); it != cpe_templates.end(); it++)
|
||||||
|
free(*it);
|
||||||
matchstrlen = 0;
|
matchstrlen = 0;
|
||||||
if (regex_compiled) pcre_free(regex_compiled);
|
if (regex_compiled) pcre_free(regex_compiled);
|
||||||
if (regex_extra) pcre_free(regex_extra);
|
if (regex_extra) pcre_free(regex_extra);
|
||||||
@@ -286,6 +292,83 @@ ServiceProbeMatch::~ServiceProbeMatch() {
|
|||||||
matchops_anchor = -1;
|
matchops_anchor = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make a new allocated null-terminated string from the bytes [start, end). */
|
||||||
|
static char *mkstr(const char *start, const char *end)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
assert(end >= start);
|
||||||
|
s = (char *) safe_malloc(end - start + 1);
|
||||||
|
memcpy(s, start, end - start);
|
||||||
|
s[end - start] = '\0';
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Realloc a malloc-allocated string and put a given prefix at the front. */
|
||||||
|
static char *string_prefix(char *string, const char *prefix)
|
||||||
|
{
|
||||||
|
size_t slen, plen;
|
||||||
|
|
||||||
|
slen = strlen(string);
|
||||||
|
plen = strlen(prefix);
|
||||||
|
string = (char *) safe_realloc(string, plen + slen + 1);
|
||||||
|
memmove(string + plen, string, slen + 1);
|
||||||
|
memmove(string, prefix, plen);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the next tmplt from *matchtext and update *matchtext. Return true iff
|
||||||
|
a template was read. For example, after
|
||||||
|
matchtext = "p/123/ d/456/";
|
||||||
|
next_template(&matchtext, &modestr, &flags, &tmplt);
|
||||||
|
then
|
||||||
|
matchtext == " d/456/"
|
||||||
|
modestr == "p"
|
||||||
|
tmplt == "123"
|
||||||
|
flags == ""
|
||||||
|
*modestr and *tmplt must be freed if the return value is true. */
|
||||||
|
static bool next_template(const char **matchtext, char **modestr, char **tmplt,
|
||||||
|
char **flags, int lineno) {
|
||||||
|
const char *p, *q;
|
||||||
|
char delimchar;
|
||||||
|
|
||||||
|
p = *matchtext;
|
||||||
|
while(isspace((int) (unsigned char) *p))
|
||||||
|
p++;
|
||||||
|
if (*p == '\0')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
q = p;
|
||||||
|
while (isalpha(*q) || *q == ':')
|
||||||
|
q++;
|
||||||
|
if (*q == '\0' || isspace(*q))
|
||||||
|
fatal("%s: parse error on line %d of nmap-service-probes", __func__, lineno);
|
||||||
|
|
||||||
|
*modestr = mkstr(p, q);
|
||||||
|
|
||||||
|
delimchar = *q;
|
||||||
|
p = q + 1;
|
||||||
|
|
||||||
|
q = strchr(p, delimchar);
|
||||||
|
if (q == NULL)
|
||||||
|
fatal("%s: parse error on line %d of nmap-service-probes", __func__, lineno);
|
||||||
|
|
||||||
|
*tmplt = mkstr(p, q);
|
||||||
|
p = q + 1;
|
||||||
|
|
||||||
|
q = p;
|
||||||
|
while (isalpha(*q))
|
||||||
|
q++;
|
||||||
|
*flags = mkstr(p, q);
|
||||||
|
|
||||||
|
/* Update pointer for caller. */
|
||||||
|
*matchtext = q;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// match text from the nmap-service-probes file. This must be called
|
// match text from the nmap-service-probes file. This must be called
|
||||||
// before you try and do anything with this match. This function
|
// before you try and do anything with this match. This function
|
||||||
// should be passed the whole line starting with "match" or
|
// should be passed the whole line starting with "match" or
|
||||||
@@ -294,12 +377,10 @@ ServiceProbeMatch::~ServiceProbeMatch() {
|
|||||||
// function will abort the program if there is a syntax problem.
|
// function will abort the program if there is a syntax problem.
|
||||||
void ServiceProbeMatch::InitMatch(const char *matchtext, int lineno) {
|
void ServiceProbeMatch::InitMatch(const char *matchtext, int lineno) {
|
||||||
const char *p;
|
const char *p;
|
||||||
char *tmptemplate;
|
char *modestr, *tmptemplate, *flags;
|
||||||
char delimchar, modechar;
|
|
||||||
int pcre_compile_ops = 0;
|
int pcre_compile_ops = 0;
|
||||||
const char *pcre_errptr = NULL;
|
const char *pcre_errptr = NULL;
|
||||||
int pcre_erroffset = 0;
|
int pcre_erroffset = 0;
|
||||||
unsigned int tmpbuflen = 0;
|
|
||||||
char **curr_tmp = NULL;
|
char **curr_tmp = NULL;
|
||||||
|
|
||||||
if (isInitialized) fatal("Sorry ... %s does not yet support reinitializion", __func__);
|
if (isInitialized) fatal("Sorry ... %s does not yet support reinitializion", __func__);
|
||||||
@@ -336,103 +417,80 @@ void ServiceProbeMatch::InitMatch(const char *matchtext, int lineno) {
|
|||||||
// options. ('i' means "case insensitive", 's' means that . matches
|
// options. ('i' means "case insensitive", 's' means that . matches
|
||||||
// newlines (both are just as in perl)
|
// newlines (both are just as in perl)
|
||||||
matchtext = p;
|
matchtext = p;
|
||||||
while(isspace((int) (unsigned char) *matchtext)) matchtext++;
|
if (!next_template(&matchtext, &modestr, &matchstr, &flags, lineno))
|
||||||
if (*matchtext == 'm') {
|
fatal("%s: parse error on line %d of nmap-service-probes", __func__, lineno);
|
||||||
if (!*(matchtext+1))
|
|
||||||
fatal("%s: parse error on line %d of nmap-service-probes: matchtext must begin with 'm'", __func__, lineno);
|
|
||||||
matchtype = SERVICEMATCH_REGEX;
|
|
||||||
delimchar = *(++matchtext);
|
|
||||||
++matchtext;
|
|
||||||
// find the end of the regex
|
|
||||||
p = strchr(matchtext, delimchar);
|
|
||||||
if (!p) fatal("%s: parse error on line %d of nmap-service-probes: could not find end delimiter for regex", __func__, lineno);
|
|
||||||
matchstrlen = p - matchtext;
|
|
||||||
matchstr = (char *) safe_malloc(matchstrlen + 1);
|
|
||||||
memcpy(matchstr, matchtext, matchstrlen);
|
|
||||||
matchstr[matchstrlen] = '\0';
|
|
||||||
|
|
||||||
matchtext = p + 1; // skip past the delim
|
|
||||||
// any options?
|
|
||||||
while(*matchtext && !isspace((int) (unsigned char) *matchtext)) {
|
|
||||||
if (*matchtext == 'i')
|
|
||||||
matchops_ignorecase = true;
|
|
||||||
else if (*matchtext == 's')
|
|
||||||
matchops_dotall = true;
|
|
||||||
else fatal("%s: illegal regexp option on line %d of nmap-service-probes", __func__, lineno);
|
|
||||||
matchtext++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next we compile and study the regular expression to match
|
if (strcmp(modestr, "m") != 0)
|
||||||
if (matchops_ignorecase)
|
fatal("%s: parse error on line %d of nmap-service-probes: matchtext must begin with 'm'", __func__, lineno);
|
||||||
pcre_compile_ops |= PCRE_CASELESS;
|
matchtype = SERVICEMATCH_REGEX;
|
||||||
|
|
||||||
if (matchops_dotall)
|
// any options?
|
||||||
pcre_compile_ops |= PCRE_DOTALL;
|
for (p = flags; *p != '\0'; p++) {
|
||||||
|
if (*p == 'i')
|
||||||
regex_compiled = pcre_compile(matchstr, pcre_compile_ops, &pcre_errptr,
|
matchops_ignorecase = true;
|
||||||
&pcre_erroffset, NULL);
|
else if (*p == 's')
|
||||||
|
matchops_dotall = true;
|
||||||
if (regex_compiled == NULL)
|
else
|
||||||
fatal("%s: illegal regexp on line %d of nmap-service-probes (at regexp offset %d): %s\n", __func__, lineno, pcre_erroffset, pcre_errptr);
|
fatal("%s: illegal regexp option on line %d of nmap-service-probes", __func__, lineno);
|
||||||
|
|
||||||
|
|
||||||
// Now study the regexp for greater efficiency
|
|
||||||
regex_extra = pcre_study(regex_compiled, 0, &pcre_errptr);
|
|
||||||
if (pcre_errptr != NULL)
|
|
||||||
fatal("%s: failed to pcre_study regexp on line %d of nmap-service-probes: %s\n", __func__, lineno, pcre_errptr);
|
|
||||||
} else {
|
|
||||||
/* Invalid matchtext */
|
|
||||||
fatal("%s: parse error on line %d of nmap-service-probes: match string must begin with 'm'", __func__, lineno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next we compile and study the regular expression to match
|
||||||
|
if (matchops_ignorecase)
|
||||||
|
pcre_compile_ops |= PCRE_CASELESS;
|
||||||
|
|
||||||
|
if (matchops_dotall)
|
||||||
|
pcre_compile_ops |= PCRE_DOTALL;
|
||||||
|
|
||||||
|
regex_compiled = pcre_compile(matchstr, pcre_compile_ops, &pcre_errptr,
|
||||||
|
&pcre_erroffset, NULL);
|
||||||
|
|
||||||
|
if (regex_compiled == NULL)
|
||||||
|
fatal("%s: illegal regexp on line %d of nmap-service-probes (at regexp offset %d): %s\n", __func__, lineno, pcre_erroffset, pcre_errptr);
|
||||||
|
|
||||||
|
// Now study the regexp for greater efficiency
|
||||||
|
regex_extra = pcre_study(regex_compiled, 0, &pcre_errptr);
|
||||||
|
if (pcre_errptr != NULL)
|
||||||
|
fatal("%s: failed to pcre_study regexp on line %d of nmap-service-probes: %s\n", __func__, lineno, pcre_errptr);
|
||||||
|
|
||||||
|
free(modestr);
|
||||||
|
free(flags);
|
||||||
|
|
||||||
/* OK! Now we look for any templates of the form ?/.../
|
/* OK! Now we look for any templates of the form ?/.../
|
||||||
* where ? is either p, v, i, h, o, or d. / is any
|
* where ? is either p, v, i, h, o, or d. / is any
|
||||||
* delimiter character and ... is a template */
|
* delimiter character and ... is a template */
|
||||||
|
|
||||||
while(1) {
|
while (next_template(&matchtext, &modestr, &tmptemplate, &flags, lineno)) {
|
||||||
while(isspace((int) (unsigned char) *matchtext)) matchtext++;
|
if (strcmp(modestr, "p") == 0)
|
||||||
if (*matchtext == '\0' || *matchtext == '\r' || *matchtext == '\n') break;
|
curr_tmp = &product_template;
|
||||||
|
else if (strcmp(modestr, "v") == 0)
|
||||||
|
curr_tmp = &version_template;
|
||||||
|
else if (strcmp(modestr, "i") == 0)
|
||||||
|
curr_tmp = &info_template;
|
||||||
|
else if (strcmp(modestr, "h") == 0)
|
||||||
|
curr_tmp = &hostname_template;
|
||||||
|
else if (strcmp(modestr, "o") == 0)
|
||||||
|
curr_tmp = &ostype_template;
|
||||||
|
else if (strcmp(modestr, "d") == 0)
|
||||||
|
curr_tmp = &devicetype_template;
|
||||||
|
else if (strcmp(modestr, "cpe:") == 0) {
|
||||||
|
tmptemplate = string_prefix(tmptemplate, "cpe:/");
|
||||||
|
cpe_templates.push_back(NULL);
|
||||||
|
curr_tmp = &cpe_templates.back();
|
||||||
|
} else
|
||||||
|
fatal("%s: Unknown template specifier '%s' on line %d of nmap-service-probes", __func__, modestr, lineno);
|
||||||
|
|
||||||
modechar = *(matchtext++);
|
/* This one already defined? */
|
||||||
if (*matchtext == 0 || *matchtext == '\r' || *matchtext == '\n')
|
if (*curr_tmp) {
|
||||||
fatal("%s: parse error on line %d of nmap-service-probes", __func__, lineno);
|
if (o.debugging) {
|
||||||
|
error("WARNING: Template \"%s/%s/\" replaced with \"%s/%s/\" on line %d of nmap-service-probes",
|
||||||
delimchar = *(matchtext++);
|
modestr, *curr_tmp, modestr, tmptemplate, lineno);
|
||||||
|
|
||||||
p = strchr(matchtext, delimchar);
|
|
||||||
if (!p) fatal("%s: parse error on line %d of nmap-service-probes", __func__, lineno);
|
|
||||||
|
|
||||||
tmptemplate = NULL;
|
|
||||||
tmpbuflen = p - matchtext;
|
|
||||||
if (tmpbuflen > 0) {
|
|
||||||
tmptemplate = (char *) safe_malloc(tmpbuflen + 1);
|
|
||||||
memcpy(tmptemplate, matchtext, tmpbuflen);
|
|
||||||
tmptemplate[tmpbuflen] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(modechar){
|
|
||||||
case 'p': curr_tmp = &product_template; break;
|
|
||||||
case 'v': curr_tmp = &version_template; break;
|
|
||||||
case 'i': curr_tmp = &info_template; break;
|
|
||||||
case 'h': curr_tmp = &hostname_template; break;
|
|
||||||
case 'o': curr_tmp = &ostype_template; break;
|
|
||||||
case 'd': curr_tmp = &devicetype_template; break;
|
|
||||||
default:
|
|
||||||
fatal("%s: Unknown template specifier '%c' on line %d of nmap-service-probes", __func__, modechar, lineno);
|
|
||||||
}
|
|
||||||
if(*curr_tmp){
|
|
||||||
if(o.debugging)
|
|
||||||
error("WARNING: Template \"%c/%s/\" replaced with \"%c/%s/\" on line %d of nmap-service-probes",
|
|
||||||
modechar,
|
|
||||||
*curr_tmp,
|
|
||||||
modechar,
|
|
||||||
tmptemplate,
|
|
||||||
lineno);
|
|
||||||
free(*curr_tmp);
|
|
||||||
}
|
}
|
||||||
*curr_tmp = tmptemplate;
|
free(*curr_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
matchtext = p + 1;
|
*curr_tmp = tmptemplate;
|
||||||
|
free(modestr);
|
||||||
|
free(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
isInitialized = 1;
|
isInitialized = 1;
|
||||||
@@ -455,6 +513,7 @@ const struct MatchDetails *ServiceProbeMatch::testMatch(const u8 *buf, int bufle
|
|||||||
static char hostname[80];
|
static char hostname[80];
|
||||||
static char ostype[32];
|
static char ostype[32];
|
||||||
static char devicetype[32];
|
static char devicetype[32];
|
||||||
|
static char cpe_a[80], cpe_h[80], cpe_o[80];
|
||||||
char *bufc = (char *) buf;
|
char *bufc = (char *) buf;
|
||||||
int ovector[150]; // allows 50 substring matches (including the overall match)
|
int ovector[150]; // allows 50 substring matches (including the overall match)
|
||||||
assert(isInitialized);
|
assert(isInitialized);
|
||||||
@@ -480,13 +539,17 @@ const struct MatchDetails *ServiceProbeMatch::testMatch(const u8 *buf, int bufle
|
|||||||
// Yeah! Match apparently succeeded.
|
// Yeah! Match apparently succeeded.
|
||||||
// Now lets get the version number if available
|
// Now lets get the version number if available
|
||||||
getVersionStr(buf, buflen, ovector, rc, product, sizeof(product), version, sizeof(version), info, sizeof(info),
|
getVersionStr(buf, buflen, ovector, rc, product, sizeof(product), version, sizeof(version), info, sizeof(info),
|
||||||
hostname, sizeof(hostname), ostype, sizeof(ostype), devicetype, sizeof(devicetype));
|
hostname, sizeof(hostname), ostype, sizeof(ostype), devicetype, sizeof(devicetype),
|
||||||
|
cpe_a, sizeof(cpe_a), cpe_h, sizeof(cpe_h), cpe_o, sizeof(cpe_o));
|
||||||
if (*product) MD_return.product = product;
|
if (*product) MD_return.product = product;
|
||||||
if (*version) MD_return.version = version;
|
if (*version) MD_return.version = version;
|
||||||
if (*info) MD_return.info = info;
|
if (*info) MD_return.info = info;
|
||||||
if (*hostname) MD_return.hostname = hostname;
|
if (*hostname) MD_return.hostname = hostname;
|
||||||
if (*ostype) MD_return.ostype = ostype;
|
if (*ostype) MD_return.ostype = ostype;
|
||||||
if (*devicetype) MD_return.devicetype = devicetype;
|
if (*devicetype) MD_return.devicetype = devicetype;
|
||||||
|
if (*cpe_a) MD_return.cpe_a = cpe_a;
|
||||||
|
if (*cpe_h) MD_return.cpe_h = cpe_h;
|
||||||
|
if (*cpe_o) MD_return.cpe_o = cpe_o;
|
||||||
|
|
||||||
MD_return.serviceName = servicename;
|
MD_return.serviceName = servicename;
|
||||||
MD_return.lineno = getLineNo();
|
MD_return.lineno = getLineNo();
|
||||||
@@ -561,44 +624,103 @@ static int getsubstcommandargs(struct substargs *args, char *args_start,
|
|||||||
return args->num_args;
|
return args->num_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function does the actual substitution of a placeholder like $2
|
/* These three functions manage a growing string buffer, appended to at the end.
|
||||||
// or $P(4) into the given buffer. It returns the number of chars
|
Begin with strbuf_init, follow with any number of strbuf_append, and end with
|
||||||
// written, or -1 if it fails. tmplvar is a template variable, such
|
strbuf_finish. */
|
||||||
// as "$P(2)". We determine the appropriate string representing that,
|
static void strbuf_init(char **buf, size_t *n, size_t *len) {
|
||||||
// and place it in newstr (as long as it doesn't exceed newstrlen).
|
*buf = NULL;
|
||||||
// We then set *tmplvarend to the character after the
|
*n = 0;
|
||||||
// variable. subject, subjectlen, ovector, and nummatches mean the
|
*len = 0;
|
||||||
// same as in dotmplsubst().
|
}
|
||||||
static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|
||||||
int newstrlen, const u8 *subject, int subjectlen, int *ovector,
|
static void strbuf_append(char **buf, size_t *n, size_t *len,
|
||||||
|
const char *from, size_t fromlen) {
|
||||||
|
/* Double the size of the buffer if necessary. */
|
||||||
|
if (*len == 0 || *len + fromlen > *n) {
|
||||||
|
*n = (*len + fromlen) * 2;
|
||||||
|
*buf = (char *) safe_realloc(*buf, *n + 1);
|
||||||
|
}
|
||||||
|
memcpy(*buf + *len, from, fromlen);
|
||||||
|
*len += fromlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trim to length. (Also does initial allocation when *buf is empty.) */
|
||||||
|
static void strbuf_finish(char **buf, size_t *n, size_t *len) {
|
||||||
|
*buf = (char *) safe_realloc(*buf, *len + 1);
|
||||||
|
(*buf)[*len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transform a string so that it is safe to insert into the middle of a CPE URL. */
|
||||||
|
static char *transform_cpe(const char *s) {
|
||||||
|
char *result;
|
||||||
|
size_t n, len, repllen;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
strbuf_init(&result, &n, &len);
|
||||||
|
for (p = s; *p != '\0'; p++) {
|
||||||
|
const char *repl;
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
/* Section 5.4 of the CPE specification lists these characters to be
|
||||||
|
escaped. */
|
||||||
|
if (strchr(":/?#[]@!$&'()*+,;=%<>\"", *p) != NULL) {
|
||||||
|
Snprintf(buf, sizeof(buf), "%%%02X", *p);
|
||||||
|
repl = buf;
|
||||||
|
/* Replacing spaces with underscores is also a convention. */
|
||||||
|
} else if (*p == ' ') {
|
||||||
|
repl = "_";
|
||||||
|
/* Otherwise just make lower-case. */
|
||||||
|
} else {
|
||||||
|
buf[0] = tolower(*p);
|
||||||
|
buf[1] = '\0';
|
||||||
|
repl = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
repllen = strlen(repl);
|
||||||
|
strbuf_append(&result, &n, &len, repl, repllen);
|
||||||
|
}
|
||||||
|
strbuf_finish(&result, &n, &len);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function does the substitution of a placeholder like $2 or $P(4). It
|
||||||
|
// returns a newly allocated string, or NULL if it fails. tmplvar is a template
|
||||||
|
// variable, such as "$P(2)". We set *tmplvarend to the character after the
|
||||||
|
// variable. subject, subjectlen, ovector, and nummatches mean the same as in
|
||||||
|
// dotmplsubst().
|
||||||
|
static char *substvar(char *tmplvar, char **tmplvarend,
|
||||||
|
const u8 *subject, int subjectlen, int *ovector,
|
||||||
int nummatches) {
|
int nummatches) {
|
||||||
char substcommand[16];
|
char substcommand[16];
|
||||||
char *p = NULL;
|
char *p = NULL;
|
||||||
char *p_end;
|
char *p_end;
|
||||||
int len;
|
|
||||||
int subnum = 0;
|
int subnum = 0;
|
||||||
int offstart, offend;
|
int offstart, offend;
|
||||||
int byteswritten = 0; // for return val
|
|
||||||
int rc;
|
int rc;
|
||||||
int i;
|
int i;
|
||||||
struct substargs command_args;
|
struct substargs command_args;
|
||||||
|
char *result;
|
||||||
|
size_t n, len;
|
||||||
|
|
||||||
// skip the '$'
|
// skip the '$'
|
||||||
if (*tmplvar != '$') return -1;
|
if (*tmplvar != '$') return NULL;
|
||||||
tmplvar++;
|
tmplvar++;
|
||||||
|
|
||||||
if (!isdigit((int) (unsigned char) *tmplvar)) {
|
if (!isdigit((int) (unsigned char) *tmplvar)) {
|
||||||
|
int commandlen;
|
||||||
/* This is a command like $P(1). */
|
/* This is a command like $P(1). */
|
||||||
p = strchr(tmplvar, '(');
|
p = strchr(tmplvar, '(');
|
||||||
if (!p) return -1;
|
if (!p) return NULL;
|
||||||
len = p - tmplvar;
|
commandlen = p - tmplvar;
|
||||||
if (!len || len >= (int) sizeof(substcommand))
|
if (!commandlen || commandlen >= (int) sizeof(substcommand))
|
||||||
return -1;
|
return NULL;
|
||||||
memcpy(substcommand, tmplvar, len);
|
memcpy(substcommand, tmplvar, commandlen);
|
||||||
substcommand[len] = '\0';
|
substcommand[commandlen] = '\0';
|
||||||
tmplvar = p+1;
|
tmplvar = p+1;
|
||||||
// Now we grab the arguments.
|
// Now we grab the arguments.
|
||||||
rc = getsubstcommandargs(&command_args, tmplvar, &p_end);
|
rc = getsubstcommandargs(&command_args, tmplvar, &p_end);
|
||||||
if (rc <= 0) return -1;
|
if (rc <= 0) return NULL;
|
||||||
tmplvar = p_end;
|
tmplvar = p_end;
|
||||||
} else {
|
} else {
|
||||||
/* This is a placeholder like $2. */
|
/* This is a placeholder like $2. */
|
||||||
@@ -609,28 +731,25 @@ static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|||||||
|
|
||||||
if (tmplvarend) *tmplvarend = tmplvar;
|
if (tmplvarend) *tmplvarend = tmplvar;
|
||||||
|
|
||||||
|
strbuf_init(&result, &n, &len);
|
||||||
if (!*substcommand) {
|
if (!*substcommand) {
|
||||||
/* Handler for a placeholder like $2. */
|
/* Handler for a placeholder like $2. */
|
||||||
if (subnum > 9 || subnum <= 0) return -1;
|
if (subnum > 9 || subnum <= 0) return NULL;
|
||||||
if (subnum >= nummatches) return -1;
|
if (subnum >= nummatches) return NULL;
|
||||||
offstart = ovector[subnum * 2];
|
offstart = ovector[subnum * 2];
|
||||||
offend = ovector[subnum * 2 + 1];
|
offend = ovector[subnum * 2 + 1];
|
||||||
assert(offstart >= 0 && offstart < subjectlen);
|
assert(offstart >= 0 && offstart < subjectlen);
|
||||||
assert(offend >= 0 && offend <= subjectlen);
|
assert(offend >= 0 && offend <= subjectlen);
|
||||||
len = offend - offstart;
|
|
||||||
// A plain-jane copy
|
// A plain-jane copy
|
||||||
if (newstrlen <= len - 1)
|
strbuf_append(&result, &n, &len, (const char *) subject + offstart, offend - offstart);
|
||||||
return -1;
|
|
||||||
memcpy(newstr, subject + offstart, len);
|
|
||||||
byteswritten = len;
|
|
||||||
} else if (strcmp(substcommand, "P") == 0) {
|
} else if (strcmp(substcommand, "P") == 0) {
|
||||||
if (command_args.num_args != 1 ||
|
if (command_args.num_args != 1 ||
|
||||||
command_args.arg_types[0] != SUBSTARGS_ARGTYPE_INT) {
|
command_args.arg_types[0] != SUBSTARGS_ARGTYPE_INT) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
subnum = command_args.int_args[0];
|
subnum = command_args.int_args[0];
|
||||||
if (subnum > 9 || subnum <= 0) return -1;
|
if (subnum > 9 || subnum <= 0) return NULL;
|
||||||
if (subnum >= nummatches) return -1;
|
if (subnum >= nummatches) return NULL;
|
||||||
offstart = ovector[subnum * 2];
|
offstart = ovector[subnum * 2];
|
||||||
offend = ovector[subnum * 2 + 1];
|
offend = ovector[subnum * 2 + 1];
|
||||||
assert(offstart >= 0 && offstart < subjectlen);
|
assert(offstart >= 0 && offstart < subjectlen);
|
||||||
@@ -638,12 +757,10 @@ static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|||||||
// This filter only includes printable characters. It is particularly
|
// This filter only includes printable characters. It is particularly
|
||||||
// useful for collapsing unicode text that looks like
|
// useful for collapsing unicode text that looks like
|
||||||
// "W\0O\0R\0K\0G\0R\0O\0U\0P\0"
|
// "W\0O\0R\0K\0G\0R\0O\0U\0P\0"
|
||||||
for(i=offstart; i < offend; i++)
|
for(i=offstart; i < offend; i++) {
|
||||||
if (isprint((int) subject[i])) {
|
if (isprint((int) subject[i]))
|
||||||
if (byteswritten >= newstrlen - 1)
|
strbuf_append(&result, &n, &len, (const char *) subject + i, 1);
|
||||||
return -1;
|
}
|
||||||
newstr[byteswritten++] = subject[i];
|
|
||||||
}
|
|
||||||
} else if (strcmp(substcommand, "SUBST") == 0) {
|
} else if (strcmp(substcommand, "SUBST") == 0) {
|
||||||
char *findstr, *replstr;
|
char *findstr, *replstr;
|
||||||
int findstrlen, replstrlen;
|
int findstrlen, replstrlen;
|
||||||
@@ -651,11 +768,11 @@ static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|||||||
command_args.arg_types[0] != SUBSTARGS_ARGTYPE_INT ||
|
command_args.arg_types[0] != SUBSTARGS_ARGTYPE_INT ||
|
||||||
command_args.arg_types[1] != SUBSTARGS_ARGTYPE_STRING ||
|
command_args.arg_types[1] != SUBSTARGS_ARGTYPE_STRING ||
|
||||||
command_args.arg_types[2] != SUBSTARGS_ARGTYPE_STRING) {
|
command_args.arg_types[2] != SUBSTARGS_ARGTYPE_STRING) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
subnum = command_args.int_args[0];
|
subnum = command_args.int_args[0];
|
||||||
if (subnum > 9 || subnum <= 0) return -1;
|
if (subnum > 9 || subnum <= 0) return NULL;
|
||||||
if (subnum >= nummatches) return -1;
|
if (subnum >= nummatches) return NULL;
|
||||||
offstart = ovector[subnum * 2];
|
offstart = ovector[subnum * 2];
|
||||||
offend = ovector[subnum * 2 + 1];
|
offend = ovector[subnum * 2 + 1];
|
||||||
assert(offstart >= 0 && offstart < subjectlen);
|
assert(offstart >= 0 && offstart < subjectlen);
|
||||||
@@ -665,26 +782,19 @@ static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|||||||
replstr = command_args.str_args[2];
|
replstr = command_args.str_args[2];
|
||||||
replstrlen = command_args.str_args_len[2];
|
replstrlen = command_args.str_args_len[2];
|
||||||
for(i=offstart; i < offend; ) {
|
for(i=offstart; i < offend; ) {
|
||||||
if (byteswritten >= newstrlen - 1)
|
if (memcmp(subject + i, findstr, findstrlen) != 0) {
|
||||||
return -1;
|
strbuf_append(&result, &n, &len, (const char *) subject + i, 1); // no match
|
||||||
if (offend - i < findstrlen)
|
i++;
|
||||||
newstr[byteswritten++] = subject[i++]; // No room for match
|
} else {
|
||||||
else if (memcmp(subject + i, findstr, findstrlen) != 0)
|
|
||||||
newstr[byteswritten++] = subject[i++]; // no match
|
|
||||||
else {
|
|
||||||
// The find string was found, copy it to newstring
|
// The find string was found, copy it to newstring
|
||||||
if (newstrlen - 1 - byteswritten < replstrlen)
|
strbuf_append(&result, &n, &len, replstr, replstrlen);
|
||||||
return -1;
|
|
||||||
memcpy(newstr + byteswritten, replstr, replstrlen);
|
|
||||||
byteswritten += replstrlen;
|
|
||||||
i += findstrlen;
|
i += findstrlen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else return -1; // Unknown command
|
} else return NULL; // Unknown command
|
||||||
|
|
||||||
if (byteswritten >= newstrlen) return -1;
|
strbuf_finish(&result, &n, &len);
|
||||||
newstr[byteswritten] = '\0';
|
return result;
|
||||||
return byteswritten;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -695,13 +805,19 @@ static int substvar(char *tmplvar, char **tmplvarend, char *newstr,
|
|||||||
// matches in ovector. The NUL-terminated newly composted string is
|
// matches in ovector. The NUL-terminated newly composted string is
|
||||||
// placed into 'newstr', as long as it doesn't exceed 'newstrlen'
|
// placed into 'newstr', as long as it doesn't exceed 'newstrlen'
|
||||||
// bytes. Trailing whitespace and commas are removed. Returns zero for success
|
// bytes. Trailing whitespace and commas are removed. Returns zero for success
|
||||||
|
//
|
||||||
|
// The transform argument is a function pointer. If not NULL, the given
|
||||||
|
// function is applied to all substitutions before they are inserted
|
||||||
|
// into the result string.
|
||||||
static int dotmplsubst(const u8 *subject, int subjectlen,
|
static int dotmplsubst(const u8 *subject, int subjectlen,
|
||||||
int *ovector, int nummatches, char *tmpl, char *newstr,
|
int *ovector, int nummatches, char *tmpl, char *newstr,
|
||||||
int newstrlen) {
|
int newstrlen,
|
||||||
|
char *(*transform)(const char *) = NULL) {
|
||||||
int newlen;
|
int newlen;
|
||||||
char *srcstart=tmpl, *srcend;
|
char *srcstart=tmpl, *srcend;
|
||||||
char *dst = newstr;
|
char *dst = newstr;
|
||||||
char *newstrend = newstr + newstrlen; // Right after the final char
|
char *newstrend = newstr + newstrlen; // Right after the final char
|
||||||
|
char *subst;
|
||||||
|
|
||||||
if (!newstr || !tmpl) return -1;
|
if (!newstr || !tmpl) return -1;
|
||||||
if (newstrlen < 3) return -1; // fuck this!
|
if (newstrlen < 3) return -1; // fuck this!
|
||||||
@@ -733,9 +849,24 @@ static int dotmplsubst(const u8 *subject, int subjectlen,
|
|||||||
dst += newlen;
|
dst += newlen;
|
||||||
}
|
}
|
||||||
srcstart = srcend;
|
srcstart = srcend;
|
||||||
newlen = substvar(srcstart, &srcend, dst, newstrend - dst, subject,
|
subst = substvar(srcstart, &srcend, subject, subjectlen, ovector, nummatches);
|
||||||
subjectlen, ovector, nummatches);
|
if (subst == NULL)
|
||||||
if (newlen == -1) return -1;
|
return -1;
|
||||||
|
/* Apply transformation if requested. */
|
||||||
|
if (transform != NULL) {
|
||||||
|
char *tmp = subst;
|
||||||
|
subst = transform(subst);
|
||||||
|
free(tmp);
|
||||||
|
if (subst == NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
newlen = strlen(subst);
|
||||||
|
if (dst + newlen >= newstrend - 1) {
|
||||||
|
free(subst);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(dst, subst, newlen);
|
||||||
|
free(subst);
|
||||||
dst += newlen;
|
dst += newlen;
|
||||||
srcstart = srcend;
|
srcstart = srcend;
|
||||||
}
|
}
|
||||||
@@ -754,7 +885,7 @@ static int dotmplsubst(const u8 *subject, int subjectlen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Use the six version templates and the match data included here
|
// Use the version templates and the match data included here
|
||||||
// to put the version info into the given strings, (as long as the sizes
|
// to put the version info into the given strings, (as long as the sizes
|
||||||
// are sufficient). Returns zero for success. If no template is available
|
// are sufficient). Returns zero for success. If no template is available
|
||||||
// for a string, that string will have zero length after the function
|
// for a string, that string will have zero length after the function
|
||||||
@@ -764,7 +895,10 @@ int ServiceProbeMatch::getVersionStr(const u8 *subject, int subjectlen,
|
|||||||
int *ovector, int nummatches, char *product, int productlen,
|
int *ovector, int nummatches, char *product, int productlen,
|
||||||
char *version, int versionlen, char *info, int infolen,
|
char *version, int versionlen, char *info, int infolen,
|
||||||
char *hostname, int hostnamelen, char *ostype, int ostypelen,
|
char *hostname, int hostnamelen, char *ostype, int ostypelen,
|
||||||
char *devicetype, int devicetypelen) {
|
char *devicetype, int devicetypelen,
|
||||||
|
char *cpe_a, int cpe_alen,
|
||||||
|
char *cpe_h, int cpe_hlen,
|
||||||
|
char *cpe_o, int cpe_olen) {
|
||||||
|
|
||||||
int rc;
|
int rc;
|
||||||
assert(productlen >= 0 && versionlen >= 0 && infolen >= 0 &&
|
assert(productlen >= 0 && versionlen >= 0 && infolen >= 0 &&
|
||||||
@@ -776,6 +910,9 @@ int ServiceProbeMatch::getVersionStr(const u8 *subject, int subjectlen,
|
|||||||
if (hostnamelen > 0) *hostname = '\0';
|
if (hostnamelen > 0) *hostname = '\0';
|
||||||
if (ostypelen > 0) *ostype = '\0';
|
if (ostypelen > 0) *ostype = '\0';
|
||||||
if (devicetypelen > 0) *devicetype = '\0';
|
if (devicetypelen > 0) *devicetype = '\0';
|
||||||
|
if (cpe_alen > 0) *cpe_a = '\0';
|
||||||
|
if (cpe_hlen > 0) *cpe_h = '\0';
|
||||||
|
if (cpe_olen > 0) *cpe_o = '\0';
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
// Now lets get this started! We begin with the product name
|
// Now lets get this started! We begin with the product name
|
||||||
@@ -844,6 +981,42 @@ int ServiceProbeMatch::getVersionStr(const u8 *subject, int subjectlen,
|
|||||||
retval = -1;
|
retval = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* There may be multiple cpe templates. We peek at the first character and
|
||||||
|
store in cpe_a, cpe_h, or cpe_o as appropriate. */
|
||||||
|
for (unsigned int i = 0; i < cpe_templates.size(); i++) {
|
||||||
|
char *cpe;
|
||||||
|
int cpelen;
|
||||||
|
int part;
|
||||||
|
|
||||||
|
part = cpe_get_part(cpe_templates[i]);
|
||||||
|
switch (part) {
|
||||||
|
case 'a':
|
||||||
|
cpe = cpe_a;
|
||||||
|
cpelen = cpe_alen;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
cpe = cpe_h;
|
||||||
|
cpelen = cpe_hlen;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
cpe = cpe_o;
|
||||||
|
cpelen = cpe_olen;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("Warning: ignoring cpe:// template with unknown part '%c' (0x%02X)",
|
||||||
|
isprint(part) ? part : '.', part);
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rc = dotmplsubst(subject, subjectlen, ovector, nummatches, cpe_templates[i], cpe, cpelen, transform_cpe);
|
||||||
|
if (rc != 0) {
|
||||||
|
error("Warning: Servicescan failed to fill cpe_%c (subjectlen: %d, devicetypelen: %d). Too long? Match string was line %d: d/%s/", part, subjectlen, devicetypelen, deflineno,
|
||||||
|
(devicetype_template)? devicetype_template : "");
|
||||||
|
if (devicetypelen > 0) *devicetype = '\0';
|
||||||
|
retval = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@@ -1368,6 +1541,7 @@ ServiceNFO::ServiceNFO(AllProbes *newAP) {
|
|||||||
currentresplen = 0;
|
currentresplen = 0;
|
||||||
product_matched[0] = version_matched[0] = extrainfo_matched[0] = '\0';
|
product_matched[0] = version_matched[0] = extrainfo_matched[0] = '\0';
|
||||||
hostname_matched[0] = ostype_matched[0] = devicetype_matched[0] = '\0';
|
hostname_matched[0] = ostype_matched[0] = devicetype_matched[0] = '\0';
|
||||||
|
cpe_a_matched[0] = cpe_h_matched[0] = cpe_o_matched[0] = '\0';
|
||||||
tunnel = SERVICE_TUNNEL_NONE;
|
tunnel = SERVICE_TUNNEL_NONE;
|
||||||
ssl_session = NULL;
|
ssl_session = NULL;
|
||||||
softMatchFound = false;
|
softMatchFound = false;
|
||||||
@@ -1891,6 +2065,7 @@ static int scanThroughTunnel(nsock_pool nsp, nsock_iod nsi, ServiceGroup *SG,
|
|||||||
svc->probe_matched = NULL;
|
svc->probe_matched = NULL;
|
||||||
svc->product_matched[0] = svc->version_matched[0] = svc->extrainfo_matched[0] = '\0';
|
svc->product_matched[0] = svc->version_matched[0] = svc->extrainfo_matched[0] = '\0';
|
||||||
svc->hostname_matched[0] = svc->ostype_matched[0] = svc->devicetype_matched[0] = '\0';
|
svc->hostname_matched[0] = svc->ostype_matched[0] = svc->devicetype_matched[0] = '\0';
|
||||||
|
svc->cpe_a_matched[0] = svc->cpe_h_matched[0] = svc->cpe_o_matched[0] = '\0';
|
||||||
svc->softMatchFound = false;
|
svc->softMatchFound = false;
|
||||||
svc->resetProbes(true);
|
svc->resetProbes(true);
|
||||||
startNextProbe(nsp, nsi, SG, svc, true);
|
startNextProbe(nsp, nsi, SG, svc, true);
|
||||||
@@ -2221,6 +2396,12 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
|
|||||||
Strncpy(svc->ostype_matched, MD->ostype, sizeof(svc->ostype_matched));
|
Strncpy(svc->ostype_matched, MD->ostype, sizeof(svc->ostype_matched));
|
||||||
if (MD->devicetype)
|
if (MD->devicetype)
|
||||||
Strncpy(svc->devicetype_matched, MD->devicetype, sizeof(svc->devicetype_matched));
|
Strncpy(svc->devicetype_matched, MD->devicetype, sizeof(svc->devicetype_matched));
|
||||||
|
if (MD->cpe_a)
|
||||||
|
Strncpy(svc->cpe_a_matched, MD->cpe_a, sizeof(svc->cpe_a_matched));
|
||||||
|
if (MD->cpe_h)
|
||||||
|
Strncpy(svc->cpe_h_matched, MD->cpe_h, sizeof(svc->cpe_h_matched));
|
||||||
|
if (MD->cpe_o)
|
||||||
|
Strncpy(svc->cpe_o_matched, MD->cpe_o, sizeof(svc->cpe_o_matched));
|
||||||
svc->softMatchFound = MD->isSoft;
|
svc->softMatchFound = MD->isSoft;
|
||||||
if (!svc->softMatchFound) {
|
if (!svc->softMatchFound) {
|
||||||
// We might be able to continue scan through a tunnel protocol
|
// We might be able to continue scan through a tunnel protocol
|
||||||
@@ -2365,11 +2546,15 @@ list<ServiceNFO *>::iterator svc;
|
|||||||
*(*svc)->hostname_matched? (*svc)->hostname_matched : NULL,
|
*(*svc)->hostname_matched? (*svc)->hostname_matched : NULL,
|
||||||
*(*svc)->ostype_matched? (*svc)->ostype_matched : NULL,
|
*(*svc)->ostype_matched? (*svc)->ostype_matched : NULL,
|
||||||
*(*svc)->devicetype_matched? (*svc)->devicetype_matched : NULL,
|
*(*svc)->devicetype_matched? (*svc)->devicetype_matched : NULL,
|
||||||
|
*(*svc)->cpe_a_matched? (*svc)->cpe_a_matched : NULL,
|
||||||
|
*(*svc)->cpe_h_matched? (*svc)->cpe_h_matched : NULL,
|
||||||
|
*(*svc)->cpe_o_matched? (*svc)->cpe_o_matched : NULL,
|
||||||
shouldWePrintFingerprint(*svc) ? (*svc)->getServiceFingerprint(NULL) : NULL);
|
shouldWePrintFingerprint(*svc) ? (*svc)->getServiceFingerprint(NULL) : NULL);
|
||||||
} else {
|
} else {
|
||||||
(*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto,
|
(*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto,
|
||||||
(*svc)->probe_state, NULL,
|
(*svc)->probe_state, NULL,
|
||||||
(*svc)->tunnel, NULL, NULL, NULL, NULL, NULL, NULL,
|
(*svc)->tunnel, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL,
|
||||||
(*svc)->getServiceFingerprint(NULL));
|
(*svc)->getServiceFingerprint(NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2415,7 +2600,8 @@ static void remove_excluded_ports(AllProbes *AP, ServiceGroup *SG) {
|
|||||||
PROBESTATE_EXCLUDED, NULL,
|
PROBESTATE_EXCLUDED, NULL,
|
||||||
SERVICE_TUNNEL_NONE,
|
SERVICE_TUNNEL_NONE,
|
||||||
"Excluded from version scan", NULL,
|
"Excluded from version scan", NULL,
|
||||||
NULL, NULL, NULL, NULL, NULL);
|
NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
SG->services_remaining.erase(i);
|
SG->services_remaining.erase(i);
|
||||||
SG->services_finished.push_back(svc);
|
SG->services_finished.push_back(svc);
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ struct MatchDetails {
|
|||||||
const char *hostname;
|
const char *hostname;
|
||||||
const char *ostype;
|
const char *ostype;
|
||||||
const char *devicetype;
|
const char *devicetype;
|
||||||
|
|
||||||
|
// CPE identifiers for application, OS, and hardware type.
|
||||||
|
const char *cpe_a;
|
||||||
|
const char *cpe_o;
|
||||||
|
const char *cpe_h;
|
||||||
};
|
};
|
||||||
|
|
||||||
/********************** CLASSES ***********************************/
|
/********************** CLASSES ***********************************/
|
||||||
@@ -190,6 +195,7 @@ class ServiceProbeMatch {
|
|||||||
char *hostname_template;
|
char *hostname_template;
|
||||||
char *ostype_template;
|
char *ostype_template;
|
||||||
char *devicetype_template;
|
char *devicetype_template;
|
||||||
|
std::vector<char *> cpe_templates;
|
||||||
// The anchor is for SERVICESCAN_STATIC matches. If the anchor is not -1, the match must
|
// The anchor is for SERVICESCAN_STATIC matches. If the anchor is not -1, the match must
|
||||||
// start at that zero-indexed position in the response str.
|
// start at that zero-indexed position in the response str.
|
||||||
int matchops_anchor;
|
int matchops_anchor;
|
||||||
@@ -205,7 +211,10 @@ class ServiceProbeMatch {
|
|||||||
int nummatches, char *product, int productlen,
|
int nummatches, char *product, int productlen,
|
||||||
char *version, int versionlen, char *info, int infolen,
|
char *version, int versionlen, char *info, int infolen,
|
||||||
char *hostname, int hostnamelen, char *ostype, int ostypelen,
|
char *hostname, int hostnamelen, char *ostype, int ostypelen,
|
||||||
char *devicetype, int devicetypelen);
|
char *devicetype, int devicetypelen,
|
||||||
|
char *cpe_a, int cpe_alen,
|
||||||
|
char *cpe_h, int cpe_hlen,
|
||||||
|
char *cpe_o, int cpe_olen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
utils.cc
17
utils.cc
@@ -449,6 +449,23 @@ void bintohexstr(char *buf, int buflen, char *src, int srclen){
|
|||||||
bp += Snprintf(buf+bp, buflen-bp,"\n");
|
bp += Snprintf(buf+bp, buflen-bp,"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the CPE part (first component of the URL, should be "a", "h", or "o") as
|
||||||
|
a character: 'a', 'h', or 'o'. Returns -1 on error. */
|
||||||
|
int cpe_get_part(const char *cpe) {
|
||||||
|
const char *PREFIX = "cpe:/";
|
||||||
|
char part;
|
||||||
|
|
||||||
|
if (strncmp(cpe, PREFIX, strlen(PREFIX) != 0))
|
||||||
|
return -1;
|
||||||
|
/* This could be more robust, by decoding character escapes and checking ':'
|
||||||
|
boundaries. */
|
||||||
|
part = cpe[strlen(PREFIX)];
|
||||||
|
|
||||||
|
if (part == 'a' || part == 'h' || part == 'o')
|
||||||
|
return part;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* mmap() an entire file into the address space. Returns a pointer
|
/* mmap() an entire file into the address space. Returns a pointer
|
||||||
|
|||||||
4
utils.h
4
utils.h
@@ -199,6 +199,10 @@ void bintohexstr(char *buf, int buflen, char *src, int srclen);
|
|||||||
char *strerror(int errnum);
|
char *strerror(int errnum);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Get the CPE part (first component of the URL, should be "a", "h", or "o") as
|
||||||
|
a character: 'a', 'h', or 'o'. Returns -1 on error. */
|
||||||
|
int cpe_get_part(const char *cpe);
|
||||||
|
|
||||||
/* mmap() an entire file into the address space. Returns a pointer
|
/* mmap() an entire file into the address space. Returns a pointer
|
||||||
to the beginning of the file. The mmap'ed length is returned
|
to the beginning of the file. The mmap'ed length is returned
|
||||||
inside the length parameter. If there is a problem, NULL is
|
inside the length parameter. If there is a problem, NULL is
|
||||||
|
|||||||
Reference in New Issue
Block a user