Skip to content

Commit

Permalink
switch nmap plugin to a custom nmap result XML parser.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 320029584
Change-Id: Ia5e35de123499e20eb3d69a12867f3a052f7905b
  • Loading branch information
magl0 authored and Copybara-Service committed Jul 7, 2020
1 parent e5d47f7 commit 10b1f90
Show file tree
Hide file tree
Showing 54 changed files with 3,592 additions and 244 deletions.
62 changes: 0 additions & 62 deletions google/portscan/nmap/build.gradle
Expand Up @@ -46,71 +46,13 @@ java {
}
}

// Begin: Nmap XML Jaxb class generation.
def dtdFileSrc = 'https://raw.githubusercontent.com/nmap/nmap/e7e7e9e8c7d83b4ca93a752dea7bb40cbb74df32/docs/nmap.dtd'
def dtdFileLocal = "${buildDir}/third_party/nmap/nmap.dtd"
def nmapJaxbSrc = "${buildDir}/generated/nmap_dtd"
def jaxbPkgName = 'com.google.tsunami.plugins.portscan.nmap.client.data.xml'

configurations {
nmapDtd2Java
}

sourceSets {
main {
java.srcDirs "${nmapJaxbSrc}"
}
}

task nmapDtd2Java() {
doLast {
def jaxbTargetDir = file("${nmapJaxbSrc}")
if (!jaxbTargetDir.exists()) {
jaxbTargetDir.mkdirs()
}
ant.taskdef(name: 'xjc',
classname: 'com.sun.tools.xjc.XJCTask',
classpath: configurations.nmapDtd2Java.asPath)
ant.jaxbTargetDir = jaxbTargetDir
ant.xjc(destdir: "${jaxbTargetDir}",
package: "${jaxbPkgName}",
schema: "${dtdFileLocal}",
fork: true
) {
arg(line: '-dtd')
jvmarg(value: '-DenableExternalEntityProcessing=true')
}
}
}

task downloadNmapDtd(type: AntDownload) {
sourceUrl = "${dtdFileSrc}"
target = new File("${dtdFileLocal}")
}

class AntDownload extends DefaultTask {
@Input
String sourceUrl
@OutputFile
File target

@TaskAction
void download() {
ant.get(src: sourceUrl, dest: target)
}
}

compileJava.dependsOn nmapDtd2Java
nmapDtd2Java.dependsOn downloadNmapDtd
// End: Nmap XML Jaxb class generation.

ext {
autoValueVersion = '1.7'
floggerVersion = '0.5.1'
guavaVersion = '28.2-jre'
guiceVersion = '4.2.3'
javaxInjectVersion = '1'
jaxbVersion = '2.3.1'
okhttpVersion = '3.12.0'
protobufVersion = '3.11.4'
tsunamiVersion = '0.0.1'
Expand All @@ -121,7 +63,6 @@ ext {
}

dependencies {
api "javax.xml.bind:jaxb-api:${jaxbVersion}"
implementation "com.google.auto.value:auto-value-annotations:${autoValueVersion}"
implementation "com.google.flogger:flogger:${floggerVersion}"
implementation "com.google.flogger:google-extensions:${floggerVersion}"
Expand All @@ -135,7 +76,6 @@ dependencies {
implementation "com.google.tsunami:tsunami-plugin:${tsunamiVersion}"
implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}"
implementation "javax.inject:javax.inject:${javaxInjectVersion}"
implementation "org.glassfish.jaxb:jaxb-runtime:${jaxbVersion}"
implementation "org.mockito:mockito-core:${mockitoVersion}"
annotationProcessor "com.google.auto.value:auto-value:${autoValueVersion}"

Expand All @@ -144,6 +84,4 @@ dependencies {
testImplementation "com.google.truth.extensions:truth-proto-extension:${truthVersion}"
testImplementation "junit:junit:${junitVersion}"
testImplementation "org.mockito:mockito-core:${mockitoVersion}"

nmapDtd2Java "org.glassfish.jaxb:jaxb-xjc:${jaxbVersion}"
}
Expand Up @@ -31,14 +31,13 @@
import com.google.tsunami.plugins.portscan.nmap.client.NmapClient.DnsResolution;
import com.google.tsunami.plugins.portscan.nmap.client.NmapClient.ScanTechnique;
import com.google.tsunami.plugins.portscan.nmap.client.NmapClient.TimingTemplate;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Address;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Host;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Hostname;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Nmaprun;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Port;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Ports;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Script;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Service;
import com.google.tsunami.plugins.portscan.nmap.client.result.Address;
import com.google.tsunami.plugins.portscan.nmap.client.result.Host;
import com.google.tsunami.plugins.portscan.nmap.client.result.Hostname;
import com.google.tsunami.plugins.portscan.nmap.client.result.NmapRun;
import com.google.tsunami.plugins.portscan.nmap.client.result.Port;
import com.google.tsunami.plugins.portscan.nmap.client.result.Ports;
import com.google.tsunami.plugins.portscan.nmap.client.result.Script;
import com.google.tsunami.proto.NetworkEndpoint;
import com.google.tsunami.proto.NetworkService;
import com.google.tsunami.proto.PortScanningReport;
Expand All @@ -55,15 +54,16 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;

/** A {@link PortScanner} plugin that uses nmap to scan for open ports and running services. */
@PluginInfo(
type = PluginType.PORT_SCAN,
name = "NmapPortScanner",
version = "0.1",
description = "Identifies open ports and fingerprints underlying services using nmap.",
author = "Tsunami Dev (tsunami-dev@google.com)",
author = "Tsunami Team (tsunami-dev@google.com)",
bootstrapModule = NmapPortScannerBootstrapModule.class)
public final class NmapPortScanner implements PortScanner {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
Expand All @@ -90,7 +90,7 @@ public PortScanningReport scan(ScanTarget scanTarget) {
try {
logger.atInfo().log("Starting nmap scan.");
Stopwatch stopwatch = Stopwatch.createStarted();
Nmaprun result =
NmapRun result =
setPortTargets(nmapClient)
.withDnsResolution(DnsResolution.NEVER)
.treatAllHostsAsOnline()
Expand All @@ -106,7 +106,11 @@ public PortScanningReport scan(ScanTarget scanTarget) {
"Finished nmap scan on target '%s' in %s.",
loggableScanTarget(scanTarget), stopwatch.stop());
return extractServicesFromNmapRun(result);
} catch (IOException | JAXBException | InterruptedException | ExecutionException e) {
} catch (IOException
| InterruptedException
| ExecutionException
| SAXException
| ParserConfigurationException e) {
logger.atSevere().withCause(e).log("Nmap scan failed.");
return PortScanningReport.newBuilder()
.setTargetInfo(
Expand Down Expand Up @@ -136,16 +140,16 @@ private NmapClient setPortTargets(NmapClient nmapClient) {
return nmapClient;
}

private PortScanningReport extractServicesFromNmapRun(Nmaprun nmaprun) {
private PortScanningReport extractServicesFromNmapRun(NmapRun nmapRun) {
logger.atInfo().log("Building PortScanningReport from Nmap result.");
PortScanningReport.Builder portScanningReportBuilder =
PortScanningReport.newBuilder().setTargetInfo(buildTargetInfoFromNmaprun(nmaprun));
PortScanningReport.newBuilder().setTargetInfo(buildTargetInfoFromNmaprun(nmapRun));

Optional<Host> host = getHostFromNmapRun(nmaprun);
Optional<Host> host = getHostFromNmapRun(nmapRun);
host.flatMap(NmapPortScanner::getPortsFromHost)
.ifPresent(
ports ->
ports.getPort().stream()
ports.ports().stream()
.filter(NmapPortScanner::isPortOpen)
.forEach(
port -> {
Expand All @@ -156,10 +160,10 @@ private PortScanningReport extractServicesFromNmapRun(Nmaprun nmaprun) {
return portScanningReportBuilder.build();
}

private TargetInfo buildTargetInfoFromNmaprun(Nmaprun nmaprun) {
private TargetInfo buildTargetInfoFromNmaprun(NmapRun nmapRun) {
return TargetInfo.newBuilder()
.addNetworkEndpoints(
getHostFromNmapRun(nmaprun)
getHostFromNmapRun(nmapRun)
.map(this::buildNetworkEndpointFromHost)
.orElse(scanTarget.getNetworkEndpoint()))
.build();
Expand All @@ -169,9 +173,9 @@ private NetworkEndpoint buildNetworkEndpointFromHost(Host host) {
Optional<Address> address = getAddressFromHost(host);
Optional<Hostname> hostname = getHostnameFromHost(host);
if (address.isPresent()) {
return NetworkEndpointUtils.forIp(address.get().getAddr());
return NetworkEndpointUtils.forIp(address.get().addr());
} else if (hostname.isPresent()) {
return NetworkEndpointUtils.forHostname(hostname.get().getName());
return NetworkEndpointUtils.forHostname(hostname.get().name());
} else {
return scanTarget.getNetworkEndpoint();
}
Expand All @@ -189,65 +193,47 @@ private NetworkService buildNetworkService(Host host, Port port) {
getSoftwareFromPort(port).ifPresent(networkServiceBuilder::setSoftware);
getSoftwareVersionSetFromPort(port).ifPresent(networkServiceBuilder::setVersionSet);
getBannerScriptFromPort(port)
.ifPresent(script -> networkServiceBuilder.addBanner(script.getOutput()));
.ifPresent(script -> networkServiceBuilder.addBanner(script.output()));
return networkServiceBuilder.build();
}

private static Optional<Script> getBannerScriptFromPort(Port port) {
return port.getScript().stream()
.filter(script -> Ascii.equalsIgnoreCase("banner", Strings.nullToEmpty(script.getId())))
return port.scripts().stream()
.filter(script -> Ascii.equalsIgnoreCase("banner", Strings.nullToEmpty(script.id())))
.findFirst();
}

private static Optional<Host> getHostFromNmapRun(Nmaprun nmaprun) {
return nmaprun
.getTargetOrTaskbeginOrTaskprogressOrTaskendOrPrescriptOrPostscriptOrHostOrOutput()
.stream()
.filter(el -> el instanceof Host)
.findFirst()
.map(Host.class::cast);
private static Optional<Host> getHostFromNmapRun(NmapRun nmapRun) {
return nmapRun.hosts().stream().findFirst();
}

private static Optional<Address> getAddressFromHost(Host host) {
return host
.getStatusOrAddressOrHostnamesOrSmurfOrPortsOrOsOrDistanceOrUptimeOrTcpsequenceOrIpidsequenceOrTcptssequenceOrHostscriptOrTraceOrTimes()
.stream()
.filter(el -> el instanceof Address)
.findFirst()
.map(Address.class::cast);
return host.addresses().stream().findFirst();
}

private static Optional<Hostname> getHostnameFromHost(Host host) {
return host
.getStatusOrAddressOrHostnamesOrSmurfOrPortsOrOsOrDistanceOrUptimeOrTcpsequenceOrIpidsequenceOrTcptssequenceOrHostscriptOrTraceOrTimes()
.stream()
.filter(el -> el instanceof Hostname)
.findFirst()
.map(Hostname.class::cast);
return host.hostnames().stream()
.flatMap(hostnames -> hostnames.hostnames().stream())
.findFirst();
}

private static Optional<Ports> getPortsFromHost(Host host) {
return host
.getStatusOrAddressOrHostnamesOrSmurfOrPortsOrOsOrDistanceOrUptimeOrTcpsequenceOrIpidsequenceOrTcptssequenceOrHostscriptOrTraceOrTimes()
.stream()
.filter(el -> el instanceof Ports)
.findFirst()
.map(Ports.class::cast);
return host.ports().stream().findFirst();
}

private static boolean isPortOpen(Port port) {
return port.getState().getState().equals("open");
return Ascii.equalsIgnoreCase("open", Strings.nullToEmpty(port.state().state()));
}

private static Optional<Software> getSoftwareFromPort(Port port) {
return Optional.ofNullable(port.getService())
.map(Service::getProduct)
return Optional.ofNullable(port.service())
.map(service -> Strings.emptyToNull(service.product()))
.map(product -> Software.newBuilder().setName(product).build());
}

private static Optional<VersionSet> getSoftwareVersionSetFromPort(Port port) {
return Optional.ofNullable(port.getService())
.map(Service::getVersion)
return Optional.ofNullable(port.service())
.map(service -> Strings.emptyToNull(service.version()))
.map(
version ->
VersionSet.newBuilder()
Expand All @@ -259,15 +245,15 @@ private static Optional<VersionSet> getSoftwareVersionSetFromPort(Port port) {
}

private static int getPortNumberFromPort(Port port) {
return Integer.parseInt(port.getPortid());
return Integer.parseInt(port.portId());
}

private static TransportProtocol getTransportProtocolFromPort(Port port) {
return TransportProtocol.valueOf(Ascii.toUpperCase(port.getProtocol()));
return TransportProtocol.valueOf(Ascii.toUpperCase(port.protocol()));
}

private static Optional<String> getServiceNameFromPort(Port port) {
return Optional.ofNullable(port.getService()).map(Service::getName);
return Optional.ofNullable(port.service()).map(service -> Strings.emptyToNull(service.name()));
}

private static void logIdentifiedNetworkService(NetworkService networkService) {
Expand Down
Expand Up @@ -28,8 +28,8 @@
import com.google.tsunami.plugins.portscan.nmap.client.data.PortRange;
import com.google.tsunami.plugins.portscan.nmap.client.data.ScriptAndArgs;
import com.google.tsunami.plugins.portscan.nmap.client.data.SinglePort;
import com.google.tsunami.plugins.portscan.nmap.client.data.xml.Nmaprun;
import com.google.tsunami.plugins.portscan.nmap.client.parser.XMLParser;
import com.google.tsunami.plugins.portscan.nmap.client.result.NmapRun;
import com.google.tsunami.proto.NetworkEndpoint;
import java.io.File;
import java.io.FileInputStream;
Expand All @@ -43,7 +43,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;

/**
* Client for the open-source nmap tool. Nmap is a network security scanner with support for
Expand All @@ -54,7 +55,7 @@
* and 9919:
*
* <pre>
* Nmaprun scanRunResult =
* NmapRun scanRunResult =
* new NmapClient(nmapFile.getAbsolutePath())
* .withTargetNetworkEndpoint(NetworkEndpoints.forIp("1.1.1.1"))
* .withHostDiscoveryTechnique(HostDiscoveryTechnique.SYN)
Expand Down Expand Up @@ -231,8 +232,9 @@ public NmapClient(String nmapBinaryPath, File report) {
* executor suitable for long running and IO blocking tasks. {@link
* java.util.concurrent.ThreadPoolExecutor} is a viable option.
*/
public Nmaprun run(Executor executor)
throws IOException, InterruptedException, ExecutionException, JAXBException {
public NmapRun run(Executor executor)
throws IOException, InterruptedException, ExecutionException, ParserConfigurationException,
SAXException {
ArrayList<String> arrayList = buildRunCommandArgs();
String[] args = arrayList.toArray(new String[0]);
CommandExecutor commandExecutor = CommandExecutorFactory.create(args);
Expand Down

0 comments on commit 10b1f90

Please sign in to comment.