diff --git a/Gemfile.lock b/Gemfile.lock
index 38db437011..cb4ef86add 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -23,6 +23,7 @@ PATH
nokogiri
octokit
openssl-ccm
+ openvas-omp
packetfu
patch_finder
pcaprub
@@ -186,6 +187,7 @@ GEM
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
openssl-ccm (1.2.1)
+ openvas-omp (0.0.4)
packetfu (1.1.11)
network_interface (~> 0.0)
pcaprub (~> 0.12)
diff --git a/lib/openvas/openvas-omp.rb b/lib/openvas/openvas-omp.rb
deleted file mode 100644
index fb7e3b7a51..0000000000
--- a/lib/openvas/openvas-omp.rb
+++ /dev/null
@@ -1,640 +0,0 @@
-#!/usr/bin/env ruby
-#
-# This plugin provides integration with OpenVAS. Written by kost and
-# averagesecurityguy.
-#
-# Distributed under MIT license:
-# http://www.opensource.org/licenses/mit-license.php
-#
-
-require 'socket'
-require 'timeout'
-require 'openssl'
-require 'rexml/document'
-require 'rexml/text'
-require 'base64'
-
-# OpenVASOMP module
-#
-# Usage: require 'openvas-omp'
-
-module OpenVASOMP
-
-#------------------------------
-# Error Classes
-#------------------------------
- class OMPError < :: RuntimeError
- attr_accessor :reason
- def initialize(reason = '')
- self.reason = reason
- end
- def to_s
- "OpenVAS OMP: #{self.reason}"
- end
- end
-
- class OMPConnectionError < OMPError
- def initialize
- self.reason = "Could not connect to server"
- end
- end
-
- class OMPResponseError < OMPError
- def initialize
- self.reason = "Error in OMP request/response"
- end
- end
-
- class OMPAuthError < OMPError
- def initialize
- self.reason = "Authentication failed"
- end
- end
-
- class XMLParsingError < OMPError
- def initialize
- self.reason = "XML parsing failed"
- end
- end
-
-
-#------------------------------
-# Connection Class
-#------------------------------
- class OpenVASConnection
- attr_accessor :socket, :bufsize, :debug
-
- def initialize(host="127.0.0.1", port=9390, debug=false)
- @host = host
- @port = port
- @socket = nil
- @bufsize = 16384
- @debug = debug
- end
-
- def connect
- if @debug then puts "Connecting to server #{@host} on port #{@port}" end
- plain_socket = TCPSocket.open(@host, @port)
- ssl_context = OpenSSL::SSL::SSLContext.new()
- @socket = OpenSSL::SSL::SSLSocket.new(plain_socket, ssl_context)
- @socket.sync_close = true
- @socket.connect
- end
-
- def disconnect
- if @debug then
- puts "Closing connection to server #{@host} on port #{@port}" end
- if @socket then @socket.close end
- end
-
- def sendrecv(data)
- # Send the data
- if @debug then puts "Preparing to send data" end
- if not @socket then connect end
- if @debug then puts "SENDING: " + data end
- @socket.puts(data)
-
- # Receive the response
- resp = ''
- size = 0
- begin
- begin
- timeout(@read_timeout) {
- a = @socket.sysread(@bufsize)
- size = a.length
- resp << a
- }
- rescue Timeout::Error
- size = 0
- rescue EOFError
- raise OMPResponseError
- end
- end while size >= @bufsize
-
- if @debug then puts "RECEIVED: " + resp end
- return resp
- end
- end
-
-
-#------------------------------
-# OpenVASOMP class
-#------------------------------
- class OpenVASOMP
- # initialize object: try to connect to OpenVAS using URL, user and password
- attr_reader :targets, :tasks, :configs, :formats, :reports
-
- def initialize(user="openvas", pass="openvas", host="localhost", port=9392, debug=false)
- @debug = debug
- @token = ''
- @server = OpenVASConnection.new(host, port, debug)
- @server.connect
- login(user, pass)
- @configs = nil
- @tasks = nil
- @targets = nil
- @formats = nil
- @reports = nil
- config_get_all
- task_get_all
- target_get_all
- format_get_all
- report_get_all
- end
-
- #--------------------------
- # Low level commands. Only
- # used by OpenVASOMP class.
- #--------------------------
- # Nests a string inside an XML tag specified by root
- def xml_str(root, str)
- return "<#{root}>#{str}#{root}>"
- end
-
- # Creates an XML root with child elements specified by a hash
- def xml_elems(root, elems)
- xml = REXML::Element.new(root)
- elems.each do |key, val|
- e = xml.add_element(key)
- e.text = val
- end
- return xml.to_s
- end
-
- # Creates and XML element with attributes specified by a hash
- def xml_attrs(elem, attribs)
- xml = REXML::Element.new(elem)
- attribs.each do |key, val|
- xml.attributes[key] = val
- end
- return xml.to_s
- end
-
- # Send authentication string and return an XML object (authentication token)
- def auth_request_xml(request)
- if @debug
- puts "Sending Request: #{request}"
- end
- resp = @server.sendrecv(request)
- begin
- docxml = REXML::Document.new(resp)
- status = docxml.root.attributes['status'].to_i
- status_text = docxml.root.attributes['status_text']
- if @debug
- puts "Status: #{status}"
- puts "Status Text: #{status_text}"
- end
- rescue
- raise XMLParsingError
- end
-
- return status, status_text
- end
-
- # Send string request wrapped with authentication XML and return
- # an XML object
- def omp_request_xml(request)
- if @debug
- puts "Sending Request: #{request}"
- end
- resp = @server.sendrecv(@token + request)
- begin
- # Wrap the response in XML tags to use next_element properly.
- docxml = REXML::Document.new("" + resp + "")
- resp = docxml.root.elements['authenticate_response'].next_element
- status = resp.attributes['status'].to_i
- status_text = resp.attributes['status_text']
- if @debug
- puts "Status: #{status}"
- puts "Status Text: #{status_text}"
- end
- rescue
- raise XMLParsingError
- end
-
- return status, status_text, resp
- end
-
- #--------------------------
- # Class API methods.
- #--------------------------
- # Sets debug level
- def debug(value)
- if value == 0
- @debug = false
- @server.debug = false
- return "Debug is deactivated."
- else
- @debug = true
- @server.debug = true
- return "Debug is activated."
- end
- end
-
- # get OMP version (you don't need to be authenticated)
- def get_version
- status, status_text, resp = omp_request_xml("")
- begin
- version = resp.elements['version'].text
- return version
- rescue
- raise XMLParsingError
- end
- end
-
- # login to OpenVAS server.
- # if successful returns authentication XML for further usage
- # if unsuccessful returns empty string
- def login(user, pass)
- creds = xml_elems("credentials", {"username"=> user, "password" => pass})
- req = xml_str("authenticate", creds)
- status, status_text = auth_request_xml(req)
-
- if status == 200
- @token = req
- else
- raise OMPAuthError
- end
- end
-
- # Logout by disconnecting from the server and deleting the
- # authentication string. There are no sessions in OMP, must
- # send the credentials every time.
- def logout
- @server.disconnect()
- @token = ''
- end
-
-#------------------------------
-# Target Functions
-#------------------------------
-
- # OMP - Get all targets for scanning and returns array of hashes
- # with following keys: id,name,comment,hosts,max_hosts,in_use
- #
- # Usage:
- # array_of_hashes = target_get_all()
- #
- def target_get_all()
- begin
- status, status_text, resp = omp_request_xml("")
-
- list = Array.new
- resp.elements.each('//get_targets_response/target') do |target|
- td = Hash.new
- td["id"] = target.attributes["id"]
- td["name"] = target.elements["name"].text
- td["comment"] = target.elements["comment"].text
- td["hosts"] = target.elements["hosts"].text
- td["max_hosts"] = target.elements["max_hosts"].text
- td["in_use"] = target.elements["in_use"].text
- list.push td
- end
- @targets = list
- return list
- rescue
- raise OMPResponseError
- end
- end
-
- # OMP - Create target for scanning
- #
- # Usage:
- #
- # target_id = ov.target_create("name"=>"localhost",
- # "hosts"=>"127.0.0.1","comment"=>"yes")
- #
- def target_create(name, hosts, comment)
- req = xml_elems("create_target", {"name"=>name, "hosts"=>hosts, "comment"=>comment})
-
- begin
- status, status_text, resp = omp_request_xml(req)
- target_get_all
- return "#{status_text}: #{resp.attributes['id']}"
- rescue
- raise OMPResponseError
- end
- end
-
- # OMP - Delete target
- #
- # Usage:
- #
- # ov.target_delete(target_id)
- #
- def target_delete(id)
- target = @targets[id.to_i]
- if not target
- raise OMPError.new("Invalid target id.")
- end
- req = xml_attrs("delete_target",{"target_id" => target["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- target_get_all
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- #--------------------------
- # Task Functions
- #--------------------------
- # In short: Create a task.
- #
- # The client uses the create_task command to create a new task.
- #
- def task_create(name, comment, config_id, target_id)
- config = @configs[config_id.to_i]
- target = @targets[target_id.to_i]
- config = xml_attrs("config", {"id"=>config["id"]})
- target = xml_attrs("target", {"id"=>target["id"]})
- namestr = xml_str("name", name)
- commstr = xml_str("comment", comment)
-
- req = xml_str("create_task", namestr + commstr + config + target)
-
- begin
- status, status_text, resp = omp_request_xml(req)
- task_get_all
- return "#{status_text}: #{resp.attributes['id']}"
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Delete a task.
- #
- # The client uses the delete_task command to delete an existing task,
- # including all reports associated with the task.
- #
- def task_delete(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("delete_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- task_get_all
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Get all tasks.
- #
- # The client uses the get_tasks command to get task information.
- #
- def task_get_all()
- begin
- status, status_text, resp = omp_request_xml("")
-
- list = Array.new
- resp.elements.each('//get_tasks_response/task') do |task|
- td = Hash.new
- td["id"] = task.attributes["id"]
- td["name"] = task.elements["name"].text
- td["comment"] = task.elements["comment"].text
- td["status"] = task.elements["status"].text
- td["progress"] = task.elements["progress"].text
- list.push td
- end
- @tasks = list
- return list
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Manually start an existing task.
- #
- # The client uses the start_task command to manually start an existing
- # task.
- #
- def task_start(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("start_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Stop a running task.
- #
- # The client uses the stop_task command to manually stop a running
- # task.
- #
- def task_stop(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("stop_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Pause a running task.
- #
- # The client uses the pause_task command to manually pause a running
- # task.
- #
- def task_pause(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("pause_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Resume task if stopped, else start task.
- #
- # The client uses the resume_or_start_task command to manually start
- # an existing task, ensuring that the task will resume from its
- # previous position if the task is in the Stopped state.
- #
- def task_resume_or_start(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("resume_or_start_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # In short: Resume a puased task
- #
- # The client uses the resume_paused_task command to manually resume
- # a paused task.
- #
- def task_resume_paused(task_id)
- task = @tasks[task_id.to_i]
- if not task
- raise OMPError.new("Invalid task id.")
- end
- req = xml_attrs("resume_paused_task",{"task_id" => task["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- #--------------------------
- # Config Functions
- #--------------------------
- # OMP - get configs and returns hash as response
- # hash[config_id]=config_name
- #
- # Usage:
- #
- # array_of_hashes=ov.config_get_all()
- #
- def config_get_all()
- begin
- status, status_text, resp = omp_request_xml("")
-
- list = Array.new
- resp.elements.each('//get_configs_response/config') do |config|
- c = Hash.new
- c["id"] = config.attributes["id"]
- c["name"] = config.elements["name"].text
- list.push c
- end
- @configs = list
- return list
- rescue
- raise OMPResponseError
- end
- end
-
-
- #--------------------------
- # Format Functions
- #--------------------------
- # Get a list of report formats
- def format_get_all()
- begin
- status, status_text, resp = omp_request_xml("")
- if @debug then print resp end
-
- list = Array.new
- resp.elements.each('//get_report_formats_response/report_format') do |report|
- td = Hash.new
- td["id"] = report.attributes["id"]
- td["name"] = report.elements["name"].text
- td["extension"] = report.elements["extension"].text
- td["summary"] = report.elements["summary"].text
- list.push td
- end
- @formats = list
- return list
- rescue
- raise OMPResponseError
- end
- end
-
-
- #--------------------------
- # Report Functions
- #--------------------------
- # Get a list of reports
- def report_get_all()
- begin
- status, status_text, resp = omp_request_xml("")
-
- list = Array.new
- resp.elements.each('//get_reports_response/report') do |report|
- td = Hash.new
- td["id"] = report.attributes["id"]
- td["task"] = report.elements["report/task/name"].text
- td["start_time"] = report.elements["report/scan_start"].text
- td["stop_time"] = report.elements["report/scan_end"].text
- list.push td
- end
- @reports = list
- return list
- rescue
- raise OMPResponseError
- end
- end
-
- def report_delete(report_id)
- report = @reports[report_id.to_i]
- if not report
- raise OMPError.new("Invalid report id.")
- end
- req = xml_attrs("delete_report",{"report_id" => report["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- report_get_all
- return status_text
- rescue
- raise OMPResponseError
- end
- end
-
- # Get a report by id. Must also specify the format_id
- def report_get_by_id(report_id, format_id)
- report = @reports[report_id.to_i]
- if not report
- raise OMPError.new("Invalid report id.")
- end
-
- format = @formats[format_id.to_i]
- if not format
- raise OMPError.new("Invalid format id.")
- end
-
- req = xml_attrs("get_reports", {"report_id"=>report["id"], "format_id"=>format["id"]})
- begin
- status, status_text, resp = omp_request_xml(req)
- rescue
- raise OMPResponseError
- end
-
- if status == "404"
- raise OMPError.new(status_text)
- end
-
- content_type = resp.elements["report"].attributes["content_type"]
- report = resp.elements["report"].to_s
-
- if report == nil
- raise OMPError.new("The report is empty.")
- end
-
- # XML reports are in XML format, everything else is base64 encoded.
- if content_type == "text/xml"
- return report
- else
- return Base64.decode64(report)
- end
- end
-
- end
-end
diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec
index 52abbba707..462ec6a2e4 100644
--- a/metasploit-framework.gemspec
+++ b/metasploit-framework.gemspec
@@ -151,4 +151,6 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'tzinfo'
# Needed so that disk size output isn't horrible
spec.add_runtime_dependency 'filesize'
+ # Needed for openvas plugin
+ spec.add_runtime_dependency 'openvas-omp'
end
diff --git a/plugins/openvas.rb b/plugins/openvas.rb
index 86bf29b147..0cea636cac 100644
--- a/plugins/openvas.rb
+++ b/plugins/openvas.rb
@@ -10,7 +10,7 @@
# http://www.opensource.org/licenses/mit-license.php
#
-require 'openvas/openvas-omp'
+require 'openvas-omp'
module Msf
class Plugin::OpenVAS < Msf::Plugin
@@ -149,7 +149,7 @@ class Plugin::OpenVAS < Msf::Plugin
return unless openvas?
begin
- ver = @ov.get_version
+ ver = @ov.version_get
print_good("Using OMP version #{ver}")
rescue OpenVASOMP::OMPError => e
print_error(e.to_s)
@@ -189,7 +189,7 @@ class Plugin::OpenVAS < Msf::Plugin
begin
print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...")
- ov = OpenVASOMP::OpenVASOMP.new(user, pass, host, port)
+ ov = OpenVASOMP::OpenVASOMP.new('user' => user, 'password' => pass, 'host' => host, 'port' => port)
rescue OpenVASOMP::OMPAuthError => e
print_error("Authentication failed: #{e.reason}")
return
@@ -222,7 +222,7 @@ class Plugin::OpenVAS < Msf::Plugin
if args?(args, 3)
begin
- resp = @ov.target_create(args[0], args[1], args[2])
+ resp = @ov.target_create('name' => args[0], 'hosts' => args[1], 'comment' => args[2])
print_status(resp)
cmd_openvas_target_list
rescue OpenVASOMP::OMPError => e
@@ -279,7 +279,7 @@ class Plugin::OpenVAS < Msf::Plugin
if args?(args, 4)
begin
- resp = @ov.task_create(args[0], args[1], args[2], args[3])
+ resp = @ov.task_create('name' => args[0], 'comment' => args[1], 'config' => args[2], 'target'=> args[3])
print_status(resp)
cmd_openvas_task_list
rescue OpenVASOMP::OMPError => e
@@ -422,7 +422,7 @@ class Plugin::OpenVAS < Msf::Plugin
'Columns' => [ "ID", "Name" ])
id = 0
- @ov.configs.each do |config|
+ @ov.config_get_all.each do |config|
tbl << [ id, config["name"] ]
id += 1
end
@@ -445,7 +445,7 @@ class Plugin::OpenVAS < Msf::Plugin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Name", "Extension", "Summary"])
id = 0
- @ov.formats.each do |format|
+ format_get_all.each do |format|
tbl << [ id, format["name"], format["extension"], format["summary"] ]
id += 1
end
@@ -468,8 +468,23 @@ class Plugin::OpenVAS < Msf::Plugin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"])
id = 0
- @ov.report_get_all().each do |report|
- tbl << [ id, report["task"], report["start_time"], report["stop_time"] ]
+ resp = @ov.report_get_raw
+
+ resp.elements.each("//get_reports_response/report") do |report|
+ report_task = nil
+ report_start_time = nil
+ report_stop_time = nil
+
+ report.elements.each("//task/name") do |task_name|
+ report_task = task_name.get_text
+ end
+ report.elements.each("//creation_time") do |creation_time|
+ report_start_time = creation_time.get_text
+ end
+ report.elements.each("//modification_time") do |mod_time|
+ report_stop_time = mod_time.get_text
+ end
+ tbl << [ id, report_task, report_start_time, report_stop_time ]
id += 1
end
print_good("OpenVAS list of reports")
@@ -502,12 +517,14 @@ class Plugin::OpenVAS < Msf::Plugin
if args?(args, 4)
begin
- report = @ov.report_get_by_id(args[0], args[1])
+ report = @ov.report_get_raw("report_id"=>args[0],"format"=>args[1])
::FileUtils.mkdir_p(args[2])
name = ::File.join(args[2], args[3])
print_status("Saving report to #{name}")
output = ::File.new(name, "w")
- output.puts(report)
+ data = nil
+ report.elements.each("//get_reports_response"){|r| data = r.to_s}
+ output.puts(data)
output.close
rescue OpenVASOMP::OMPError => e
print_error(e.to_s)
@@ -522,9 +539,11 @@ class Plugin::OpenVAS < Msf::Plugin
if args?(args, 2)
begin
- report = @ov.report_get_by_id(args[0], args[1])
+ report = @ov.report_get_raw("report_id"=>args[0],"format"=>args[1])
+ data = nil
+ report.elements.each("//get_reports_response"){|r| data = r.to_s}
print_status("Importing report to database.")
- framework.db.import({:data => report})
+ framework.db.import({:data => data})
rescue OpenVASOMP::OMPError => e
print_error(e.to_s)
end
@@ -534,6 +553,33 @@ class Plugin::OpenVAS < Msf::Plugin
end
end
+
+
+ #--------------------------
+ # Format Functions
+ #--------------------------
+ # Get a list of report formats
+ def format_get_all
+ begin
+ resp = @ov.omp_request_xml("")
+ if @debug then print resp end
+
+ list = Array.new
+ resp.elements.each('//get_report_formats_response/report_format') do |report|
+ td = Hash.new
+ td["id"] = report.attributes["id"]
+ td["name"] = report.elements["name"].text
+ td["extension"] = report.elements["extension"].text
+ td["summary"] = report.elements["summary"].text
+ list.push td
+ end
+ @formats = list
+ return list
+ rescue
+ raise OMPResponseError
+ end
+ end
+
end # End OpenVAS class
#------------------------------
@@ -551,6 +597,7 @@ class Plugin::OpenVAS < Msf::Plugin
print_status
@ov = nil
@formats = nil
+ @debug = nil
end
def cleanup