From 487c08ff63c364f3b9824ad3272b76bae414ae95 Mon Sep 17 00:00:00 2001 From: henri Date: Fri, 13 Jan 2012 10:24:19 +0000 Subject: [PATCH] Make gathered CPE codes available to NSE. CPEs are available at host.os (for the ones from OS fingerprinting) and port.version.cpe (for the version detection ones). This patch also fix a memory leak that David noticed in PortList::setServiceProbeResults(). --- CHANGELOG | 6 ++++++ docs/scripting.xml | 15 +++++++++++++++ nse_nmaplib.cc | 35 ++++++++++++++++++++++++++++++++--- portlist.cc | 32 +++++++++++++++++++------------- portlist.h | 4 ++-- service_scan.cc | 18 ++++++++++++------ 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1b16e713a..dd2e2dc9d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Made gathered CPE codes available to NSE. [Henri] + +o [NSE] Fixed a memory leak in PortList::setServiceProbeResults() noticed and + reported by David. The leak was triggered by set_port_version calls from NSE. + [Henri] + o [NSE] Added http-generator.nse by Michael Kohl, which gets version information for web applications that set the "generator" meta element. diff --git a/docs/scripting.xml b/docs/scripting.xml index 1e9c38dad..e4604739c 100644 --- a/docs/scripting.xml +++ b/docs/scripting.xml @@ -1630,6 +1630,12 @@ LUALIB_API int luaopen_openssl(lua_State *L) { option, then host.os is nil. + + Additionally the table can contain CPE codes for the detected + operating system. These codes, as described in + the official CPE specification + all start with the cpe:/ prefix. + @@ -1890,6 +1896,15 @@ LUALIB_API int luaopen_openssl(lua_State *L) { nil if rpc_status is anything other than good_prog. + + + cpe + + List of CPE codes for the detected service. As described in the + official CPE specification these strings + all start with the cpe:/ prefix. + + diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index 23baf1c16..11b671f00 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -32,6 +32,8 @@ static const int NSE_PROTOCOL[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP}; void set_version (lua_State *L, const struct serviceDeductions *sd) { + size_t i; + setsfield(L, -1, "name", sd->name); setnfield(L, -1, "name_confidence", sd->name_confidence); setsfield(L, -1, "product", sd->product); @@ -61,6 +63,13 @@ void set_version (lua_State *L, const struct serviceDeductions *sd) setnfield(L, -1, "rpc_lowver", sd->rpc_lowver); setnfield(L, -1, "rpc_highver", sd->rpc_highver); } + + lua_newtable(L); + for (i = 0; i < sd->cpe.size(); i++) { + lua_pushstring(L, sd->cpe[i]); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "cpe"); } /* set some port state information onto the @@ -179,7 +188,8 @@ void set_hostinfo(lua_State *L, Target *currenths) { FPR->overall_results == OSSCAN_SUCCESS && FPR->num_perfect_matches > 0 && FPR->num_perfect_matches <= 8 ) { - int i; + int i, classno; + const OS_Classification_Results *OSR = FPR->getOSClassification(); lua_newtable(L); // this will run at least one time and at most 8 times, see if condition @@ -187,6 +197,15 @@ void set_hostinfo(lua_State *L, Target *currenths) { lua_pushstring(L, FPR->matches[i]->OS_name); lua_rawseti(L, -2, i+1); } + + for (classno = 0; classno < OSR->OSC_num_matches; classno++) { + size_t j; + + for (j = 0; j < OSR->OSC[classno]->cpe.size(); j++) { + lua_pushstring(L, OSR->OSC[classno]->cpe[j]); + lua_rawseti(L, -2, ++i); + } + } lua_setfield(L, -2, "os"); } } @@ -508,6 +527,7 @@ static int l_set_port_version (lua_State *L) Target *target; Port *p; Port port; + std::vector cpe; enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE; enum serviceprobestate probestate = opversion[luaL_checkoption(L, 3, "hardmatched", ops)]; @@ -537,14 +557,23 @@ static int l_set_port_version (lua_State *L) else luaL_argerror(L, 2, "invalid value for port.version.service_tunnel"); + lua_getfield(L, 4, "cpe"); + if (!lua_istable(L, -1)) + luaL_error(L, "port.version 'cpe' field must be a table"); + + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + cpe.push_back(lua_tostring(L, -1)); + } + if (o.servicescan) target->ports.setServiceProbeResults(p->portno, p->proto, probestate, name, tunnel, product, - version, extrainfo, hostname, ostype, devicetype, NULL, NULL, NULL, NULL); + version, extrainfo, hostname, ostype, devicetype, + (cpe.size() > 0) ? &cpe : NULL, NULL); else target->ports.setServiceProbeResults(p->portno, p->proto, probestate, name, tunnel, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, NULL); return 0; } diff --git a/portlist.cc b/portlist.cc index 05fb60fc8..4816c3c78 100644 --- a/portlist.cc +++ b/portlist.cc @@ -115,7 +115,7 @@ Port::Port() { state_reason_init(&reason); } -void Port::freeService() { +void Port::freeService(bool del_service) { if (service != NULL) { std::vector::iterator it; @@ -137,7 +137,10 @@ void Port::freeService() { free(service->service_fp); for (it = service->cpe.begin(); it != service->cpe.end(); it++) free(*it); - delete service; + service->cpe.clear(); + + if (del_service) + delete service; } } @@ -336,8 +339,9 @@ void PortList::setServiceProbeResults(u16 portno, int protocol, enum serviceprobestate sres, const char *sname, enum service_tunnel_type tunnel, const char *product, const char *version, const char *extrainfo, const char *hostname, const char *ostype, - const char *devicetype, const char *cpe_a, const char *cpe_h, const char *cpe_o, + const char *devicetype, const std::vector *cpe, const char *fingerprint) { + std::vector::iterator it; Port *port; char *p; @@ -370,6 +374,8 @@ void PortList::setServiceProbeResults(u16 portno, int protocol, // port->serviceprobe_results = sres; port->service->service_tunnel = tunnel; + port->freeService(false); + if (sname) port->service->name = strdup(sname); else @@ -387,15 +393,15 @@ void PortList::setServiceProbeResults(u16 portno, int protocol, port->service->ostype = cstringSanityCheck(ostype, 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); + if (cpe) { + std::vector::const_iterator cit; + + for (cit = cpe->begin(); cit != cpe->end(); cit++) { + p = cstringSanityCheck(*cit, 80); + if (p != NULL) + port->service->cpe.push_back(p); + } + } } /* Sets the results of an RPC scan. if rpc_status is not @@ -498,7 +504,7 @@ PortList::~PortList() { if(port_list[proto]) { for(i=0; i < port_list_count[proto]; i++) { // free every Port if(port_list[proto][i]) { - port_list[proto][i]->freeService(); + port_list[proto][i]->freeService(true); delete port_list[proto][i]; } } diff --git a/portlist.h b/portlist.h index 026f111af..317127f22 100644 --- a/portlist.h +++ b/portlist.h @@ -178,7 +178,7 @@ class Port { public: Port(); - void freeService(); + void freeService(bool del_service); void getNmapServiceName(char *namebuf, int buflen, const char *rpcinfo) const; u16 portno; @@ -267,7 +267,7 @@ class PortList { const char *version, const char *hostname, const char *ostype, const char *devicetype, const char *extrainfo, - const char *cpe_a, const char *cpe_h, const char *cpe_o, + const std::vector *cpe, const char *fingerprint); // pass in an allocated struct serviceDeductions (don't worry about initializing, and diff --git a/service_scan.cc b/service_scan.cc index 04dc02be7..514c2fef7 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -2536,6 +2536,15 @@ list::iterator svc; for(svc = SG->services_finished.begin(); svc != SG->services_finished.end(); svc++) { if ((*svc)->probe_state != PROBESTATE_FINISHED_NOMATCH) { + vector cpe; + + if (*(*svc)->cpe_a_matched) + cpe.push_back((*svc)->cpe_a_matched); + if (*(*svc)->cpe_h_matched) + cpe.push_back((*svc)->cpe_h_matched); + if (*(*svc)->cpe_o_matched) + cpe.push_back((*svc)->cpe_o_matched); + (*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto, (*svc)->probe_state, (*svc)->probe_matched, @@ -2546,15 +2555,13 @@ list::iterator svc; *(*svc)->hostname_matched? (*svc)->hostname_matched : NULL, *(*svc)->ostype_matched? (*svc)->ostype_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, + (cpe.size() > 0) ? &cpe : NULL, shouldWePrintFingerprint(*svc) ? (*svc)->getServiceFingerprint(NULL) : NULL); } else { (*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto, (*svc)->probe_state, NULL, (*svc)->tunnel, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, (*svc)->getServiceFingerprint(NULL)); } } @@ -2600,8 +2607,7 @@ static void remove_excluded_ports(AllProbes *AP, ServiceGroup *SG) { PROBESTATE_EXCLUDED, NULL, SERVICE_TUNNEL_NONE, "Excluded from version scan", NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, NULL); SG->services_remaining.erase(i); SG->services_finished.push_back(svc);