harryhorton / node-nmap Goto Github PK
View Code? Open in Web Editor NEWNPM package for interfacing with local NMAP installation
License: MIT License
NPM package for interfacing with local NMAP installation
License: MIT License
Kill a running scan
Hey, I'm having problems with running node-nmap with async. Basically I'm trying to get 4 scans to run at a time asynchronously. The code runs fine, however, when all the scans are done, the rest of the program hangs and I have to ^C to get the program to stop. Any ideas?
Here's my code:
function NmapScan(callback) {
var ranges = file.GetRanges();
console.log('>> Starting nmap scan');
async.eachLimit(ranges, 4, function(range, callback) {
var nmapscan = new nmap.nodenmap.NmapScan(range, '-sT', '-PN', '-n', 'p80,443');
console.log('>>> Starting scan of range: ' + range);
nmapscan.on('complete', function(data) {
console.log(data);
console.log('>>> Finished scan of range: ' + range + ' (' + nmapscan.scanTime + ' ms)');
return callback();
});
nmapscan.on('error', function(error) {
console.log(error);
return callback();
});
nmapscan.startScan();
}, function (err) {
if(err) {
console.log(err);
}
return callback();
});
}
exports.NmapScan = function(callback) {
NmapScan(function() {
console.log('>> Finished all nmap scans');
return callback();
});
}
The scan breaks due to this line:
https://github.com/Johnhhorton/node-nmap/blob/master/index.js#L124
Non-Windows operating systems return "\n", not "\r\n".
/*
* NodeJS <-> NMAP interface
* Author: John Horton
* Purpose: Create an interface for NodeJS applications to make use of NMAP installed on the local system.
*/
const child_process = require('child_process');
const execSync = child_process.execSync;
const exec = child_process.exec;
const spawn = child_process.spawn;
const fs = require('fs');
const EventEmitter = require('events').EventEmitter;
const os = require('os');
const Queue = require('queued-up');
const xml2js = require('xml2js');
/**
*
* @param {*} xmlInput
* @param {*} onFailure
* @returns {host[]} - Array of hosts
*/
function convertRawJsonToScanResults(xmlInput) {
let tempHostList = [];
if (!xmlInput.nmaprun.host) {
//onFailure("There was a problem with the supplied NMAP XML");
return tempHostList;
};
xmlInput = xmlInput.nmaprun.host;
tempHostList = xmlInput.map((host) => {
const newHost = {
hostname: null,
ip: null,
mac: null,
openPorts: null,
osNmap: null
}
//Get hostname
if (host.hostnames && host.hostnames[0] !== "\r\n" && host.hostnames[0] !== "\n") {
newHost.hostname = host.hostnames[0].hostname[0].$.name
}
//get addresses
host.address.forEach((address) => {
const addressType = address.$.addrtype
const addressAdress = address.$.addr
const addressVendor = address.$.vendor
if (addressType === 'ipv4') {
newHost.ip = addressAdress
} else if (addressType === 'mac') {
newHost.mac = addressAdress
newHost.vendor = addressVendor
}
})
//get ports
if (host.ports && host.ports[0].port) {
const portList = host.ports[0].port
const openPorts = portList.filter((port) => {
return (port.state[0].$.state === 'open')
})
newHost.openPorts = openPorts.map((portItem) => {
// console.log(JSON.stringify(portItem, null, 4))
const port = parseInt(portItem.$.portid)
const protocol = portItem.$.protocol
const service = portItem.service[0].$.name
const tunnel = portItem.service[0].$.tunnel
const method = portItem.service[0].$.method
const product = portItem.service[0].$.tunnel
let portObject = {}
if(port) portObject.port = port
if(protocol) portObject.protocol = protocol
if(service) portObject.service = service
if(tunnel) portObject.tunnel = tunnel
if(method) portObject.method = method
if(product) portObject.product = product
return portObject
})
}
if (host.os && host.os[0].osmatch && host.os[0].osmatch[0].$.name) {
newHost.osNmap = host.os[0].osmatch[0].$.name
}
return newHost
})
return tempHostList;
}
class NmapScan extends EventEmitter {
constructor(range, inputArguments, runAsRoot = false) {
super();
this.runAsRoot = runAsRoot
this.command = [];
this.nmapoutputXML = "";
this.timer;
this.range = [];
this.arguments = ['-oX', '-'];
this.rawData = '';
this.rawJSON;
this.child;
this.cancelled = false;
this.scanTime = 0;
this.error = null;
this.scanResults;
this.scanTimeout = 0;
this.commandConstructor(range, inputArguments);
this.initializeChildProcess();
}
startTimer() {
this.timer = setInterval(() => {
this.scanTime += 10;
if (this.scanTime >= this.scanTimeout && this.scanTimeout !== 0) {
this.killChild();
}
}, 10);
}
stopTimer() {
clearInterval(this.timer);
}
commandConstructor(range, additionalArguments) {
if (additionalArguments) {
if (!Array.isArray(additionalArguments)) {
additionalArguments = additionalArguments.split(' ');
}
this.command = this.arguments.concat(additionalArguments);
} else {
this.command = this.arguments;
}
if (!Array.isArray(range)) {
range = range.split(' ');
}
this.range = range;
this.command = this.command.concat(this.range);
}
killChild() {
this.cancelled = true;
if (this.child) {
this.child.kill();
}
}
initializeChildProcess() {
let begin
if(this.runAsRoot)
{
begin= "sudo -u nmap ".concat(nmap.nmapLocation)
}
else {
begin = nmap.nmapLocation
}
this.startTimer();
this.child = spawn(begin, this.command);
process.on('SIGINT', this.killChild);
process.on('uncaughtException', this.killChild);
process.on('exit', this.killChild);
this.child.stdout.on("data", (data) => {
if (data.indexOf("percent") > -1) {
// console.log(data.toString());
} else {
this.rawData += data;
}
});
this.child.on('error', (err) => {
this.killChild();
if (err.code === 'ENOENT') {
this.emit('error', 'NMAP not found at command location: ' + nmap.nmapLocation)
} else {
this.emit('error', err.Error)
}
})
this.child.stderr.on("data", (err) => {
this.error = err.toString();
});
this.child.on("close", () => {
process.removeListener('SIGINT', this.killChild);
process.removeListener('uncaughtException', this.killChild);
process.removeListener('exit', this.killChild);
if (this.error) {
this.emit('error', this.error);
} else if (this.cancelled === true) {
this.emit('error', "Over scan timeout " + this.scanTimeout);
} else {
this.rawDataHandler(this.rawData);
}
});
}
startScan() {
this.child.stdin.end();
}
cancelScan() {
this.killChild();
this.emit('error', "Scan cancelled");
}
scanComplete(results) {
this.scanResults = results;
this.stopTimer();
this.emit('complete', this.scanResults);
}
rawDataHandler(data) {
let results;
//turn NMAP's xml output into a json object
xml2js.parseString(data, (err, result) => {
if (err) {
this.emit('error', "Error converting XML to JSON in xml2js: " + err);
} else {
this.rawJSON = result;
results = convertRawJsonToScanResults(this.rawJSON, (err) => {
this.emit('error', "Error converting raw json to cleans can results: " + err + ": " + this.rawJSON);
});
this.scanComplete(results);
}
});
}
}
class StealthScan extends NmapScan {
constructor(range) {
super(range, '-sS', true);
}
}
class QuickScan extends NmapScan {
constructor(range) {
super(range, '-sP');
}
}
class OsAndPortScan extends NmapScan {
constructor(range) {
super(range, '-O');
}
}
class QueuedScan extends EventEmitter {
constructor(scanClass, range, args, action = () => {}) {
super();
this.scanResults = [];
this.scanTime = 0;
this.currentScan;
this.runActionOnError = false;
this.saveErrorsToResults = false;
this.singleScanTimeout = 0;
this.saveNotFoundToResults = false;
this._queue = new Queue((host) => {
if (args !== null) {
this.currentScan = new scanClass(host, args);
} else {
this.currentScan = new scanClass(host);
}
if (this.singleScanTimeout !== 0) {
this.currentScan.scanTimeout = this.singleScanTimeout;
}
this.currentScan.on('complete', (data) => {
this.scanTime += this.currentScan.scanTime;
if (data[0]) {
data[0].scanTime = this.currentScan.scanTime;
this.scanResults = this.scanResults.concat(data);
} else if (this.saveNotFoundToResults) {
data[0] = {
error: "Host not found",
scanTime: this.currentScan.scanTime
}
this.scanResults = this.scanResults.concat(data);
}
action(data);
this._queue.done();
});
this.currentScan.on('error', (err) => {
this.scanTime += this.currentScan.scanTime;
let data = {
error: err,
scanTime: this.currentScan.scanTime
}
if (this.saveErrorsToResults) {
this.scanResults = this.scanResults.concat(data);
}
if (this.runActionOnError) {
action(data);
}
this._queue.done();
});
this.currentScan.startScan();
});
this._queue.add(this.rangeFormatter(range));
this._queue.on('complete', () => {
this.emit('complete', this.scanResults);
});
}
rangeFormatter(range) {
let outputRange = [];
if (!Array.isArray(range)) {
range = range.split(' ');
}
for (let i = 0; i < range.length; i++) {
let input = range[i];
let temprange = range[i];
if (countCharacterOccurence(input, ".") === 3 &&
input.match(new RegExp("-", "g")) !== null &&
!input.match(/^[a-zA-Z]+$/) &&
input.match(new RegExp("-", "g")).length === 1
) {
let firstIP = input.slice(0, input.indexOf("-"));
let network;
let lastNumber = input.slice(input.indexOf("-") + 1);
let firstNumber;
let newRange = [];
for (let j = firstIP.length - 1; j > -1; j--) {
if (firstIP.charAt(j) === ".") {
firstNumber = firstIP.slice(j + 1);
network = firstIP.slice(0, j + 1);
break;
}
}
for (let iter = firstNumber; iter <= lastNumber; iter++) {
newRange.push(network + iter);
}
//replace the range/host string with array
temprange = newRange;
}
outputRange = outputRange.concat(temprange);
}
function countCharacterOccurence(input, character) {
let num = 0;
for (let k = 0; k < input.length; k++) {
if (input.charAt(k) === character) {
num++;
}
}
return num;
}
return outputRange;
}
startRunScan(index = 0) {
this.scanResults = [];
this._queue.run(0);
}
startShiftScan() {
this.scanResults = [];
this._queue.shiftRun();
}
pause() {
this._queue.pause();
}
resume() {
this._queue.resume();
}
next(iterations = 1) {
return this._queue.next(iterations);
}
shift(iterations = 1) {
return this._queue.shift(iterations);
}
results() {
return this.scanResults;
}
shiftResults() {
this._queue.shiftResults();
return this.scanResults.shift();
}
index() {
return this._queue.index();
}
queue(newQueue) {
if (Array.isArray(newQueue)) {
return this._queue.queue(newQueue);
} else {
return this._queue.queue();
}
}
percentComplete() {
return Math.round(((this._queue.index() + 1) / this._queue.queue().length) * 100);
}
}
class QueuedNmapScan extends QueuedScan {
constructor(range, additionalArguments, actionFunction = () => {}) {
super(NmapScan, range, additionalArguments, actionFunction);
}
}
class QueuedQuickScan extends QueuedScan {
constructor(range, actionFunction = () => {}) {
super(QuickScan, range, null, actionFunction);
}
}
class QueuedOsAndPortScan extends QueuedScan {
constructor(range, actionFunction = () => {}) {
super(OsAndPortScan, range, null, actionFunction);
}
}
let nmap = {
nmapLocation: "nmap",
NmapScan,
QuickScan,
OsAndPortScan,
QueuedScan,
StealthScan,
QueuedNmapScan,
QueuedQuickScan,
QueuedOsAndPortScan,
}
module.exports = nmap;
Could you please include the following line of code in the index.js file:
tempHostList[hostLoopIter].vendor = xmlInput[hostLoopIter]["address"][addressLoopIter]["$"]["vendor"];
It adds the vendor to the result array of the nmap scan.
for (var addressLoopIter = 0; addressLoopIter < xmlInput[hostLoopIter]["address"].length; addressLoopIter++) {
//If IPv4, Mac, or unknown. Get the type and add it or log the type found.
if (xmlInput[hostLoopIter]["address"][addressLoopIter]["$"]["addrtype"] === 'ipv4') {
tempHostList[hostLoopIter].ip = xmlInput[hostLoopIter]["address"][addressLoopIter]["$"]["addr"];
}
else if (xmlInput[hostLoopIter]["address"][addressLoopIter]["$"]["addrtype"] === 'mac') {
tempHostList[hostLoopIter].mac = xmlInput[hostLoopIter]["address"][addressLoopIter]["$"]["addr"];
**NEW LINE OF CODE HERE**
}
else {
}
Thank you very much!
Hi,
I would like to be able to scan customer port ranges.
But if I include '-p 80,8080,8443' to my NmapScan object it fails with an error.
Can you please tell me how to include customer ports?
Thanks
My nmap requires admin privileges to run the OS detection. Therefore when running this application, I am getting an error "Quitting". How to deal with this issue?
Error handling for:
Can't find NMAP
Error from NMAP
No data from NMAP (frozen)
Hi, I was wondering if you could include the option to get the version of the services running, I dont know if it would be much trouble, but this is what the library needs, would be really awesome to include that since im working in a big project where we are using it.
Or if it is already there and im not seeing it, please guide me, thanks.
@Johnhhorton There is an error on line 78 (master branch, 11/04/19).
When scanning a port, it gets twice the service[0].$.tunnel property, instead of the service[0].$.product.
So, currently, the scan does not return the service name.
When I try to run the nmap OS and port scan throws an error. The error is mentioned below.
TCP/IP fingerprinting (for OS scan) requires root privileges.
QUITTING!
I don't get results of any scan on recent nodeJS... Like the complete is never emmited. Anybody experiencing same problem?
(node) warning: possible EventEmitter memory leak detected. 11 SIGINT listeners added
I have tried the package on my Home LAN and it works great but when i went to my Work LAN I got an error:
Error converting XML to JSON in xml2js: Error: Unclosed root tag
Line: 5
Column: 0
Char:
Somehow If I do nmap -oX output.xml -sP 192.168.1.*
it works but in the code it reads the file when nmap has not finish so only the headers are places on the xml output. That is way it is unclosed. Any advice, is this an error with nmap (maybe it send a close event) or what can it be this bug?
Not sure what I'm missing here... its like the scan quicks off automatically, not letting me set responses to the event emitter
var nmap = require('node-nmap')
var blah = new nmap.QuickScan('192.168.1.1/24')
console.log(blah.scanResults)
/* returns [{ hostname: ...
...
}]
*/
without having explicitly ran
blah.startScan()
Hi,
I want to use spoof source IP to do the detection destination IP and port.
What should I do to change the source IP?
Thanks!!
Automatically get host IP and scan the local network.
/home/jean/app_node/hostname_nmap/node_modules/queued-up/index.js:23
this.action.prototype.pause = this.pause;
^
TypeError: Cannot set property 'pause' of undefined
at new Queue (/home/jean/app_node/hostname_nmap/node_modules/queued-up/index.js:23:37)
at QueuedScan (/home/jean/app_node/hostname_nmap/node_modules/node-nmap/index.js:260:19)
at QueuedOsAndPortScan (/home/jean/app_node/hostname_nmap/node_modules/node-nmap/index.js:433:5)
at Object. (/home/jean/app_node/hostname_nmap/index.js:14:12)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
I've set capabilities for nmap so it can run in non-root mode:
sudo setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip /usr/bin/nmap
and nmap from command line works fine
(not that running in non-root mode with explicit capabilities requires --priviledges flag).
nmap --privileged -sN -O localhost
but when invoked using nodejs as
const proc = new nmap.OsAndPortScan(host, '--privileged');
it still fails with "TCP/IP fingerprinting (for OS scan) requires root privileges."
seems flags are not set properly?
After running a certain CRON job combined with nmap i get this error:
Error [ERR_UNHANDLED_ERROR]: Unhandled error. ("nmap: Target.cc:503: void at new NodeError (node:internal/errors:372:5) at NmapScan.emit (node:events:516:17) at ChildProcess.<anonymous> (/home/pi/Downloads/network/node_modules/node-nma at ChildProcess.emit (node:events:527:28) at maybeClose (node:internal/child_process:1092:16) at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5) { code: 'ERR_UNHANDLED_ERROR', context: "nmap: Target.cc:503: void Target::stopTimeOutClock(const timeval*): }
As a developer I want to be able to run multiple queued hosts through a single instance of NMAP as this will allow me to selectively reduce the time overhead of running one instance of NMAP per host.
Include a scan duration in the scan results
have the ability to call on a scan in progress for completion percentage.
This requires more verbose logging from NMAP and parsing of all new data as the scan progresses.
It throws
TypeError: Cannot read property 'kill' of undefined
at process.killChild (/home/dharmax2/projects/mockup/prefender/prefenderMocup/node-server/node_modules/node-nmap/index.js:54:23)
... on the example code itself.
Hi does this support the use of nmap scripts?
NMAP is freezing when the node app is closed before the scan is complete.
Hey everyone,
This project seems to get a fair amount of usage. I originally created this package for a project I'm no longer pursuing. I've got a really full plate, and I feel a lot of guilt about not maintaining this repo properly.
I'd like to pull in some other maintainers.
If anyone's interested, let's chat.
Extend autoDiscover to calculate range based on the subnet mask.
Iterate through each interface found to perform scan on all connected networks
I am using nmap on Onion Omega 2 Linux OS, I have installed nmap globally, when I use following program
const nmap = require('node-nmap');
nmap.nmapLocation = 'nmap'; //default
let quickscan = new nmap.QuickScan('127.0.0.1 google.com');
console.log(quickscan);
it works but when I try to run NmapScan I am getting this error when I try running following program, "NMAP not found at command location: nmap"
var nmapscan = new nmap.NmapScan('127.0.0.1 google.com', '-sn');
nmapscan.on('complete',function(data){
console.log(data);
});
nmapscan.on('error', function(error){
console.log(error);
});
nmapscan.startScan();
Appreciate any help and inputs
npm test
:
NmapScan
1) runs NMAP
✓ accepts space separated command
✓ accepts multiple hosts (84ms)
✓ returns failure data for bad requests (55ms)
quickScan
✓ scans range of hosts (85ms)
✓ returns failure data for bad requests
osAndPortScan
2) scans hosts for open ports and OS data
✓ returns failure data for bad requests (295ms)
6 passing (11s)
2 failing
1) NmapScan runs NMAP:
Error: timeout of 10000ms exceeded. Ensure the done() callback is being called in this test.
2) osAndPortScan scans hosts for open ports and OS data:
Uncaught Error: Unhandled "error" event. (TCP/IP fingerprinting (for OS scan) requires root privileges.
QUITTING!
)
at ChildProcess.child.on (index.js:194:14)
at maybeClose (internal/child_process.js:925:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
when I run sudo npm test
it goes ok.
My npm ls --depth=1
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
├── [email protected]
└─┬ [email protected]
├── [email protected]
└── [email protected]
MacOS v10.13.2
Node v8.9.4
npm v5.6.0
nmap 7.60
Modify package to use scans as instances of the scan and events instead of callbacks. This will enable the transition to a queuing system and progress reports.
In index.js at line 76: When subscribing to stdin, shouldn't the data argument be converted to a string before using the method indexOf()?
I'm receiving:
TypeError: Object <?xml version="1.0"?>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 6.00 scan initiated Fri Feb 19 22:40:35 2016 as: nmap -oX - -sP 192.168.0.1-5 -->
<nmaprun scanner="nmap" args="nmap -oX - -sP 192.168.0.1-5" start="1455918035" startstr="Fri Feb 19 22:40:35 2016" version="6.00" xmloutputversion="1.04">
has no method 'indexOf' at Socket.<anonymous> ( /home/pi/.node-red/node_modules/node-red-contrib-nmap/node_modules/node-nmap/index.js:76:26)
I fixed the issue by adding:
data = data.toString();
to result in:
this.child.stdout.on("data", function (data) {
data = data.toString();
if (data.indexOf("percent") > -1) {
console.log(data.toString());
}
else {
_this.rawData += data;
}
});
I'm using Node.js v0.10.35
i get:
Unable to find nmap-services! Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
here is my code:
let ipaddressRange = "192.168.0.0/24";
var scan = new nmap.nodenmap.NmapScan(ipaddressRange, '-p 5432 --open');
scan.on('complete', function (data: any[]) {
let hosts = _.map(data, (d) => { return { hostname: d.hostname, ip: d.ip } });
resolve(hosts);
});
scan.on('error', function (error) {
console.error(error);
reject(error);
});
scan.startScan();
I don't know if it's currently possible, but i cannot get mac address when i do the nmap with "-sn" param !
Is there a solution ?
Set a timeout for a scan
Hi John,
I've been using this module in my node application for a while and in overall it's pretty great! However there is one issue I'd like to mention here. It causes a crash when nmap is not installed locally. Here is the error message:
/home/.../node-nmap/index.js:66
this.child.kill();
^
TypeError: Cannot read property 'kill' of undefined
at process.NmapScan.killChild (/home/.../node-nmap/index.js:66:23)
at emitOne (events.js:96:13)
at process.emit (events.js:188:7)
at process._fatalException (bootstrap_node.js:296:26)
Is there a way of detecting if nmap is installed and returning an error message instead?
index.ts, lines 83 +
} private killChild(){ this.cancelled = true; this.child.kill(); }
i don't know why it gets there, maybe because it starts before it finished a previous scan, but it crashes on this.child = undefined.
perhaps it would be better to write:
this.child && this.child.kill();
Btw, is this module maintain? reliable?
Prepare queue of scans to be processed in order.
Pause and continue the progress of the queue
Check status of the queue
Append results to object allowing for retrieval of data as queue progresses
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.