#!/usr/bin/env ruby # = nessus-cli.rb: Nessus command line interface for XML-RPC # Author:: Vlatko Kosturjak # # (C) Vlatko Kosturjak, Kost. Distributed under GPL and BSD (dual licensed). require 'nessus-xmlrpc' require 'getoptlong' verbose = 0 debug = 0 operation = '' targets = '' deletereport = false user = '' password = '' scanname = '' output = '' output1 = '' wait = '' policy = '' url = '' def intro $stderr.print $0 + ": Nessus command line interface for XML-RPC\n" $stderr.print "(C) Vlatko Kosturjak, Kost. Distributed under GPL.\n" $stderr.print "\n" end intro def give_help puts <<-EOF --user user for login to Nessus server --password

password for login to Nessus server --scan start scan with name --target specify list of targets, separated by comma --policy specify policy to use (name of policy) --url url of Nessus server (default: localhost:8834) --wait [t] wait scan to finish (ask in regular periods of for status) --output output report XML to file --output1 output report XML v1 to file --reportdelete delete report after finish or delete report by id (if alone) --stop stop scan identified by --stop-all stop all scans --pause pause scan identified by --pause-all pause all scans --resume resume scan identified by --resume-all resume all scans --report download report identified by --list-scans list scans --list-policy list policies --status get status of scan by --verbose be verbose --debug be even more verbose --help this help Examples: #{$0} --user john --password doe --scan scan-localhost --wait --output report.xml --target localhost EOF exit 0 end if ARGV.length < 1 give_help end opt = GetoptLong.new( ["--help", "-h", GetoptLong::NO_ARGUMENT], ["--verbose", "-v", GetoptLong::OPTIONAL_ARGUMENT], ["--target", "-t", GetoptLong::REQUIRED_ARGUMENT], ["--user", "-u", GetoptLong::REQUIRED_ARGUMENT], ["--password", "-p", GetoptLong::REQUIRED_ARGUMENT], ["--policy", "-P", GetoptLong::REQUIRED_ARGUMENT], ["--url", "-U", GetoptLong::REQUIRED_ARGUMENT], ["--deletereport", "-D", GetoptLong::OPTIONAL_ARGUMENT], ["--wait", "-w", GetoptLong::OPTIONAL_ARGUMENT], ["--scan", "-s", GetoptLong::REQUIRED_ARGUMENT], ["--list-scans", "-l", GetoptLong::NO_ARGUMENT], ["--list-policy", "-L", GetoptLong::NO_ARGUMENT], ["--status", "-W", GetoptLong::REQUIRED_ARGUMENT], ["--stop", "-S", GetoptLong::REQUIRED_ARGUMENT], ["--stop-all", "-a", GetoptLong::NO_ARGUMENT], ["--pause", "-q", GetoptLong::REQUIRED_ARGUMENT], ["--pause-all", "-Q", GetoptLong::NO_ARGUMENT], ["--resume", "-e", GetoptLong::REQUIRED_ARGUMENT], ["--resume-all", "-E", GetoptLong::NO_ARGUMENT], ["--report", "-r", GetoptLong::REQUIRED_ARGUMENT], ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT], ["--output1", "-1", GetoptLong::REQUIRED_ARGUMENT] ) def give_error $stderr.print "You used incompatible options, probably you mixed --scan with --stop" $stderr.print "or something similar." exit 0 end opt.each do |opt,arg| case opt when '--help' give_help when '--user' user = arg when '--password' password = arg when '--stop' if operation == '' operation = "stop" scanname = arg else give_error end when '--pause' if operation == '' operation = "pause" scanname = arg else give_error end when '--resume' if operation == '' operation = "resume" scanname = arg else give_error end when '--stop-all' if operation == '' operation = "stop-all" else give_error end when '--pause-all' if operation == '' operation = "pause-all" else give_error end when '--resume-all' if operation == '' operation = "resume-all" else give_error end when '--report' if operation == '' operation = "report" scanname = arg else give_error end when '--scan' if operation == '' operation = "scan" scanname = arg else give_error end when '--target' if arg[0..6] == 'file://' f = File.open(arg[7..-1], "r") f.each_line do |line| line=line.chomp line=line.strip unless line == '' or line == nil if targets == '' targets = line else targets = targets + "," + line end end end f.close else # if there's multiple target options, add comma if targets == '' targets = arg else targets = targets + "," + arg end end when '--wait' if arg == '' wait = 15 else wait = arg.to_i end when '--reportdelete' if arg = '' deletereport=true else operation = "reportdelete" scanname = arg end when '--output' output = arg when '--output1' output1 = arg when '--policy' policy = arg when '--status' if operation == '' operation = "status" scanname = arg else give_error end when '--url' url = arg when '--verbose' if arg == '' verbose += 1 else verbose = arg.to_i end when '--debug' if arg == '' debug += 1 else debug = arg.to_i end when '--list-scans' if operation == '' operation = "list-scans" scanname = arg else give_error end when '--list-policy' if operation == '' operation = "list-policy" scanname = arg else give_error end end end if (user == '') or (password == '') $stderr.print "User and password is required to login to Nessus server" $stderr.print "Try --help!" exit 1 end $stderr.print "[i] Targets: " + targets +"\n" if verbose > 0 $stderr.print "[i] Connecting to nessus server: " if verbose > 0 n=NessusXMLRPC::NessusXMLRPC.new(url,user,password) if n.logged_in $stderr.print "OK!\n" if verbose > 0 else $stderr.print "[e] Error connecting/logging to the server!\n" exit 2 end case operation when "scan" if policy == '' $stderr.print "[w] Policy not defined, using first served from the server\n" pid,name = n.policy_get_first $stderr.print "[w] using policy id " + pid + " with name " + name + "\n" else pid=n.policy_get_id(policy) if pid == '' $stderr.print "[e] policy doesn't exit: " + policy + "\n" exit 3 end end if targets == '' $stderr.print "[w] Targets not defined, using localhost as target\n" targets = '127.0.0.1' end $stderr.print "[i] Initiating scan with targets: "+targets+': ' if verbose > 0 uid=n.scan_new(pid,scanname,targets) $stderr.print "done\n" if verbose > 0 unless wait == '' while not n.scan_finished(uid) $stderr.print "[v] Sleeping for " + wait.to_s() + ": " if verbose > 1 sleep wait $stderr.print "done\n" if verbose > 1 stat = n.scan_status(uid) print "\r" + stat if verbose > 0 end else puts uid exit 0 end unless output == '' $stderr.print "[i] Output XML report to file: "+output if verbose > 0 content=n.report_file_download(uid) File.open(output, 'w') {|f| f.write(content) } $stderr.print ": done\n" if verbose > 0 end unless output1 == '' $stderr.print "[i] Output XML1 report to file: "+output1 if verbose > 0 content=n.report_file1_download(uid) File.open(output, 'w') {|f| f.write(content) } $stderr.print ": done\n" if verbose > 0 end if deletereport $stderr.print "[i] Deleting report: " if verbose > 0 n.report_delete(uid) $stderr.print "done\n" if verbose > 0 end when "report" uid=scanname if (output == '') and (output1 == '') $stderr.print "[e] You want report, but specify filename with --output or output1\n" end unless output == '' $stderr.print "[i] Output XML report to file: "+output if verbose > 0 content=n.report_file_download(uid) File.open(output, 'w') {|f| f.write(content) } $stderr.print ": done\n" if verbose > 0 end unless output1 == '' $stderr.print "[i] Output XML1 report to file: "+output1 if verbose > 0 content=n.report1_file_download(uid) File.open(output, 'w') {|f| f.write(content) } $stderr.print ": done\n" if verbose > 0 end if deletereport $stderr.print "[i] Deleting report: " if verbose > 0 n.report_delete(uid) $stderr.print "done\n" if verbose > 0 end when "stop" $stderr.print "[i] Stopping scan: " + scanname if verbose > 0 n.scan_stop(scanname) $stderr.print "done\n" if verbose > 0 when "stop-all" $stderr.print "[i] Stopping all scans: " if verbose > 0 list=n.scan_stop_all $stderr.print "done\n" if verbose > 0 if verbose > 1 list.each {|uuid| puts "[v] Stop all: " + uuid } end when "pause" $stderr.print "[i] Pausing scan: " + scanname if verbose > 0 n.scan_pause(scanname) $stderr.print "done\n" if verbose > 0 when "pause-all" $stderr.print "[i] Pausing all scans: " if verbose > 0 list=n.scan_pause_all $stderr.print "done\n" if verbose > 0 if verbose > 1 list.each {|uuid| puts "[v] Pause all: " + uuid } end when "resume" $stderr.print "[i] Resuming scan: " + scanname if verbose > 0 n.scan_resume(scanname) $stderr.print "done\n" if verbose > 0 when "resume-all" $stderr.print "[i] Resuming all scans: " if verbose > 0 list=n.scan_resume_all $stderr.print "done\n" if verbose > 0 if verbose > 1 list.each {|uuid| puts "[v] Resume all: " + uuid } end when "reportdelete" $stderr.print "[i] Deleting report: " + scanname if verbose > 0 n.report_delete(scanname) $stderr.print "done\n" if verbose > 0 when "status" puts "status: " + n.scan_status(scanname) when "list-scans" list=n.scan_list_hash list.each {|scan| puts scan['id']+":"+scan['name']+":"+ \ scan['current']+"/"+scan['total'] } when "list-policy" list=n.policy_list_names list.each {|policy| puts policy } end $stderr.print "[v] End reached.\n" if verbose > 1