Access nmap using node.js
To install npm install node-libnmap
To test npm test
nmap
- Path to nmap binaryrange
- Subnet range(s)ports
- Port range(s)callback
- A user defined callback function to retrieve & parse report
init
- Returns version, license, help & nmap legal resourcesdiscover
- Performs auto-discovery of online hostsscan
- Performs scan given available range & optional port (not yet implemented)
Here are a few usage examples & their output
console.log(require('libnmap').nmap())
> require('./').nmap()
{ name: 'node-libnmap',
version: 'v0.1.12',
usage: 'https://github.com/jas-/node-libnmap',
license: 'https://github.com/jas-/node-libnmap/blob/master/LICENSE',
issues: 'https://github.com/jas-/node-libnmap/issues',
nmap: { legal: 'http://nmap.org/book/man-legal.html' } }
The discover method is the quickest method but is limited to finding local peers within the same CIDR per interface.
require('libnmap').nmap('discover', function(err, report){
if (err) throw err
console.log(report)
})
{ adapter: 'eth0',
properties:
{ address: '10.0.2.15',
netmask: '255.255.255.0',
family: 'IPv4',
mac: '52:54:00:12:34:56',
internal: false,
cidr: '10.0.2.0/24',
hosts: 256,
range: { start: '10.0.2.1', end: '10.0.2.254' } },
neighbors: [ '10.0.2.2', '10.0.2.3', '10.0.2.15' ] }
A manually specified scan example using a single host (both IPv4 & IPv6 notation), a CIDR range a host range as well as a port range specification.
var opts = {
range: ['10.0.2.128-255', '10.0.2.0/25', '192.168.0.0/17', '::ffff:192.168.2.15'],
ports: '21,22,80,443,3306,60000-65535'
}
require('libnmap').nmap('scan', opts, function(err, report){
if (err) throw err
report.forEach(function(item){
console.log(item[0])
})
})
{ ip: '127.0.0.1',
hostname: 'localhost',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '10.0.2.15',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '192.168.2.15',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '192.168.2.2',
ports:
[ { port: '513',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'login',
rpc: '',
version: '' },
{ port: '514',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'shell',
rpc: '',
version: '' },
{ port: '631',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ipp',
rpc: '',
version: '' } ] }
The following errors are thrown when invalid configuration options are passed to the module and/or when the necessary node.js version is below version v0.11.*
If you attempt to specify an unkown or unimplemented method, the following error
is thrown. Allowed methods are scan
& discover
.
Method "[missing method]" does not exist, please see node-libnmap API
The discover method requires a node.js version > v0.11
due to the
os.networkInterfaces().netmask
property being used to traverse each
physical/virtual adapter and examing the address space for online hosts.
Requires node.js v0.11.* and above
If your system does not have the nmap binary installed the following error is thrown
nmap binary not found, install nmap
When specifying an invalid range to the scan
method the following error is
thrown. Valid range types are a single hostname/ipv4 (ipv6 is not yet
implemented), a CIDR range notation or a range.
Range must be an array of host(s), examples: ['192.168.2.10', '10.0.2.0/24', '10.0.10.5-20']
A range of ports may also be specified with the scan
method, for an invalid
port specification the following error is thrown.
Port(s) must match one of the following examples: 512 (single) | 0-65535 (range) | 22-25,80,443,3306 (multiple)
If you recieve the spawn EAGAIN
or spawn EMFILE
error(s) you have reached
the max number of max user processes
. This error is generally thrown if your
attempting to scan a very large network block.
To alleviate this you may need to increase the max number of processes and/or which can file handles can be done like so (though not recommended):
$ ulimit -u 65000
$ ulimit -n 65000
Important These limits are in place to help protect the operating system against attacks such as a fork bombing & chroot jail breaking.
A note on performance of nmap scans; the nmap tool already makes efforts to utilize parallel processing when multiple processor cores are available.
Even with that in mind this library performs the following calculation prior
to running scans on an IP range. ip-range / cpu
. It tries to split the
IP range into chunks then creates separate threads performing each scan
sequentially.
For example instead of executing nmap like this nmap -sn -oG - 10.0.2.0/24
it instead breaks the subnet range into chunks based on the amount of cpu
cores like this (i.e. 8 cores, where each command is run in its own thread)
nmap -sn -oG - 10.0.2.1-31
nmap -sn -oG - 10.0.2.33-63
nmap -sn -oG - 10.0.2.65-95
nmap -sn -oG - 10.0.2.97-127
nmap -sn -oG - 10.0.2.129-159
nmap -sn -oG - 10.0.2.161-191
nmap -sn -oG - 10.0.2.193-223
nmap -sn -oG - 10.0.2.225-255
The technical details of Fyodor's optimizations can be found @ insecure.org.
The results here are all coming from a virtual environment with limited system
resources but should give an overall picture of performance of the scans. My VM
environment is using 8 cores with 4 threads per core given a total returned from
require('os').cpus.length
= 32.
$ time nmap -sn -oG - 10.0.2.0/24
# Nmap 5.51 scan initiated Wed Jan 8 18:54:07 2014 as: nmap -sn -oG - 10.0.2.0/24
Host: 10.0.2.2 () Status: Up
Host: 10.0.2.3 () Status: Up
Host: 10.0.2.15 () Status: Up
# Nmap done at Wed Jan 8 18:54:26 2014 -- 256 IP addresses (3 hosts up) scanned in 19.33 seconds
real 0m19.339s
user 0m0.052s
sys 0m0.080s
$ time node test/run.js
{ adapter: 'eth0',
properties:
{ address: '10.0.2.15',
netmask: '255.255.255.0',
family: 'IPv4',
mac: '52:54:00:12:34:56',
internal: false,
cidr: '10.0.2.0/24',
hosts: 256,
range: { start: '10.0.2.1', end: '10.0.2.254' } },
neighbors: [ '10.0.2.2', '10.0.2.3', '10.0.2.15' ] }
real 0m3.323s
user 0m0.326s
sys 0m0.412s
And an example with multiple adapters on multiple 802.11Q segments
$ time node test/run.js
[ { adapter: 'eth0',
properties:
{ address: '10.0.2.15',
netmask: '255.255.255.0',
family: 'IPv4',
mac: '52:54:00:12:34:56',
internal: false,
cidr: '10.0.2.0/24',
hosts: 256,
range: {start: '10.0.2.0', end: '10.0.2.255'} },
neighbors: [ '10.0.2.2', '10.0.2.3', '10.0.2.15' ] },
{ adapter: 'eth1',
properties:
{ address: '192.168.2.15',
netmask: '255.255.255.128',
family: 'IPv4',
mac: '52:54:00:12:34:57',
internal: false,
cidr: '192.168.2.0/25',
hosts: 128,
range: {start: '192.168.2.1', end: '192.168.2.128'} },
neighbors: [ '192.168.2.2', '192.168.2.3', '192.168.2.15' ] } ]
real 0m3.447s
user 0m0.493s
sys 0m0.796s
$ time nmap -T4 -oG - localhost 10.0.2.0/24 192.168.2.0/25
# Nmap 5.51 scan initiated Sun Jan 26 08:03:18 2014 as: nmap -T4 -oG - localhost 10.0.2.0/24 192.168.2.0/25
Host: 127.0.0.1 (localhost) Status: Up
Host: 127.0.0.1 (localhost) Ports: 22/open/tcp//ssh/// Ignored State: closed (999)
Host: 10.0.2.2 () Status: Up
Host: 10.0.2.2 () Ports: 513/open/tcp//login///, 514/open/tcp//shell///, 631/open/tcp//ipp///, 1192/filtered/tcp//caids-sensor///, 1524/filtered/tcp//ingreslock///, 1533/filtered/tcp//virtual-places///, 1862/filtered/tcp//mysql-cm-agent///, 1864/filtered/tcp//paradym-31///, 2179/filtered/tcp//vmrdp///, 2222/open/tcp//EtherNet|IP-1///, 2381/filtered/tcp//compaq-https///, 3000/open/tcp//ppp///, 3003/filtered/tcp//cgms///, 3369/filtered/tcp//satvid-datalnk///, 4343/open/tcp//unicall///, 5901/open/tcp//vnc-1///, 7019/filtered/tcp//unknown///, 8000/open/tcp//http-alt///, 8080/open/tcp//http-proxy///, 8300/filtered/tcp//tmi///, 9009/filtered/tcp//pichat///, 9594/filtered/tcp//msgsys///, 10009/filtered/tcp//swdtp-sv///, 16000/filtered/tcp//fmsas/// Ignored State: closed (976)
Host: 10.0.2.15 () Status: Up
Host: 10.0.2.15 () Ports: 22/open/tcp//ssh/// Ignored State: closed (999)
Host: 192.168.2.2 () Status: Up
Host: 192.168.2.2 () Ports: 513/open/tcp//login///, 514/open/tcp//shell///, 631/open/tcp//ipp///, 1174/filtered/tcp//fnet-remote-ui///, 2222/open/tcp//EtherNet|IP-1///, 3000/open/tcp//ppp///, 4343/open/tcp//unicall///, 5901/open/tcp//vnc-1///, 7402/filtered/tcp//rtps-dd-mt///, 8000/open/tcp//http-alt///, 8002/filtered/tcp//teradataordbms///, 8080/open/tcp//http-proxy///, 9100/filtered/tcp//jetdirect///, 9666/filtered/tcp//unknown///, 9968/filtered/tcp//unknown///, 11110/filtered/tcp//unknown///, 54045/filtered/tcp//unknown/// Ignored State: closed (983)
Host: 192.168.2.3 () Status: Up
Host: 192.168.2.3 () Ports: 80/open/tcp//http///, 513/open/tcp//login///, 514/open/tcp//shell///, 1051/filtered/tcp//optima-vnet/// Ignored State: closed (996)
Host: 192.168.2.15 () Status: Up
Host: 192.168.2.15 () Ports: 22/open/tcp//ssh/// Ignored State: closed (999)
# Nmap done at Sun Jan 26 08:06:52 2014 -- 385 IP addresses (6 hosts up) scanned in 214.20 seconds
real 3m34.218s
user 0m0.911s
sys 0m3.315s
The test case used:
var libnmap = require('node-libnmap')
var opts = {
range: ['localhost', '10.0.2.0/24', '192.168.2.0/25']
}
libnmap.nmap('scan', opts, function(err, report){
if (err) throw err
report.forEach(function(item){
console.log(item[0])
})
})
The results
{ ip: '127.0.0.1',
hostname: 'localhost',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '10.0.2.15',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '192.168.2.15',
ports:
[ { port: '22',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ssh',
rpc: '',
version: '' } ] }
{ ip: '192.168.2.2',
ports:
[ { port: '255',
state: 'filtered',
protocol: 'tcp',
owner: '',
service: 'unknown',
rpc: '',
version: '' },
{ port: '513',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'login',
rpc: '',
version: '' },
{ port: '514',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'shell',
rpc: '',
version: '' },
{ port: '631',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ipp',
rpc: '',
version: '' },
{ port: '1186',
state: 'filtered',
protocol: 'tcp',
owner: '',
service: 'mysql-cluster',
rpc: '',
version: '' },
{ port: '2222',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'EtherNet|IP-1',
rpc: '',
version: '' },
{ port: '3000',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'ppp',
rpc: '',
version: '' },
{ port: '4343',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'unicall',
rpc: '',
version: '' },
{ port: '5901',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'vnc-1',
rpc: '',
version: '' },
{ port: '8000',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'http-alt',
rpc: '',
version: '' },
{ port: '8080',
state: 'open',
protocol: 'tcp',
owner: '',
service: 'http-proxy',
rpc: '',
version: '' },
{ port: '9111',
state: 'filtered',
protocol: 'tcp',
owner: '',
service: 'DragonIDSConsole',
rpc: '',
version: '' },
{ port: '19801',
state: 'filtered',
protocol: 'tcp',
owner: '',
service: 'unknown',
rpc: '',
version: '' } ] }
real 2m32.158s
user 0m13.066s
sys 0m8.890s
To really test the performance of the module I did several scans of a class B
network containing a maximum host count of 32766
. Below are the times for
both scans.
$ time nmap -T4 -n -oG - 155.97.0.0/17
real 10m32.856s
user 0m11.709s
sys 0m33.364s
$ time node nmap-test.js
real 0m32.034s
user 1m3.209s
sys 0m33.950s
Mileage may vary
I welcome contributions. Testing, patches, features etc. are appreciated. To submit a pull request the following instructions will help.
First fork the project from github.com.
Any contributions you make should be made under a unique branch to avoid
conflicts. While creating your branch it is recommended you track changes with the latest production
branch like so: git checkout -b my-new-feature -t origin/master
-
To ensure changes are as up to date as possible it is recommended to add an upstream branch to rebase any upstream changes like so:
git remote add upstream https://github.com/jas-/node-libnmap.git
-
You will then need to
merge
it to track thecontribute
branch:git fetch upstream
Once you have modified your branch simply create a pull request that I can review and test prior to acceptance.