Hello!
Why bother writing a port scanner in Javascript you might ask? Well javascript is many things, however the majority of its use is based on its original design to be synchronously executed in-browser using a single thread. Because of this, it becomes a challenge to write code outside of this original use case, though not impossible. The challenge of interacting with a local network using a language like Javascript then becomes an interesting endeavour (at least to me).
If you take this type of a challenge and integrate it into a javascript based framework like react native, the potential increases exponentially. If one can successfully implement this type of network analysis on react native, then the use-case expands to platforms like iOS and Android. Same goes for AngularJS or Apache Cordova.
There are many functions, libraries and modules that will help write asynchronous code. One of which is async. Many helper functions are included in this module to assist writing dependent, asynchronous and linear processing code.
The problem, when writing something like a port scanner, is there are main dependencies that need to be established before you start blasting IP addresses with TCP requests and wait for responses. We need to know things like are we on a local network? Do we have a local IP? Whats the subnet? What ports are we going to scan?
Since some of these requests take a bit of processing and calculation, we need to either execute the code asynchronously using a module like async or we can use promises to ensure that certain blocks of code are executed before subsequent blocks of code are executed.
The goal of this project is to ultimately complete the following tasks :
- Import the dependencies you will need to interact with your network
- Gather information about your local network
- Scan the list of IPs in your subnet with defined ports
- Report on the IPs that responded or have open connections on the defined ports
Import the dependencies you will need to interact with your network
First and foremost before we can do anything, we need to include the dependencies that we will need for port scanning in react native.
import { NetworkInfo } from 'react-native-network-info'; import SubnetmaskModule from 'get-subnet-mask'; var sip = require ('shift8-ip-func'); var ipaddr = require('ipaddr.js'); var net = require('react-native-tcp');
Since we’re going the route of react native, we’ll need to include a few dependencies in our project to start. The first dependency is the react-native-network-info depdendency. This is a react-native specific dependency because it allows you to gather the local device’s network information so that we can extrapolate and utilize
The next dependency is get-subnet-mask. Similar idea to the network info module above, however where that module falls short this module makes up for it. As implied by the name we need the local subnet mask in order to ultimately calculate an IP range to scan of your local network.
Next in the list we have our own npm package shift8-ip-func. We wrote and published this module because it could be re-used elsewhere and nothing exactly existed as a package. All this does is calculate the range of IP addresses between two IP addresses that are converted to hex codes. This module is key in determining the list of IPs to scan in the first place.
The next package is the wonderfully useful and integral ipaddr.js library. This is an IPv6 and IPv4 address manipulation library. It does everything with IP calculation except calculate range of IPs (which is why we wrote our own that did that).
The last package in this list is definitely the most important. It is a react-native version of the NodeJS Net api, affably called react-native-tcp. This allows us to interact with and establish TCP network connections with IP addresses. This is key for us to be able to build a functional port scanner.
Gather information about your local network
Now that we have the dependencies we need, lets write a Javascript promise to gather the network information in a specific order to control the flow of information so that we can calculate the IP range and whatever else we may need in our port scanner.
var network_promise = new Promise(function(resolve, reject) { NetworkInfo.getIPAddress(ip => { local_ip = ip; NetworkInfo.getBroadcast(address => { local_broadcast = address; SubnetmaskModule.getSubnet((sb) => { local_netmask = sb; subconv = ipaddr.IPv4.parse(local_netmask).prefixLengthFromSubnetMask(); firstHost = ipaddr.IPv4.networkAddressFromCIDR(local_ip + "/" + subconv); lastHost = ipaddr.IPv4.broadcastAddressFromCIDR(local_ip + "/" + subconv); firstHostHex = sip.convertIPtoHex(firstHost); lastHostHex = sip.convertIPtoHex(lastHost); ipRange = sip.getIPRange(firstHostHex,lastHostHex); ipRange = ipRange.slice(1); // Remove the first ip in the array // Resolve all the calculated values resolve({ local_ip: local_ip, local_broadcast: local_broadcast, local_netmask: local_netmask, subnet_conv: subconv, first_host: firstHost, last_host: lastHost, first_host_hex: firstHostHex, last_host_hex: lastHostHex, ip_range: ipRange }); }); }); }); });
So whats happening above? We are creating a new Promise with a chain of functions that will execute in a specific order. First we want to get the local IP address of the person running the code. Then we want to extrapolate the broadcast address and netmask. Then we are using the ipaddr.js and shift8-ip-func packages to get the subnet mask, first host in the subnet and last host in the subnet.
Then we need to convert those first and last hosts to hex codes so that they can be converted into an IP range. We then want to remove the first host in that range because it usually is the 192.168.0.0 (network address).
The last variable that we care about is the ip_range variable. Even though we are only going to be using that variable, we are sending all the calculated variables in the resolve.
Scan the list of IPs in your subnet with defined ports
Now that we have all the information we need, we’ll need to build a function to scan each IP. Ultimately we will be building nested for-loops to cycle through the IPs in the list as well as the TCP ports that we want to scan, which we will define as an array :
var portRange = [ 22, 80, 443 ];
Since we will be running this function many times, likely more than once against each IP, we will want to define a robust Promise based function that will resolve once complete before being executed again. This will ensure that we dont quickly overwhelm resources or bottleneck network traffic. It also allows us to build in throttling later on if that does become a problem.
// Function to scan hosts var scanHost = function(hostIP, hostPort) { return new Promise(function (resolve,reject) { var client = net.connect({ host: hostIP, port: hostPort }, function() { //'connect' listener console.log('Connected'); }); client.setTimeout(2000,function(){ // called after timeout -> same as socket.on('timeout') // it just tells that soket timed out => its ur job to end or destroy the socket. // socket.end() vs socket.destroy() => end allows us to send final data and allows some i/o activity to finish before destroying the socket // whereas destroy kills the socket immediately irrespective of whether any i/o operation is goin on or not...force destry takes place console.log('Socket timed out'); }); client.on('connect', function() { var scan_result = { ip:hostIP, port:hostPort }; resolve(scan_result); }) client.on('timeout',function(){ console.log('Socket timed out !'); client.end('Timed out!'); // can call socket.destroy() here too. }); client.on('end',function(data){ console.log('Socket ended from other end!'); console.log('End data : ' + data); }); client.on('close',function(error){ var bread = client.bytesRead; var bwrite = client.bytesWritten; console.log('Bytes read : ' + bread); console.log('Bytes written : ' + bwrite); console.log('Socket closed!'); if(error){ console.log('Socket was closed as a result of transmission error'); } }); client.on('error', function(err) { console.log('******* ERROR : ' + JSON.stringify(err)); client.destroy(); }); setTimeout(function(){ var isdestroyed = client.destroyed; console.log('Socket destroyed:' + isdestroyed); client.destroy(); },5000); }); }
The scanHost function has a bit going on there. Basically we are opening a socket connection to the defined IP and Port. If the connection opens, we consider that port open and resolve the promise by sending an object of the IP and port so that we can ultimately notify the end user that hey that ip and port are open!
The other functions that should be considered are the “client.on” conditions, such as close, error and timeout. Its all pretty self explanatory and there is plenty of examples and additional documentation on the react-native-tcp github page.
Report on the IPs that responded or have open connections on the defined ports
This is the last step in the process, but I’ll cover the actual trigger for everything to be initiated, including gathering the network information.
network_promise.then((response) => { for (let i = 0; i < response["ip_range"].length; i++) { for (let j = 0; j < portRange.length; j++) { scanHost(response["ip_range"][i], portRange[j]) .then(response => { scanResult.push(response); this.setState({ listContent: this.state.listContent.concat([response]) }); }) .catch(err => { console.error(err); return err; }) } } }) .catch(err => { console.error(err); return err; })
As you can see, we start with the network_promise, then the nested for-loops that trigger the scanHost function which will then allow us to extrapolate the resolve of the scanHost function and use react-native’s setState to update the view for the end-user to see the results.
If you weren’t using react native, instead of setState, you could simply push the results to an array which you can see that we are actually doing anyways with scanResult.push(response).
Thats it! In a nutshell, we covered how to gather network information, extrapolate the information we need to build a range of local IP addresses on your network, scan them with a defined array of TCP ports and then report back on the results.
Port scanning and banner grabbing with Javascript
One of the interesting prospects that could take this entire endeavour a step further would be to build a system for banner grabbing. If you have used command-line port scanners like nmap, then you already know that it is very useful in network analysis to have the ability to extrapolate not only the services that are running on certain ports, but the versions and other important information that is returned when interacting with the services by mimicking a legitimate request.
Find an example below that scans an IP address and a port and then simulates an HTTP request (assuming it is port 80 or 443) in order to get the banner response. This could be used to extrapolate the Apache/Nginx/Lighttpd version as well as any other important information that is disclosed.
function scanTCPHost(host, port) { var client = net.createConnection(port, host); console.log('Socket created.'); client.on('data', function(data) { // Log the response from the HTTP server. console.log('RESPONSE: ' + data); }).on('connect', function() { // Manually write an HTTP request. client.write("GET / HTTP/1.0\r\n\r\n"); console.log('CONNECTED : ' + host + ' ' + port); }).on('end', function() { console.log('DONE'); client.close(); }); }
Interesting, isnt it? Who said Javascript should be restricted to the browser? These exercises are interesting in that they push a language or framework to the absolute limit of what it was designed to do, sometimes utilizing modules and packages that are designed for something else (Browser sockets) and using them for something more (Port scanning).
I hope you found this useful!