From 9bf8437b6e234c02f3392a5959ee5f9d08b2a322 Mon Sep 17 00:00:00 2001 From: sidafydd Date: Thu, 18 Jul 2013 16:20:44 +0100 Subject: [PATCH 1/2] Added additional functions that list reports available on the nessus server --- README.md | 6 +++++ lib/nessus-xmlrpc.rb | 56 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/README.md b/README.md index 55bfc5e..911d234 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# about this fork + +Changes to orginal include: + - Function added to download list of all reports in UID form + - Function added to download list of all reports included detailed information such as status, timestamp, id and name. + # nessus-xmlrpc Nessus XML RPC library and Nessus Command Line interface to XML RPC diff --git a/lib/nessus-xmlrpc.rb b/lib/nessus-xmlrpc.rb index ebc08b0..4778339 100644 --- a/lib/nessus-xmlrpc.rb +++ b/lib/nessus-xmlrpc.rb @@ -338,6 +338,37 @@ def reports_list file=nessus_http_request('report/list', post) return file end + + # get uids of reports + # + # returns: array of uids of reports + def report_list_uids + post = { "token" => @token } + docxml = nessus_request('report/list', post) + uuids = Array.new + docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| + uuids.push(report.elements['name'].text) + end + return uuids + end + + # get hash of report data + # + # returns: array of hash of reports + def report_list_hash + post = { "token" => @token } + docxml = nessus_request('report/list', post) + reports = Array.new + docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| + entry = Hash.new + entry['id'] = report.elements['name'].text + entry['name'] = report.elements['readableName'].text + entry['status'] = report.elements['status'].text; + entry['timestamp'] = report.elements['timestamp'].text; + reports.push(entry) + end + return reports + end # get report by reportID and return XML file # @@ -541,6 +572,31 @@ def reports_list file=nessus_http_request('report/list', post) return file end + + def report_list_uids + post= { "token" => @token } + docxml=nessus_request('report/list', post) + return docxml.xpath("/reply/contents/reports/report/name").collect(&:text) + end + + def report_list_hash + post = { "token" => @token } + docxml = nessus_request('report/list', post) + items = docxml.xpath("/reply/contents/reports/report") + retval = items.collect do |item| + tmpitem = {} + [ + [:id, 'name'], + [:name, 'readableName'], + [:status, 'status'], + [:timestamp, 'timestamp'] + ].collect do |key, xpath| + tmpitem[key] = item.at_xpath(xpath).content + end + tmpitem + end + return retval + end def report_hosts(report_id) post= { "token" => @token, "report" => report_id } From e3a23a8bf29eb5b39ffe9a32aeb80416b5a6788f Mon Sep 17 00:00:00 2001 From: b00stfr3ak Date: Sun, 16 Mar 2014 20:22:12 -0700 Subject: [PATCH 2/2] Format file, add report_list_hash and report_list_uids Added features from https://github.com/sidafydd/nessus-xmlrpc-ruby/commit/3ca71179a27cb55ee8ddeacd846316a84e1c82c3 Branch is over 8 months old and adds some nice features. I think it would go well in the main branch --- README.md | 6 - lib/nessus-xmlrpc.rb | 1197 +++++++++++++++++++++--------------------- 2 files changed, 598 insertions(+), 605 deletions(-) diff --git a/README.md b/README.md index 911d234..55bfc5e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ -# about this fork - -Changes to orginal include: - - Function added to download list of all reports in UID form - - Function added to download list of all reports included detailed information such as status, timestamp, id and name. - # nessus-xmlrpc Nessus XML RPC library and Nessus Command Line interface to XML RPC diff --git a/lib/nessus-xmlrpc.rb b/lib/nessus-xmlrpc.rb index 4778339..24280ed 100644 --- a/lib/nessus-xmlrpc.rb +++ b/lib/nessus-xmlrpc.rb @@ -4,23 +4,23 @@ # Author:: Vlatko Kosturjak # # (C) Vlatko Kosturjak, Kost. Distributed under GPL and BSD license (dual). -# -# == What is this library? -# -# This library is used for communication with Nessus over XML RPC interface. -# You can start, stop, pause and resume scan. Watch progress and status of scan, +# +# == What is this library? +# +# This library is used for communication with Nessus over XML RPC interface. +# You can start, stop, pause and resume scan. Watch progress and status of scan, # download report, etc. # # == Requirements -# -# Required libraries are standard Ruby libraries: uri, net/https and rexml/document. +# +# Required libraries are standard Ruby libraries: uri, net/https and rexml/document. # # == Optional -# +# # Library is able to use nokogiri if available, but nokogiri is not required. -# +# # == Usage: -# +# # require 'nessus-xmlrpc' # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); # if n.logged_in @@ -40,9 +40,9 @@ require 'rexml/document' # NessusXMLRPC module -# +# # Usage: -# +# # require 'nessus-xmlrpc' # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); # if n.logged_in @@ -51,600 +51,599 @@ # puts "status: " + n.scan_status(uid) # end # -# Check NessusXMLRPCrexml for description of methods implemented +# Check NessusXMLRPCrexml for description of methods implemented # (for both NessusXMLRPCnokogiri and NessusXMLRPCrexml). -module NessusXMLRPC - -# Class which uses standard REXML to parse nessus XML RPC replies. -# It is adviseable to use NessusXMLRPC class, not this class directly. -# As NessusXMLRPC class will use nokogiri or rexml, depending on availability. -class NessusXMLRPCrexml - # initialize object: try to connect to Nessus Scanner using URL, user and password - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - def initialize(url,user,password) - if url == '' - @nurl="https://localhost:8834/" - else - if url =~ /\/$/ - @nurl=url - else - @nurl=url + "/" - end - end - @token='' - login(user,password) - end - - # checks if we're logged in correctly - # - # returns: true if logged in, false if not - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # puts "Logged in" - # else - # puts "Error" - # end - def logged_in - if @token == '' - return false - else - return true - end - end - - # send standard Nessus XML request and check - # - # returns: rexml/document root - def nessus_request(uri, post_data) - body=nessus_http_request(uri, post_data) - # puts response.body - docxml = REXML::Document.new(body) - begin - status = docxml.root.elements['status'].text - rescue - puts "[e] error in XML parsing" - end - if status == "OK" - return docxml - else - return '' - end - end - - # send standard Nessus HTTP request and check - # - # returns: body of response - def nessus_http_request(uri, post_data) - url = URI.parse(@nurl + uri) - request = Net::HTTP::Post.new( url.path ) - request.set_form_data( post_data ) - if not defined? @https - @https = Net::HTTP.new( url.host, url.port ) - @https.use_ssl = true - @https.verify_mode = OpenSSL::SSL::VERIFY_NONE - end - # puts request - begin - response = @https.request( request ) - rescue - puts "[e] error connecting to server: "+ @nurl + " with URI: " + uri - - exit - end - # puts response.body - return response.body - end - - # login with user & password and sets object-wide @token, @name and @admin - def login(user, password) - post = { "login" => user, "password" => password } - docxml=nessus_request('login', post) - if docxml == '' - @token='' - else - @token = docxml.root.elements['contents'].elements['token'].text - @name = docxml.root.elements['contents'].elements['user'].elements['name'].text - @admin = docxml.root.elements['contents'].elements['user'].elements['admin'].text - # puts "Got token:" + @token - end - - end - - # initiate new scan with policy id, descriptive name and list of targets - # - # returns: uuid of scan - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # id,name = n.policy_get_first - # puts "using policy ID: " + id + " with name: " + name - # uid=n.scan_new(id,"textxmlrpc","127.0.0.1") - # end - def scan_new(policy_id,scan_name,target) - post= { "token" => @token, "policy_id" => policy_id, "scan_name" => scan_name, "target" => target } - docxml=nessus_request('scan/new', post) - if docxml == '' - return '' - else - uuid=docxml.root.elements['contents'].elements['scan'].elements['uuid'].text - return uuid - end - end - - # get uids of scans - # - # returns: array of uids of active scans - def scan_list_uids - post= { "token" => @token } - docxml=nessus_request('scan/list', post) - uuids=Array.new - docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| uuids.push(scan.elements['uuid'].text) } - return uuids - end - - # get hash of active scan data - # - # returns: array of hash of active scans - def scan_list_hash - post= { "token" => @token } - docxml=nessus_request('scan/list', post) - scans=Array.new - docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| - entry=Hash.new - entry['id']=scan.elements['uuid'].text - entry['name']=scan.elements['readableName'].text - entry['current']=scan.elements['completion_current'].text; - entry['total']=scan.elements['completion_total'].text; - scans.push(entry) - } - return scans - end - - # get policy by textname and return policyID - # - # returns: policyID - def policy_get_id(textname) - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| - if policy.elements['policyName'].text == textname - return policy.elements['policyID'].text - end - } - return '' - end - - # get first policy from server and returns: policyID, policyName - # - # returns: policyID, policyName - def policy_get_first - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| - return policy.elements['policyID'].text, policy.elements['policyName'].text - } - end - - # get list of policy IDs - # - # returns: array of all policy uids - def policy_list_uids - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - pids=Array.new - docxml.root.elements['contents'].elements['policies'].each_element('//policy') { |policy| - pids.push(policy.elements['policyID'].text) } - return pids - end - - # stop scan identified by scan_uuid - def scan_stop(uuid) - post= { "token" => @token, "scan_uuid" => uuid } - docxml=nessus_request('scan/stop', post) - return docxml - end - # stop all active scans - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_stop_all - # end - def scan_stop_all - b=scan_list_uids - b.each {|uuid| - scan_stop(uuid) - } - return b - end - # pause scan identified by scan_uuid - def scan_pause(uuid) - post= { "token" => @token, "scan_uuid" => uuid } - docxml=nessus_request('scan/pause', post) - return docxml - end - # pause all active scans - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_pause_all - # end - def scan_pause_all - b=scan_list_uids - b.each {|uuid| - scan_pause(uuid) - } - return b - end - # remove scan identified by uuid - def scan_resume(uuid) - post= { "token" => @token, "scan_uuid" => uuid } - docxml=nessus_request('scan/resume', post) - return docxml - end - # resume all active scans - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_resume_all - # end - def scan_resume_all - b=scan_list_uids - b.each {|uuid| - scan_resume(uuid) - } - return b - end - - # check status of scan identified by uuid - def scan_status(uuid) - post= { "token" => @token, "report" => uuid } - docxml=nessus_request('report/list', post) - docxml.root.elements['contents'].elements['reports'].each_element('//report') { |report| - if report.elements['name'].text == uuid - return (report.elements['status'].text) - end - } - return '' - end - - # check if scan is finished (completed to be exact) identified by uuid - def scan_finished(uuid) - status=scan_status(uuid) - if status == "completed" - return true - else - return false - end - end - - # get an XML file that lists all the reports - def reports_list - post= { "token" => @token } - file=nessus_http_request('report/list', post) - return file - end - - # get uids of reports - # - # returns: array of uids of reports - def report_list_uids - post = { "token" => @token } - docxml = nessus_request('report/list', post) - uuids = Array.new - docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| - uuids.push(report.elements['name'].text) - end - return uuids +module NessusXMLRPC + + # Class which uses standard REXML to parse nessus XML RPC replies. + # It is adviseable to use NessusXMLRPC class, not this class directly. + # As NessusXMLRPC class will use nokogiri or rexml, depending on availability. + class NessusXMLRPCrexml + # initialize object: try to connect to Nessus Scanner using URL, user and password + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + def initialize(url,user,password) + if url == '' + @nurl="https://localhost:8834/" + else + if url =~ /\/$/ + @nurl=url + else + @nurl=url + "/" end - - # get hash of report data - # - # returns: array of hash of reports - def report_list_hash - post = { "token" => @token } - docxml = nessus_request('report/list', post) - reports = Array.new - docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| - entry = Hash.new - entry['id'] = report.elements['name'].text - entry['name'] = report.elements['readableName'].text - entry['status'] = report.elements['status'].text; - entry['timestamp'] = report.elements['timestamp'].text; - reports.push(entry) - end - return reports + end + @token='' + login(user,password) + end + + # checks if we're logged in correctly + # + # returns: true if logged in, false if not + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # puts "Logged in" + # else + # puts "Error" + # end + def logged_in + if @token == '' + return false + else + return true + end + end + + # send standard Nessus XML request and check + # + # returns: rexml/document root + def nessus_request(uri, post_data) + body=nessus_http_request(uri, post_data) + # puts response.body + docxml = REXML::Document.new(body) + begin + status = docxml.root.elements['status'].text + rescue + puts "[e] error in XML parsing" + end + if status == "OK" + return docxml + else + return '' + end + end + + # send standard Nessus HTTP request and check + # + # returns: body of response + def nessus_http_request(uri, post_data) + url = URI.parse(@nurl + uri) + request = Net::HTTP::Post.new( url.path ) + request.set_form_data( post_data ) + if not defined? @https + @https = Net::HTTP.new( url.host, url.port ) + @https.use_ssl = true + @https.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + # puts request + begin + response = @https.request( request ) + rescue + puts "[e] error connecting to server: "+ @nurl + " with URI: " + uri + + exit + end + # puts response.body + return response.body + end + + # login with user & password and sets object-wide @token, @name and @admin + def login(user, password) + post = { "login" => user, "password" => password } + docxml=nessus_request('login', post) + if docxml == '' + @token='' + else + @token = docxml.root.elements['contents'].elements['token'].text + @name = docxml.root.elements['contents'].elements['user'].elements['name'].text + @admin = docxml.root.elements['contents'].elements['user'].elements['admin'].text + # puts "Got token:" + @token + end + + end + + # initiate new scan with policy id, descriptive name and list of targets + # + # returns: uuid of scan + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # id,name = n.policy_get_first + # puts "using policy ID: " + id + " with name: " + name + # uid=n.scan_new(id,"textxmlrpc","127.0.0.1") + # end + def scan_new(policy_id,scan_name,target) + post= { "token" => @token, "policy_id" => policy_id, "scan_name" => scan_name, "target" => target } + docxml=nessus_request('scan/new', post) + if docxml == '' + return '' + else + uuid=docxml.root.elements['contents'].elements['scan'].elements['uuid'].text + return uuid + end + end + + # get uids of scans + # + # returns: array of uids of active scans + def scan_list_uids + post= { "token" => @token } + docxml=nessus_request('scan/list', post) + uuids=Array.new + docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| uuids.push(scan.elements['uuid'].text) } + return uuids + end + + # get hash of active scan data + # + # returns: array of hash of active scans + def scan_list_hash + post= { "token" => @token } + docxml=nessus_request('scan/list', post) + scans=Array.new + docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| + entry=Hash.new + entry['id']=scan.elements['uuid'].text + entry['name']=scan.elements['readableName'].text + entry['current']=scan.elements['completion_current'].text; + entry['total']=scan.elements['completion_total'].text; + scans.push(entry) + } + return scans + end + + # get policy by textname and return policyID + # + # returns: policyID + def policy_get_id(textname) + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| + if policy.elements['policyName'].text == textname + return policy.elements['policyID'].text end - - # get report by reportID and return XML file - # - # returns: XML file of report (nessus v2 format) - def report_file_download(id) - post= { "token" => @token, "report" => id } - file=nessus_http_request('file/report/download', post) - return file - end - - # get report by reportID and return XML file (version 1) - # - # returns: XML file of report (nessus v1 format) - def report_file1_download(id) - post= { "token" => @token, "report" => id, "v1" => "true" } - file=nessus_http_request('file/report/download', post) - return file - end - - # delete report by report ID - def report_delete(id) - post= { "token" => @token, "report" => id } - docxml=nessus_request('report/delete', post) - return docxml - end - - # get list of names of policies - # - # returns: array of names - def policy_list_names - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - list = Array.new - docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| - list.push policy.elements['policyName'].text - } - return list - end - - # get hosts for particular report - # - # returns: array of hosts - def report_hosts(report_id) - post= { "token" => @token, "report" => report_id } - docxml=nessus_request('report/hosts', post) - list = Array.new - docxml.root.elements['contents'].elements['hostList'].each_element('//host') { |host| - list.push host.elements['hostname'].text - } - return list - end - - # get host details for particular host identified by report id - # - # returns: severity, current, total - def report_get_host(report_id,host) - post= { "token" => @token, "report" => report_id } - docxml=nessus_request('report/hosts', post) - docxml.root.elements['contents'].elements['hostList'].each_element('//host') { |host| - if host.elements['hostname'].text == host - retval={} - retval["severity"] = host.elements['severity'].text - retval["current"] = host.elements['scanProgressCurrent'].text - retval["total"] = host.elements['scanProgressTotal'].text - return retval - end - } - end - #-- ToDo items - def plugins_list - post= { "token" => @token } - docxml=nessus_request('plugins/list', post) - return docxml - end - def users_list - post= { "token" => @token } - docxml=nessus_request('users/list', post) - return docxml - end -end # end of NessusXMLRPC::Class - -# use nokogiri if available (it's faster!) -nokogiri=true -begin - require 'nokogiri' -rescue LoadError - nokogiri=false -end - -# if found nokogiri -if nokogiri -# Class which uses nokogiri to parse nessus XML RPC replies. -# It is adviseable to use NessusXMLRPC class, not this class directly. -# As NessusXMLRPC class will use nokogiri or rexml, depending on availability. -# -# Documentation for this class documents only differences from NessusXMLRPCrexml. -# So, check NessusXMLRPCrexml for method documentation -class NessusXMLRPCnokogiri < NessusXMLRPCrexml - # send standard Nessus XML request and check - # - # return: nokogiri XML file - def nessus_request(uri, post_data) - body=nessus_http_request(uri, post_data) - docxml = Nokogiri::XML.parse(body) - begin - status = docxml.xpath("/reply/status").collect(&:text)[0] - rescue - puts "[e] error in XML parsing" - end - if status == "OK" - return docxml - else - return '' - end - end - - def login(user, password) - post = { "login" => user, "password" => password } - docxml=nessus_request('login', post) - if docxml == '' - @token='' - else - @token = docxml.xpath("/reply/contents/token").collect(&:text)[0] - @name = docxml.xpath("/reply/contents/user/name").collect(&:text)[0] - @admin = docxml.xpath("/reply/contents/user/admin").collect(&:text)[0] - end - - end - - def scan_new(policy_id,scan_name,target) - post= { "token" => @token, "policy_id" => policy_id, "scan_name" => scan_name, "target" => target } - docxml=nessus_request('scan/new', post) - if docxml == '' - return '' - else - uuid=docxml.xpath("/reply/contents/scan/uuid").collect(&:text)[0] - return uuid - end - end - - def scan_status(uuid) - post= { "token" => @token, "report" => uuid } - docxml=nessus_request('report/list', post) - return docxml.xpath("/reply/contents/reports/report/name[text()='"+uuid+"']/../status").collect(&:text)[0] - end - - def scan_list_uids - post= { "token" => @token } - docxml=nessus_request('scan/list', post) - return docxml.xpath("/reply/contents/scans/scanList/scan/uuid").collect(&:text) - end - - def scan_list_hash - post= { "token" => @token } - docxml=nessus_request('scan/list', post) - items = docxml.xpath("/reply/contents/scans/scanList/scan") - retval = items.collect do |item| - tmpitem = {} - [ - [:id, 'uuid'], - [:name, 'readableName'], - [:current, 'completion_current'], - [:total, 'completion_total'] - ].collect do |key, xpath| - tmpitem[key] = item.at_xpath(xpath).content - end - tmpitem - end - return retval - end - - def policy_get_id(textname) - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - return docxml.xpath("/reply/contents/policies/policy[policyName='"+textname+"']/policyID").collect(&:text) - end - - def policy_list_uids - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - return docxml.xpath("/reply/contents/policies/policy/policyID").collect(&:text) - end - - def policy_get_first - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - id=docxml.xpath("/reply/contents/policies/policy/policyID").collect(&:text)[0] - name=docxml.xpath("/reply/contents/policies/policy/policyName").collect(&:text)[0] - return id, name - end - - def policy_list_names - post= { "token" => @token } - docxml=nessus_request('policy/list', post) - return docxml.xpath("/reply/contents/policies/policy/policyName").collect(&:text) - end - - # get an XML file that lists all the reports - def reports_list - post= { "token" => @token } - file=nessus_http_request('report/list', post) - return file + } + return '' + end + + # get first policy from server and returns: policyID, policyName + # + # returns: policyID, policyName + def policy_get_first + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| + return policy.elements['policyID'].text, policy.elements['policyName'].text + } + end + + # get list of policy IDs + # + # returns: array of all policy uids + def policy_list_uids + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + pids=Array.new + docxml.root.elements['contents'].elements['policies'].each_element('//policy') { |policy| + pids.push(policy.elements['policyID'].text) } + return pids + end + + # stop scan identified by scan_uuid + def scan_stop(uuid) + post= { "token" => @token, "scan_uuid" => uuid } + docxml=nessus_request('scan/stop', post) + return docxml + end + # stop all active scans + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # n.scan_stop_all + # end + def scan_stop_all + b=scan_list_uids + b.each {|uuid| + scan_stop(uuid) + } + return b + end + # pause scan identified by scan_uuid + def scan_pause(uuid) + post= { "token" => @token, "scan_uuid" => uuid } + docxml=nessus_request('scan/pause', post) + return docxml + end + # pause all active scans + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # n.scan_pause_all + # end + def scan_pause_all + b=scan_list_uids + b.each {|uuid| + scan_pause(uuid) + } + return b + end + # remove scan identified by uuid + def scan_resume(uuid) + post= { "token" => @token, "scan_uuid" => uuid } + docxml=nessus_request('scan/resume', post) + return docxml + end + # resume all active scans + # + # Usage: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # n.scan_resume_all + # end + def scan_resume_all + b=scan_list_uids + b.each {|uuid| + scan_resume(uuid) + } + return b + end + + # check status of scan identified by uuid + def scan_status(uuid) + post= { "token" => @token, "report" => uuid } + docxml=nessus_request('report/list', post) + docxml.root.elements['contents'].elements['reports'].each_element('//report') { |report| + if report.elements['name'].text == uuid + return (report.elements['status'].text) + end + } + return '' + end + + # check if scan is finished (completed to be exact) identified by uuid + def scan_finished(uuid) + status=scan_status(uuid) + if status == "completed" + return true + else + return false + end + end + + # get an XML file that lists all the reports + def reports_list + post= { "token" => @token } + file=nessus_http_request('report/list', post) + return file + end + + # get uids of reports + # + # returns: array of uids of reports + def report_list_uids + post = { "token" => @token } + docxml = nessus_request('report/list', post) + uuids = Array.new + docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| + uuids.push(report.elements['name'].text) + end + return uuids + end + + # get hash of report data + # + # returns: array of hash of reports + def report_list_hash + post = { "token" => @token } + docxml = nessus_request('report/list', post) + reports = Array.new + docxml.root.elements['contents'].elements['reports'].each_element('//report') do |report| + entry = Hash.new + entry['id'] = report.elements['name'].text + entry['name'] = report.elements['readableName'].text + entry['status'] = report.elements['status'].text; + entry['timestamp'] = report.elements['timestamp'].text; + reports.push(entry) + end + return reports + end + + # get report by reportID and return XML file + # + # returns: XML file of report (nessus v2 format) + def report_file_download(id) + post= { "token" => @token, "report" => id } + file=nessus_http_request('file/report/download', post) + return file + end + + # get report by reportID and return XML file (version 1) + # + # returns: XML file of report (nessus v1 format) + def report_file1_download(id) + post= { "token" => @token, "report" => id, "v1" => "true" } + file=nessus_http_request('file/report/download', post) + return file + end + + # delete report by report ID + def report_delete(id) + post= { "token" => @token, "report" => id } + docxml=nessus_request('report/delete', post) + return docxml + end + + # get list of names of policies + # + # returns: array of names + def policy_list_names + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + list = Array.new + docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| + list.push policy.elements['policyName'].text + } + return list + end + + # get hosts for particular report + # + # returns: array of hosts + def report_hosts(report_id) + post= { "token" => @token, "report" => report_id } + docxml=nessus_request('report/hosts', post) + list = Array.new + docxml.root.elements['contents'].elements['hostList'].each_element('//host') { |host| + list.push host.elements['hostname'].text + } + return list + end + + # get host details for particular host identified by report id + # + # returns: severity, current, total + def report_get_host(report_id,host) + post= { "token" => @token, "report" => report_id } + docxml=nessus_request('report/hosts', post) + docxml.root.elements['contents'].elements['hostList'].each_element('//host') { |host| + if host.elements['hostname'].text == host + retval={} + retval["severity"] = host.elements['severity'].text + retval["current"] = host.elements['scanProgressCurrent'].text + retval["total"] = host.elements['scanProgressTotal'].text + return retval + end + } + end + #-- ToDo items + def plugins_list + post= { "token" => @token } + docxml=nessus_request('plugins/list', post) + return docxml + end + def users_list + post= { "token" => @token } + docxml=nessus_request('users/list', post) + return docxml + end + end # end of NessusXMLRPC::Class + + # use nokogiri if available (it's faster!) + nokogiri=true + begin + require 'nokogiri' + rescue LoadError + nokogiri=false end - - def report_list_uids - post= { "token" => @token } - docxml=nessus_request('report/list', post) - return docxml.xpath("/reply/contents/reports/report/name").collect(&:text) + + # if found nokogiri + if nokogiri + # Class which uses nokogiri to parse nessus XML RPC replies. + # It is adviseable to use NessusXMLRPC class, not this class directly. + # As NessusXMLRPC class will use nokogiri or rexml, depending on availability. + # + # Documentation for this class documents only differences from NessusXMLRPCrexml. + # So, check NessusXMLRPCrexml for method documentation + class NessusXMLRPCnokogiri < NessusXMLRPCrexml + # send standard Nessus XML request and check + # + # return: nokogiri XML file + def nessus_request(uri, post_data) + body=nessus_http_request(uri, post_data) + docxml = Nokogiri::XML.parse(body) + begin + status = docxml.xpath("/reply/status").collect(&:text)[0] + rescue + puts "[e] error in XML parsing" + end + if status == "OK" + return docxml + else + return '' + end + end + + def login(user, password) + post = { "login" => user, "password" => password } + docxml=nessus_request('login', post) + if docxml == '' + @token='' + else + @token = docxml.xpath("/reply/contents/token").collect(&:text)[0] + @name = docxml.xpath("/reply/contents/user/name").collect(&:text)[0] + @admin = docxml.xpath("/reply/contents/user/admin").collect(&:text)[0] end - def report_list_hash - post = { "token" => @token } - docxml = nessus_request('report/list', post) - items = docxml.xpath("/reply/contents/reports/report") - retval = items.collect do |item| - tmpitem = {} - [ - [:id, 'name'], - [:name, 'readableName'], - [:status, 'status'], - [:timestamp, 'timestamp'] - ].collect do |key, xpath| - tmpitem[key] = item.at_xpath(xpath).content - end - tmpitem - end - return retval + end + + def scan_new(policy_id,scan_name,target) + post= { "token" => @token, "policy_id" => policy_id, "scan_name" => scan_name, "target" => target } + docxml=nessus_request('scan/new', post) + if docxml == '' + return '' + else + uuid=docxml.xpath("/reply/contents/scan/uuid").collect(&:text)[0] + return uuid + end + end + + def scan_status(uuid) + post= { "token" => @token, "report" => uuid } + docxml=nessus_request('report/list', post) + return docxml.xpath("/reply/contents/reports/report/name[text()='"+uuid+"']/../status").collect(&:text)[0] + end + + def scan_list_uids + post= { "token" => @token } + docxml=nessus_request('scan/list', post) + return docxml.xpath("/reply/contents/scans/scanList/scan/uuid").collect(&:text) + end + + def scan_list_hash + post= { "token" => @token } + docxml=nessus_request('scan/list', post) + items = docxml.xpath("/reply/contents/scans/scanList/scan") + retval = items.collect do |item| + tmpitem = {} + [ + [:id, 'uuid'], + [:name, 'readableName'], + [:current, 'completion_current'], + [:total, 'completion_total'] + ].collect do |key, xpath| + tmpitem[key] = item.at_xpath(xpath).content + end + tmpitem + end + return retval + end + + def policy_get_id(textname) + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + return docxml.xpath("/reply/contents/policies/policy[policyName='"+textname+"']/policyID").collect(&:text) + end + + def policy_list_uids + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + return docxml.xpath("/reply/contents/policies/policy/policyID").collect(&:text) + end + + def policy_get_first + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + id=docxml.xpath("/reply/contents/policies/policy/policyID").collect(&:text)[0] + name=docxml.xpath("/reply/contents/policies/policy/policyName").collect(&:text)[0] + return id, name + end + + def policy_list_names + post= { "token" => @token } + docxml=nessus_request('policy/list', post) + return docxml.xpath("/reply/contents/policies/policy/policyName").collect(&:text) + end + + # get an XML file that lists all the reports + def reports_list + post= { "token" => @token } + file=nessus_http_request('report/list', post) + return file + end + + def report_list_uids + post= { "token" => @token } + docxml=nessus_request('report/list', post) + return docxml.xpath("/reply/contents/reports/report/name").collect(&:text) + end + + def report_list_hash + post = { "token" => @token } + docxml = nessus_request('report/list', post) + items = docxml.xpath("/reply/contents/reports/report") + retval = items.collect do |item| + tmpitem = {} + [ + [:id, 'name'], + [:name, 'readableName'], + [:status, 'status'], + [:timestamp, 'timestamp'] + ].collect do |key, xpath| + tmpitem[key] = item.at_xpath(xpath).content + end + tmpitem end - - def report_hosts(report_id) - post= { "token" => @token, "report" => report_id } - docxml=nessus_request('report/hosts', post) - return docxml.xpath("/reply/contents/hostList/host/hostname").collect(&:text) - end - - def report_get_host(report_id,host) - post= { "token" => @token, "report" => report_id } - docxml=nessus_request('report/hosts', post) - items = docxml.xpath("/reply/contents/hostList/host/hostname[text()='"+host+"']") - retval = items.collect do |item| - tmpitem = {} - [ - [:severity, 'severity'], - [:current, 'scanProgressCurrent'], - [:total, 'scanProgressTotal'] - ].collect do |key, xpath| - tmpitem[key] = item.at_xpath(xpath).content - end - tmpitem - end - return retval - end - -end # end of NessusXMLRPCnokogiri::Class - # Main class which controls Nessus using XMLRPC. - # It is adviseable to use this NessusXMLRPC class, and not NessusXMLRPCnokogiri or NessusXMLRPCrexml, - # As NessusXMLRPC class will use nokogiri or rexml, depending on availability. - # Of course, choosing nokogiri first because of speed. - # - # Example: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # id,name = n.policy_get_first - # uid=n.scan_new(id,"textxmlrpc","127.0.0.1") - # puts "status: " + n.scan_status(uid) - # end - # - # Check NessusXMLRPCrexml for description of methods implemented - # (for both NessusXMLRPCnokogiri and NessusXMLRPCrexml). - class NessusXMLRPC < NessusXMLRPCnokogiri - end -else # nokogiri not found, use REXML - class NessusXMLRPC < NessusXMLRPCrexml - end -end # if nokogiri + return retval + end + + def report_hosts(report_id) + post= { "token" => @token, "report" => report_id } + docxml=nessus_request('report/hosts', post) + return docxml.xpath("/reply/contents/hostList/host/hostname").collect(&:text) + end + + def report_get_host(report_id,host) + post= { "token" => @token, "report" => report_id } + docxml=nessus_request('report/hosts', post) + items = docxml.xpath("/reply/contents/hostList/host/hostname[text()='"+host+"']") + retval = items.collect do |item| + tmpitem = {} + [ + [:severity, 'severity'], + [:current, 'scanProgressCurrent'], + [:total, 'scanProgressTotal'] + ].collect do |key, xpath| + tmpitem[key] = item.at_xpath(xpath).content + end + tmpitem + end + return retval + end + + end # end of NessusXMLRPCnokogiri::Class + # Main class which controls Nessus using XMLRPC. + # It is adviseable to use this NessusXMLRPC class, and not NessusXMLRPCnokogiri or NessusXMLRPCrexml, + # As NessusXMLRPC class will use nokogiri or rexml, depending on availability. + # Of course, choosing nokogiri first because of speed. + # + # Example: + # + # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); + # if n.logged_in + # id,name = n.policy_get_first + # uid=n.scan_new(id,"textxmlrpc","127.0.0.1") + # puts "status: " + n.scan_status(uid) + # end + # + # Check NessusXMLRPCrexml for description of methods implemented + # (for both NessusXMLRPCnokogiri and NessusXMLRPCrexml). + class NessusXMLRPC < NessusXMLRPCnokogiri + end + else # nokogiri not found, use REXML + class NessusXMLRPC < NessusXMLRPCrexml + end + end # if nokogiri end # of Module -