Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions scanners/nmap/.helm-docs.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ usecase: "Network discovery and security auditing"
Nmap ("Network Mapper") is a free and open source (license) utility for network discovery and security auditing. Many systems and network administrators also find it useful for tasks such as network inventory, managing service upgrade schedules, and monitoring host or service uptime.

To learn more about the Nmap scanner itself visit [nmap.org].

## NSE scripts

Currently, the secureCodeBox Nmap parser supports the `smb-protocols`, `ftp-anon`, and `ftp-banner` script with compatibility for hostrules and portrules. If you want custom scripts to be parsed by secureCodeBox, you may contribute your script parser in `parser/parser.js` under `const scriptParser`

```javascript
const scriptParser = {
"ftp-anon": parseFtpAnon,
"banner": parseBanner,
"smb-protocols": parseSmbProtocols,
[...] // Contributed custom parsers
}
```

Scripts without an existing parser will only generate an open port finding. Scripts with parsers will add a script output finding.

{{- end }}

{{- define "extra.scannerConfigurationSection" -}}
Expand Down
15 changes: 15 additions & 0 deletions scanners/nmap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ Nmap ("Network Mapper") is a free and open source (license) utility for network

To learn more about the Nmap scanner itself visit [nmap.org].

## NSE scripts

Currently, the secureCodeBox Nmap parser supports the `smb-protocols`, `ftp-anon`, and `ftp-banner` script with compatibility for hostrules and portrules. If you want custom scripts to be parsed by secureCodeBox, you may contribute your script parser in `parser/parser.js` under `const scriptParser`

```javascript
const scriptParser = {
"ftp-anon": parseFtpAnon,
"banner": parseBanner,
"smb-protocols": parseSmbProtocols,
[...] // Contributed custom parsers
}
```

Scripts without an existing parser will only generate an open port finding. Scripts with parsers will add a script output finding.

## Deployment
The nmap chart can be deployed via helm:

Expand Down
25 changes: 25 additions & 0 deletions scanners/nmap/parser/__testFiles__/ftp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
SPDX-FileCopyrightText: 2021 Secura

SPDX-License-Identifier: NOASSERTION
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.91 scan initiated Mon Sep 20 09:23:51 2021 as: nmap -oX /home/securecodebox/nmap-results.xml -Pn -p21 -&#45;script banner -&#45;script ftp-anon 10.103.42.74 -->
<nmaprun scanner="nmap" args="nmap -oX /home/securecodebox/nmap-results.xml -Pn -p21 -&#45;script banner -&#45;script ftp-anon 10.103.42.74" start="1632129831" startstr="Mon Sep 20 09:23:51 2021" version="7.91" xmloutputversion="1.05">
<scaninfo type="connect" protocol="tcp" numservices="1" services="21"/>
<verbose level="0"/>
<debugging level="0"/>
<host starttime="1632129831" endtime="1632129831"><status state="up" reason="user-set" reason_ttl="0"/>
<address addr="10.103.42.74" addrtype="ipv4"/>
<hostnames>
<hostname name="dummy-ftp.demo-targets.svc.cluster.local" type="PTR"/>
</hostnames>
<ports><port protocol="tcp" portid="21"><state state="open" reason="syn-ack" reason_ttl="0"/><service name="ftp" method="table" conf="3"/><script id="banner" output="220-&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45; Welcome to Pure-FTPd [privsep] [TLS] -&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#45;\x&#xa;0D\x0A220-You are user number 2 of 30 allowed.\x0D\x0A220-Local time...&#xa;"/><script id="ftp-anon" output="Anonymous FTP login allowed (FTP code 230)&#xa;Can&apos;t get directory listing: PASV IP 127.0.0.1 is not the same as 10.103.42.74"/></port>
</ports>
<times srtt="105" rttvar="5000" to="100000"/>
</host>
<runstats><finished time="1632129831" timestr="Mon Sep 20 09:23:51 2021" summary="Nmap done at Mon Sep 20 09:23:51 2021; 1 IP address (1 host up) scanned in 0.32 seconds" elapsed="0.32" exit="success"/><hosts up="1" down="0" total="1"/>
</runstats>
</nmaprun>
233 changes: 128 additions & 105 deletions scanners/nmap/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0

const xml2js = require('xml2js');
const { get } = require('lodash');
const { get, merge } = require('lodash');


async function parse(fileContent) {
const hosts = await parseResultFile(fileContent);
Expand Down Expand Up @@ -72,10 +73,10 @@ function transformNMAPScripts(hosts) {

if(host.scripts) {
for(const script of host.scripts) {

// Parse SMB Script Results
if(script.$.id === 'smb-protocols') {
transformNmapScriptSmb(host, script ,scriptFindings);
// Parse Script Results
const parseFunction = scriptParser[script.$.id];
if (parseFunction) {
scriptFindings = scriptFindings.concat(parseFunction(host, script));
}
}
}
Expand All @@ -84,29 +85,75 @@ function transformNMAPScripts(hosts) {
return scriptFindings;
}

function transformNmapScriptSmb(host, script, scriptFindings) {
const scriptParser = {
"ftp-anon": parseFtpAnon,
"banner": parseBanner,
"smb-protocols": parseSmbProtocols,
}

function parseFtpAnon(host, script) {
return [merge(
{
name: "Anonymous FTP Login possible",
description: `Port ${host.openPorts[0].port} allows anonymous FTP login`,
severity: 'MEDIUM',
},
parseFtpCommon(host, script)
)]
}

function parseBanner(host, script) {
return [merge(
{
name: "Server banner found",
description: `Port ${host.openPorts[0].port} displays banner`,
severity: 'INFORMATIONAL',
attributes: {
banner: script.$.output || null,
},
},
host.openPorts[0].port === 21 ? parseFtpCommon(host, script) : parseCommon(host,script)
)]
}

function parseFtpCommon(host, script) {
return {
category: 'FTP',
location: `ftp://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
attributes: {
script: script.$.id || null,
},
}
}

function parseCommon(host, script) {
return {
category: 'TCP',
location: `tcp://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
attributes: {
script: script.$.id || null,
},
}
}

function parseSmbProtocols(host, script) {
// Parse SMB Script Results
if(script.$.id === 'smb-protocols') {
console.log ("Found SMB Script Result: " + script.$.output);
//console.log (script);

if(script.table && script.table[0] && script.table[0].elem) {

for(const elem of script.table[0].elem) {
console.log ("Found SMB SMB Protocol: " + elem);
//console.log (elem);

const smbVersion = parseFloat(elem);

if(elem.toString().includes("SMBv1")) {
scriptFindings.push({
name: "SMB Dangerous Protocol Version Finding SMBv1",
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: SMBv1`,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'HIGH',
attributes: {
console.log ("Found SMB Script Result: " + script.$.output);
//console.log (script);

var scriptFindings = [];

if(script.table && script.table[0] && script.table[0].elem) {

for(const elem of script.table[0].elem) {
console.log ("Found SMB SMB Protocol: " + elem);
//console.log (elem);

const smbVersion = elem.toString().includes("SMBv1") ? 1 : parseFloat(elem);

const attributes = {
hostname: host.hostname,
mac_address: host.mac || null,
ip_address: host.ip,
Expand All @@ -119,90 +166,58 @@ function transformNmapScriptSmb(host, script, scriptFindings) {
serviceProduct: host.openPorts[0].serviceProduct || null,
serviceVersion: host.openPorts[0].serviceVersion || null,
scripts: elem || null,
smb_protocol_version: 1,
smb_protocol_version: smbVersion,
}

if(elem.toString().includes("SMBv1")) {
scriptFindings.push({
name: "SMB Dangerous Protocol Version Finding SMBv1",
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: SMBv1`,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'HIGH',
attributes: attributes
});
}
else if(!isNaN(smbVersion)) {
if(smbVersion > 0 && smbVersion < 2) {
scriptFindings.push({
name: "SMB Dangerous Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: ` + smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'MEDIUM',
attributes: attributes
});
}
else if(!isNaN(smbVersion)) {
if(smbVersion > 0 && smbVersion < 2) {
scriptFindings.push({
name: "SMB Dangerous Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: ` + smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'MEDIUM',
attributes: {
hostname: host.hostname,
mac_address: host.mac || null,
ip_address: host.ip,
port: host.openPorts[0].port,
state: host.openPorts[0].state,
protocol: host.openPorts[0].protocol,
method: host.openPorts[0].method,
operating_system: host.osNmap || null,
service: host.openPorts[0].service,
serviceProduct: host.openPorts[0].serviceProduct || null,
serviceVersion: host.openPorts[0].serviceVersion || null,
scripts: elem || null,
smb_protocol_version: smbVersion,
}
});
}
if(smbVersion >= 2 && smbVersion < 3) {
scriptFindings.push({
name: "SMB Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: `+ smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'LOW',
attributes: {
hostname: host.hostname,
mac_address: host.mac || null,
ip_address: host.ip,
port: host.openPorts[0].port,
state: host.openPorts[0].state,
protocol: host.openPorts[0].protocol,
method: host.openPorts[0].method,
operating_system: host.osNmap || null,
service: host.openPorts[0].service,
serviceProduct: host.openPorts[0].serviceProduct || null,
serviceVersion: host.openPorts[0].serviceVersion || null,
scripts: elem || null,
smb_protocol_version: smbVersion,
}
});
}
if(smbVersion >= 3) {
scriptFindings.push({
name: "SMB Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with version: ` + smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'INFORMATIONAL',
attributes: {
hostname: host.hostname,
mac_address: host.mac || null,
ip_address: host.ip,
port: host.openPorts[0].port,
state: host.openPorts[0].state,
protocol: host.openPorts[0].protocol,
method: host.openPorts[0].method,
operating_system: host.osNmap || null,
service: host.openPorts[0].service,
serviceProduct: host.openPorts[0].serviceProduct || null,
serviceVersion: host.openPorts[0].serviceVersion || null,
scripts: elem || null,
smb_protocol_version: smbVersion,
}
});
}
else if(smbVersion >= 2 && smbVersion < 3) {
scriptFindings.push({
name: "SMB Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with an old version: `+ smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'LOW',
attributes: attributes
});
}
else if(smbVersion >= 3) {
scriptFindings.push({
name: "SMB Protocol Version Finding v"+smbVersion,
description: `Port ${host.openPorts[0].port} is ${host.openPorts[0].state} using SMB protocol with version: ` + smbVersion,
category: 'SMB',
location: `${host.openPorts[0].protocol}://${host.ip}:${host.openPorts[0].port}`,
osi_layer: 'NETWORK',
severity: 'INFORMATIONAL',
attributes: attributes
});
}
}
}
}
return scriptFindings
}

/**
Expand Down Expand Up @@ -322,7 +337,15 @@ function parseResultFile(fileContent) {
if(host.hostscript && host.hostscript[0].script) {
newHost.scripts = host.hostscript[0].script
}

// Get Script Content in case the script is of the port-rule type,
// and thus has the script under 'port' instead of 'hostscript'.
else if(host.ports && host.ports[0].port){
for (let i=0; i < host.ports[0].port.length; i++){
if ((host.ports[0].port)[i].script) {
newHost.scripts = host.ports[0].port[i].script
}
}
}
if (host.os && host.os[0].osmatch && host.os[0].osmatch[0].$.name) {
newHost.osNmap = host.os[0].osmatch[0].$.name;
}
Expand Down
Loading