Writing A Simple Ruby Script To Automate Nmap

An nmap scan should always be the first thing to do when you start a box, and since I'm too lazy to write nmap -sV -sC -oA initial box_ip
(and I want a progress bar instead of having to constantly press a button to see progress), we're just going to write a script to do it for us.
I'm using Ruby for this because I think it's just as readable as Python (so I don't have to explain as much) and because I'm not bothered to learn how to do this in Python, which means that this is the first time I've ever shown code on this website that isn't Python.
Anyway, to do this we will use a class from Ruby's standard library called PTY. PTY
allows you to spawn an external process and then interact with that process by using puts to write to it's stdin
and gets to read from it's stdout
.
#!/usr/bin/ruby
require 'pty'
cmd = "nmap -sV #{ARGV[0]}"
PTY.spawn( cmd ) do |stdout, stdin, pid|
loop do
stdin.puts ' '
puts stdout.gets.chomp
sleep 0.1
end
end
This is our initial code. It runs nmap with the box IP as an argument and every 0.1 seconds, it sends a space character to stdin and prints stdout so we can see the progress of the scan. This works but right now we are only running nmap with -sV, so now we should add all the arguments we need.
#!/usr/bin/ruby
require 'pty'
require 'fileutils'
FileUtils.mkdir_p 'nmap'
cmd = "nmap -sV -sC -oA nmap/initial #{ARGV[0]}"
PTY.spawn( cmd ) do |stdout, stdin, pid|
loop do
stdin.puts ' '
puts stdout.gets.chomp
running = %x[ ps -p #{pid} -o comm= ]
if running.include? "defunct"
break
end
sleep 0.1
end
end
Now we are creating a folder where all the nmap scripts will be stored using the library 'fileutils' and we've edited the 'cmd' variable to use all the arguments. I've also added a check to see if nmap has finshed in order to break out of the loop by checking if the process name includes the words 'defunct'.
If you run this you will see that stdout becomes very messy as the progress is constantly being called. Let's get that progress bar working.
#!/usr/bin/ruby
require 'pty'
require 'fileutils'
require 'progress_bar'
FileUtils.mkdir_p 'nmap'
cmd = "nmap -sV -sC -oA nmap/initial #{ARGV[0]}"
$syn_bar = ProgressBar.new
$srv_bar = ProgressBar.new
$nse_bar = ProgressBar.new
$syn_progress = 0
$srv_progress = 0
$nse_progress = 0
$step = "init"
def increment_bar(stdout)
new_status = stdout.match(/[[:digit:]]{1,2}\.[[:digit:]]{2}/)
new_status ? new_status = new_status[0].to_i : return
if stdout.include? "SYN Stealth Scan"
if $step != "syn"
puts "Step 1/3 [SYN Stealth Scan]"
$step = "syn"
end
inc_amount = new_status - $syn_progress
$syn_progress = new_status
$syn_bar.increment! inc_amount
elsif stdout.include? "Service"
if $step != "srv"
puts "Step 2/3 [Service Scan]"
$step = "srv"
end
inc_amount = new_status - $srv_progress
$srv_progress = new_status
$srv_bar.increment! inc_amount
elsif stdout.include? "NSE Timing"
if $step != "nse" && $step != 'init'
# NSE Timing shows up before it actually begins
puts "Step 3/3 [NSE]"
$step = "nse"
end
inc_amount = new_status - $nse_progress
$nse_progress = new_status
$nse_bar.increment! inc_amount
end
end
PTY.spawn( cmd ) do |stdout, stdin, pid|
loop do
stdin.puts ' '
response = stdout.gets.chomp
increment_bar(response)
running = %x[ ps -p #{pid} -o comm= ]
if running.include? "defunct"
break
end
sleep 0.1
end
end
This is a really quick and dirty way of getting a progress bar using the library progress_bar
. Make sure to install it with:
gem install progress_bar
Obviously I'm not super proud this script, it's actually pretty terrible for my standards, so much so that I don't want to explain it. But it does what I wanted it to and only took me 5 minutes to write. Someday I may comeback to it to clean up all the repeating code and stop using so many global variables (but I say that about all the code I write, so whatever).
Now I can move save this as /opt/scan-box
and call it with /opt/scan-box box_ip
. You could also put it in /bin
so you can call it with just scan-box box_ip
, but I don't like doing that.