1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-16 01:21:15 +02:00

Import the OpenVAS bridge from Kost. Go see his talk: http://www.berlinsides.org/node/14

git-svn-id: file:///home/svn/framework3/trunk@11428 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
HD Moore 2010-12-28 17:45:05 +00:00
parent b3bfb5834e
commit cd2088ee96
2 changed files with 1636 additions and 0 deletions

899
lib/openvas/openvas-omp.rb Normal file
View File

@ -0,0 +1,899 @@
#
# = openvas-omp.rb: communicate with OpenVAS manager over OMP
#
# Author:: Vlatko Kosturjak
#
# (C) Vlatko Kosturjak, Kost. Distributed under MIT license:
# http://www.opensource.org/licenses/mit-license.php
#
# == What is this library?
#
# This library is used for communication with OpenVAS manager over OMP
# You can start, stop, pause and resume scan. Watch progress and status of
# scan, download report, etc.
#
# == Requirements
#
# Required libraries are standard Ruby libraries: socket,timeout,openssl,
# rexml/document, rexml/text, base64
#
# == Usage:
#
# require 'openvas-omp'
require 'socket'
require 'timeout'
require 'openssl'
require 'rexml/document'
require 'rexml/text'
require 'base64'
# OpenVASOMP module
#
# Usage:
#
# require 'openvas-omp'
module OpenVASOMP
class OMPError < :: RuntimeError
attr_accessor :req, :reason
def initialize(req, reason = '')
self.req = req
self.reason = reason
end
def to_s
"OpenVAS OMP: #{self.reason}"
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
# Class which uses standard REXML to parse OpenVAS replies.
class OpenVASOMP
# initialize object: try to connect to OpenVAS using URL, user and password
#
# Usage:
#
# ov=OpenVASOMP.new(user=>'user',password=>'pass')
# # default: host=>'localhost', port=>'9390'
#
def initialize(p={})
if p.has_key?("host")
@host=p["host"]
else
@host="localhost"
end
if p.has_key?("port")
@port=p["port"]
else
@port=9390
end
if p.has_key?("user")
@user=p["user"]
else
@user="openvas"
end
if p.has_key?("password")
@password=p["password"]
else
@password="openvas"
end
if p.has_key?("bufsize")
@bufsize=p["bufsize"]
else
@bufsize=16384
end
if p.has_key?("debug")
@debug=p["debug"]
else
@debug=0
end
if @debug>3
puts "Host: "+@host
puts "Port: "+@port.to_s()
puts "User: "+@user
end
if @debug>99
puts "Password: "+@password
end
@areq=''
@read_timeout=3
if defined? p["noautoconnect"] and not p["noautoconnect"]
connect()
if defined? p["noautologin"] and not p["noautologin"]
login()
end
end
end
# Sets debug level
#
# Usage:
#
# ov.debug(3)
#
def debug (level)
@debug=level
end
# Low level method - Connect to SSL socket
#
# Usage:
#
# ov.connect()
#
def connect
@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
# Low level method - Disconnect SSL socket
#
# Usage:
#
# ov.disconnect()
#
def disconnect
if @socket
@socket.close
end
end
# Low level method: Send request and receive response - socket
#
# Usage:
#
# ov.connect();
# puts ov.sendrecv("<get_version/>")
# ov.disconnect();
#
def sendrecv (tosend)
if not @socket
connect
end
if @debug>3 then
puts "SENDING: "+tosend
end
@socket.puts(tosend)
@rbuf=''
size=0
begin
begin
timeout(@read_timeout) {
a = @socket.sysread(@bufsize)
size=a.length
# puts "sysread #{size} bytes"
@rbuf << a
}
rescue Timeout::Error
size=0
rescue EOFError
raise OMPResponseError
end
end while size>=@bufsize
response=@rbuf
if @debug>3 then
puts "RECEIVED: "+response
end
return response
end
# get OMP version (you don't need to be authenticated)
#
# Usage:
#
# ov.version_get()
#
def version_get
vreq="<get_version/>"
resp=sendrecv(vreq)
resp = "<X>"+resp+"</X>"
begin
docxml = REXML::Document.new(resp)
version=''
version=docxml.root.elements['get_version_response'].elements['version'].text
return version
rescue
raise XMLParsingError
end
end
# produce single XML element with attributes specified as hash
# low-level function
#
# Usage:
#
# ov.xml_attr()
#
def xml_attr(name, opts={})
xml = REXML::Element.new(name)
opts.keys.each do |k|
xml.attributes[k] = opts[k]
end
return xml
end
# produce multiple XML elements with text specified as hash
# low-level function
#
# Usage:
#
# ov.xml_ele()
#
def xml_ele(name, child={})
xml = REXML::Element.new(name)
child.keys.each do |k|
xml.add_element(k)
xml.elements[k].text = child[k]
end
return xml
end
# produce multiple XML elements with text specified as hash
# also produce multiple XML elements with attributes
# low-level function
#
# Usage:
#
# ov.xml_mix()
#
def xml_mix(name, child, attr, elem)
xml = REXML::Element.new(name)
child.keys.each do |k|
xml.add_element(k)
xml.elements[k].text = child[k]
end
elem.keys.each do |k|
xml.add_element(k)
xml.elements[k].attributes[attr] = elem[k]
end
return xml
end
# login to OpenVAS server.
# if successful returns authentication XML for further usage
# if unsuccessful returns empty string
#
# Usage:
#
# ov.login()
#
def login
areq="<authenticate>"+xml_ele("credentials", {"username"=>@user, "password"=>@password}).to_s()+"</authenticate>"
resp=sendrecv(areq+"<HELP/>")
# wrap it inside tags, so rexml does not complain
resp = "<X>"+resp+"</X>"
begin
docxml = REXML::Document.new(resp)
status=docxml.root.elements['authenticate_response'].attributes['status'].to_i()
rescue
raise XMLParsingError
end
if status == 200
@areq=areq
else
raise OMPAuthError
end
end
# check if we're successful logged in
# if successful returns true
# if unsuccessful returns false
#
# Usage:
#
# if ov.logged_in() then
# puts "logged in"
# end
#
def logged_in
if @areq == ''
return false
else
return true
end
end
# logout from OpenVAS server.
# it actually just sets internal authentication XML to empty str
# (as in OMP you have to send username/password each time)
# (i.e. there is no session id)
#
# Usage:
#
# ov.logout()
#
def logout
disconnect()
@areq = ''
end
# OMP low level method - Send string request wrapped with
# authentication XML and return response as string
#
# Usage:
#
# ov.request_xml("<HELP/")
#
def omp_request_raw (request)
resp=sendrecv(@areq+request)
return resp
end
# OMP low level method - Send string request wrapped with
# authentication XML and return REXML parsed object
#
# Usage:
#
# rexmlobject = ov.request_xml("<HELP/")
#
def omp_request_xml (request)
resp=sendrecv(@areq+request)
resp = "<X>"+resp+"</X>"
begin
docxml = REXML::Document.new(resp)
status=docxml.root.elements['authenticate_response'].attributes['status'].to_i
if status<200 and status>299
raise OMPAuthError
end
return docxml.root
rescue
raise XMLParsingError
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 (p={})
xmlreq=xml_ele("create_target", p).to_s()
begin
xr=omp_request_xml(xmlreq)
id=xr.elements['create_target_response'].attributes['id']
rescue
raise OMPResponseError
end
return id
end
# OMP - Delete target
#
# Usage:
#
# ov.target_delete(target_id)
#
def target_delete (id)
xmlreq=xml_attr("delete_task",{"target_id" => id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - Get target for scanning and returns rexml object
#
# Usage:
# rexmlobject = target_get_raw("target_id"=>target_id)
#
def target_get_raw (p={})
xmlreq=xml_attr("get_targets", p).to_s()
begin
xr=omp_request_xml(xmlreq)
return xr
rescue
raise OMPResponseError
end
end
# 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 (p={})
begin
xr=target_get_raw(p)
list=Array.new
xr.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
return list
rescue
raise OMPResponseError
end
end
def target_get_byid (id)
begin
xr=target_get_raw("target_id"=>id)
xr.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
return td
end
return list
rescue
raise OMPResponseError
end
end
# OMP - get reports and returns raw rexml object as response
#
# Usage:
#
# rexmlobject=ov.report_get_raw("format"=>"PDF")
#
# rexmlobject=ov.report_get_raw(
# "report_id" => "",
# "format"=>"PDF")
#
def report_get_raw (p={})
xmlreq=xml_attr("get_reports",p).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - get report by id and format, returns report
# (already base64 decoded if needed)
#
# format can be: HTML, NBE, PDF, ...
#
# Usage:
#
# pdf_content=ov.report_get_byid(id,"PDF")
# File.open('report.pdf', 'w') {|f| f.write(pdf_content) }
#
def report_get_byid (id,format)
decode=Array["HTML","NBE","PDF"]
xr=report_get_raw("report_id"=>id,"format"=>format)
resp=xr.elements['get_reports_response'].elements['report'].text
if decode.include?(format)
resp=Base64.decode64(resp)
end
return resp
end
# OMP - get report all, returns report
#
# Usage:
#
# pdf_content=ov.report_get_all()
#
def report_get_all ()
begin
xr=report_get_raw("format"=>"NBE")
list=Array.new
xr.elements.each('//get_reports_response/report') do |report|
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
return list
rescue
raise OMPResponseError
end
end
# OMP - get reports and returns raw rexml object as response
#
# Usage:
#
# rexmlobject=ov.result_get_raw("notes"=>0)
#
def result_get_raw (p={})
begin
xmlreq=xml_attr("get_results",p).to_s()
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - get configs and returns rexml object as response
#
# Usage:
#
# rexmldocument=ov.config_get_raw()
#
def config_get_raw (p={})
xmlreq=xml_attr("get_configs",p).to_s()
begin
xr=omp_request_xml(xmlreq)
return xr
rescue
raise OMPResponseError
end
return false
end
# 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 (p={})
begin
xr=config_get_raw(p)
tc=Array.new
xr.elements.each('//get_configs_response/config') do |config|
c=Hash.new
c["id"]=config.attributes["id"]
c["name"]=config.elements["name"].text
c["comment"]=config.elements["comment"].text
tc.push c
end
return tc
rescue
raise OMPResponseError
end
return false
end
# OMP - get configs and returns hash as response
# hash[config_id]=config_name
#
# Usage:
#
# all_configs_hash=ov.config.get()
#
# config_id=ov.config_get().index("Full and fast")
#
def config_get (p={})
begin
xr=config_get_raw(p)
list=Hash.new
xr.elements.each('//get_configs_response/config') do |config|
id=config.attributes["id"]
name=config.elements["name"].text
list[id]=name
end
return list
rescue
raise OMPResponseError
end
return false
end
# OMP - copy config with new name and returns new id
#
# Usage:
#
# new_config_id=config_copy(config_id,"new_name");
#
def config_copy (config_id,name)
xmlreq=xml_attr("create_config",
{"copy"=>config_id,"name"=>name}).to_s()
begin
xr=omp_request_xml(xmlreq)
id=xr.elements['create_config_response'].attributes['id']
return id
rescue
raise OMPResponseError
end
end
# OMP - create config with specified RC file and returns new id
# name = name of new config
# rcfile = base64 encoded OpenVAS rcfile
#
# Usage:
#
# config_id=config_create("name",rcfile);
#
def config_create (name,rcfile)
xmlreq=xml_attr("create_config",
{"name"=>name,"rcfile"=>rcfile}).to_s()
begin
xr=omp_request_xml(xmlreq)
id=xr.elements['create_config_response'].attributes['id']
return id
rescue
raise OMPResponseError
end
end
# OMP - creates task and returns id of created task
#
# Parameters which usually fit in p hash and i hash:
# p = name,comment,rcfile
# i = config,target,escalator,schedule
#
# Usage:
#
# task_id=ov.task_create_raw()
#
def task_create_raw (p={}, i={})
xmlreq=xml_mix("create_task",p,"id",i).to_s()
begin
xr=omp_request_xml(xmlreq)
id=xr.elements['create_task_response'].attributes['id']
return id
rescue
raise OMPResponseError
end
end
# OMP - creates task and returns id of created task
#
# parameters = name,comment,rcfile,config,target,escalator,
# schedule
#
# Usage:
#
# config_id=o.config_get().index("Full and fast")
# target_id=o.target_create(
# {"name"=>"localtarget", "hosts"=>"127.0.0.1", "comment"=>"t"})
# task_id=ov.task_create(
# {"name"=>"testlocal","comment"=>"test", "target"=>target_id,
# "config"=>config_id}
#
def task_create (p={})
specials=Array["config","target","escalator","schedule"]
ids = Hash.new
specials.each do |spec|
if p.has_key?(spec)
ids[spec]=p[spec]
p.delete(spec)
end
end
return task_create_raw(p,ids)
end
# OMP - deletes task specified by task_id
#
# Usage:
#
# ov.task_delete(task_id)
#
def task_delete (task_id)
xmlreq=xml_attr("delete_task",{"task_id" => task_id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - get task and returns raw rexml object as response
#
# Usage:
#
# rexmlobject=ov.task_get_raw("details"=>"0")
#
def task_get_raw (p={})
xmlreq=xml_attr("get_tasks",p).to_s()
begin
xr=omp_request_xml(xmlreq)
return xr
rescue
raise OMPResponseError
end
end
# OMP - get all tasks and returns array with hashes with
# following content:
# id,name,comment,status,progress,first_report,last_report
#
# Usage:
#
# array_of_hashes=ov.task_get_all()
#
def task_get_all (p={})
xr=task_get_raw(p)
t=Array.new
xr.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
if defined? task.elements["first_report"].elements["report"].attributes["id"] then
td["firstreport"]=task.elements["first_report"].elements["report"].attributes["id"]
else
td["firstreport"]=nil
end
if defined? task.elements["last_report"].elements["report"].attributes["id"] then
td["lastreport"]=task.elements["last_report"].elements["report"].attributes["id"]
else
td["lastreport"]=nil
end
t.push td
end
return t
end
# OMP - get task specified by task_id and returns hash with
# following content:
# id,name,comment,status,progress,first_report,last_report
#
# Usage:
#
# hash=ov.task_get_byid(task_id)
#
def task_get_byid (id)
xr=task_get_raw("task_id"=>id,"details"=>0)
xr.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
if defined? task.elements["first_report"].elements["report"].attributes["id"] then
td["firstreport"]=task.elements["first_report"].elements["report"].attributes["id"]
else
td["firstreport"]=nil
end
if defined? task.elements["last_report"].elements["report"].attributes["id"] then
td["lastreport"]=task.elements["last_report"].elements["report"].attributes["id"]
else
td["lastreport"]=nil
end
return (td)
end
end
# OMP - check if task specified by task_id is finished
# (it checks if task status is "Done" in OMP)
#
# Usage:
#
# if ov.task_finished(task_id)
# puts "Task finished"
# end
#
def task_finished (id)
xr=task_get_raw("task_id"=>id,"details"=>0)
xr.elements.each('//get_tasks_response/task') do |task|
if status=task.elements["status"].text == "Done"
return true
else
return false
end
end
end
# OMP - check progress of task specified by task_id
# (OMP returns -1 if task is finished, not started, etc)
#
# Usage:
#
# print "Progress: "
# puts ov.task_progress(task_id)
#
def task_progress (id)
xr=task_get_raw("task_id"=>id,"details"=>0)
xr.elements.each('//get_tasks_response/task') do |task|
return task.elements["progress"].text.to_i()
end
end
# OMP - starts task specified by task_id
#
# Usage:
#
# ov.task_start(task_id)
#
def task_start (task_id)
xmlreq=xml_attr("start_task",{"task_id" => task_id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - stops task specified by task_id
#
# Usage:
#
# ov.task_stop(task_id)
#
def task_stop (task_id)
xmlreq=xml_attr("stop_task",{"task_id" => task_id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - pauses task specified by task_id
#
# Usage:
#
# ov.task_pause(task_id)
#
def task_pause (task_id)
xmlreq=xml_attr("pause_task",{"task_id" => task_id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
# OMP - resumes (or starts) task specified by task_id
#
# Usage:
#
# ov.task_resume_or_start(task_id)
#
def task_resume_or_start (task_id)
xmlreq=xml_attr("resume_or_start_task",{"task_id" => task_id}).to_s()
begin
xr=omp_request_xml(xmlreq)
rescue
raise OMPResponseError
end
return xr
end
end # end of Class
end # of Module

737
plugins/openvas.rb Normal file
View File

@ -0,0 +1,737 @@
#!/usr/bin/env ruby
#
# This plugin provides integration with OpenVAS. Written by kost.
# Distributed under MIT license:
# http://www.opensource.org/licenses/mit-license.php
#
# Typical usage:
# load openvas
# db_create
# openvas_connect test test localhost 9390 ok
# openvas_scan localhost
require 'openvas/openvas-omp'
module Msf
class Plugin::OpenVAS < Msf::Plugin
class OpenVASCommandDispatcher
include Msf::Ui::Console::CommandDispatcher
def name
"OpenVAS"
end
def commands
{
'openvas_help' => "Displays help",
'openvas_connect' => "Connect to a OpenVAS manager using OMP ( user:pass@host[:port] )",
'openvas_task_list' => "Display list of tasks",
'openvas_task_create' => "Creates task (name, comment, target, config)",
'openvas_task_start' => "Start task by ID",
'openvas_task_stop' => "Stop task by ID",
'openvas_task_pause' => "Pause task by ID",
'openvas_task_resume' => "Resume task by ID",
'openvas_task_resume_or_start' => "Resume task or start task by ID",
'openvas_task_status' => "Get status of task ID",
'openvas_task_delete' => "Delete task by ID",
'openvas_target_create' => "Create target (name, hosts, comment)",
'openvas_target_list' => "Display list of targets",
'openvas_target_delete' => "Delete target by ID",
'openvas_config_list' => "Quickly display list of configs",
'openvas_report_import' => "Import report specified by ID to framework",
'openvas_report_save' => "Save report specified by ID and format to file",
'openvas_scan' => "Launch an automatic OpenVAS scan against a specific IP range and import the results",
'openvas_debug' => "Sets debug level",
'openvas_disconnect' => "Disconnect from OpenVAS manager",
}
end
def cmd_openvas_help (*args)
usage="
openvas_help Display this help
CONNECTION
==========
openvas_connect Connects to OpenVAS
openvas_disconnect Disconnects from OpenVAS
TARGETS
=======
openvas_target_list Lists targets
openvas_target_create Create target
openvas_target_delete Deletes target specified by ID
TASKS
=====
openvas_task_list Lists tasks
openvas_task_create Create task
openvas_task_start Starts task specified by ID
openvas_task_stop Stops task specified by ID
openvas_task_pause Pauses task specified by ID
openvas_task_resume Resumes task specified by ID
openvas_task_resume_or_start Resumes or starts task specified by ID
CONFIGS
=======
openvas_config_list Lists configs
REPORTS
=======
openvas_report_import Imports OpenVAS report in framework spec. by ID
openvas_report_save Saves OpenVAS report specified by ID and format
AUTO
====
openvas_scan Launch an automatic OpenVAS scan against a specific IP range and import the results automatically with optional autopwn
"
print_status(usage)
end
def openvas_verify
if ! @ov
print_error("No active OpenVAS instance has been configured, please use 'openvas_connect'")
return false
end
if ! (framework.db and framework.db.usable)
print_error("No database has been configured, please use db_create/db_connect first")
return false
end
true
end
def cmd_openvas_debug(*args)
usagecmd="openvas_debug <level>
Example: openvas_debug 99"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
level=args[0]
@ov.debug(level)
print_good("Command completed successfuly: "+level)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_start(*args)
usagecmd="openvas_task_start <id>
Example: openvas_task_start 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_start(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_create(*args)
usagecmd="openvas_task_create <name> <comment> <config_id> <target_id>
Example: openvas_task_create newtask MyNewTask abc12345-a234-46a1-c01c-123456789012 9abc0790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length != 4 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=@ov.task_create({"name"=>args[0],"comment"=>arg[1],"config"=>args[2],"target"=>args[3]})
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_stop(*args)
usagecmd="openvas_task_stop <id>
Example: openvas_task_stop 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_stop(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_pause(*args)
usagecmd="openvas_task_pause <id>
Example: openvas_task_pause 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_pause(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_resume(*args)
usagecmd="openvas_task_resume <id>
Example: openvas_task_resume 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_resume(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_resume_or_start(*args)
usagecmd="openvas_task_resume_or_start <id>
Example: openvas_task_resume_or_start 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_resume_or_start(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_delete(*args)
usagecmd="openvas_task_delete <id>
Example: openvas_task_delete 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.task_delete(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_status(*args)
usagecmd="openvas_task_status <id>
Example: openvas_task_status 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if (args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[ "ID", "Name", "Status", "Progress", "Comment","First Report","Last Report" ]
)
@ov.task_get_all("task_id"=>id).each do |task|
tbl << [ task["id"] , task["name"] ,
task["status"],
task["progress"],
task["comment"],
task["firstreport"],
task["lastreport"]
]
end
print_good("OpenVAS task status")
puts "\n"
puts tbl.to_s + "\n"
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_task_list(*args)
usagecmd="openvas_task_list [id]
Example: openvas_task_list
Example: openvas_task_list 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if (args[0] == "-h") then
print_status(usagecmd)
return
end
if not (args.length == 0 or args[0].empty?) then
id=args[0]
end
begin
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[ "ID", "Name", "Status", "Progress" ]
)
p={}
if id
p={"task_id"=>id}
end
@ov.task_get_all(p).each do |task|
tbl << [ task["id"] , task["name"] ,
task["status"] , task["progress"] ]
end
print_good("OpenVAS list of tasks")
puts "\n"
puts tbl.to_s + "\n"
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_target_list(*args)
usagecmd="openvas_target_list [id]
Example: openvas_target_list
Example: openvas_target_list 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if (args[0] == "-h") then
print_status(usagecmd)
return
end
if not (args.length == 0 or args[0].empty?) then
id=args[0]
end
begin
tbl = Rex::Ui::Text::Table.new(
'Columns' => [ "ID","Name","Hosts" ])
p={}
if id
p={"target_id"=>id}
end
@ov.target_get_all(p).each do |target|
tbl << [target["id"],
target["name"],
target["hosts"] ]
end
print_good("OpenVAS list of targets")
puts "\n"
puts tbl.to_s + "\n"
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_target_create(*args)
usagecmd="openvas_target_create <name> <hosts> <comment>
Example: openvas_target_create mylocalhost 127.0.0.1 MyLocalHostComment"
return if not openvas_verify
if(args.length != 3 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=@ov.target_create({"name"=>args[0],"comment"=>arg[2],"hosts"=>args[1]})
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_target_delete(*args)
usagecmd="openvas_target_delete <id>
Example: openvas_target_delete 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
@ov.target_delete(id)
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_connect(*args)
usagecmd = "Usage:
openvas_connect username:password@host[:port] <ssl-confirm>
-OR-
openvas_connect username password host port <ssl-confirm>"
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
user = pass = host = port = sslv = nil
case args.length
when 1,2
cred,targ = args[0].split('@', 2)
user,pass = cred.split(':', 2)
targ ||= '127.0.0.1:9390'
host,port = targ.split(':', 2)
port ||= '9390'
sslv = args[1]
when 4,5
user,pass,host,port,sslv = args
else
print_status(usagecmd)
return
end
if ! ((user and user.length > 0) and (host and host.length > 0) and (port and port.length > 0 and port.to_i > 0) and (pass and pass.length > 0))
print_status(usagecmd)
return
end
# taken from NeXpose WARNING
if(host != "localhost" and host != "127.0.0.1" and sslv != "ok")
print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
print_error(" with the ability to man-in-the-middle the OpenVAS traffic to capture the OpenVAS")
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
print_error(" as an additional parameter to this command.")
return
end
# Wrap this so a duplicate session doesnt prevent a new login
begin
cmd_openvas_disconnect
rescue ::Interrupt
raise $!
rescue ::Exception
end
begin
print_status("Connecting to OpenVAS instance at #{host}:#{port} with username #{user}...")
ov = OpenVASOMP::OpenVASOMP.new("user"=>user,"password"=>pass,"host"=>host,"port"=>port)
rescue OpenVASOMP::OMPAuthError => e
print_error("Connection failed: #{e.reason}")
return
end
@ov = ov
end
def cmd_openvas_config_list(*args)
usagecmd="openvas_config_list [id]
Example: openvas_config_list
Example: openvas_config_list 9fd90790-a79b-49e0-b08e-6912afde72f4"
return if not openvas_verify
if (args[0] == "-h") then
print_status(usagecmd)
return
end
if not (args.length == 0 or args[0].empty?) then
id=args[0]
end
begin
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[ "ID", "Name", "Comments" ]
)
p={}
if id
p={"config_id"=>id}
end
@ov.config_get_all(p).each do |config|
tbl << [ config["id"], config["name"],
config["comment"] ]
end
print_good("OpenVAS list of configs")
puts "\n"
puts tbl.to_s + "\n"
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_report_import(*args)
usagecmd="openvas_report_import <id>
Example: openvas_report_import 9fd90790-a79b-49e0-b08e-6912afde72f4
Note: gets NBE report from the OpenVAS and tries to import it into framework
"
return if not openvas_verify
if(args.length == 0 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
content=@ov.report_get_byid(id,'NBE')
framework.db.import({:data => content})
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_report_save(*args)
usagecmd="openvas_report_save <id> <format> <file>
Example: openvas_report_save 9fd90790-a79b-49e0-b08e-6912afde72f4 PDF /tmp/a.pdf"
return if not openvas_verify
if(args.length != 3 or args[0].empty? or args[0] == "-h")
print_status(usagecmd)
return
end
begin
id=args[0]
content=@ov.report_get_byid(id,args[1])
File.open(args[2], 'w') {|f| f.write(content) }
print_good("Command completed successfuly: "+id)
rescue ::Exception
print_error("Error executing")
end
end
def cmd_openvas_scan(*args)
opts = Rex::Parser::Arguments.new(
"-h" => [ false, "This help menu"],
"-t" => [ true, "The scan template to use (default:Full and fast )"],
"-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"],
"-x" => [ false, "Automatically launch all exploits by matching reference after the scan completes (unsafe)"],
"-X" => [ false, "Automatically launch all exploits by matching reference and port after the scan completes (unsafe)"],
"-d" => [ false, "Scan hosts based on the contents of the existing database"],
"-v" => [ false, "Display diagnostic information about the scanning process"],
"-I" => [ true, "Only scan systems with an address within the specified range"],
"-E" => [ true, "Exclude hosts in the specified range from the scan"],
"-R" => [ true, "Specify a minimum exploit rank to use for automated exploitation"]
)
opt_template = "Full and fast"
opt_verbose = false
opt_preserve = false
opt_autopwn = false
opt_rescandb = false
opt_addrinc = nil
opt_addrexc = nil
opt_scanned = []
opt_minrank = "manual"
opt_ranges = []
opts.parse(args) do |opt, idx, val|
case opt
when "-h"
print_line("Usage: openvas_scan [options] <Target IP Ranges>")
print_line(opts.usage)
return
when "-v"
opt_verbose = true
when "-t"
opt_template = val
when "-P"
opt_preserve = true
when "-X"
opt_autopwn = "-p -x"
when "-x"
opt_autopwn = "-x" unless opt_autopwn
when "-d"
opt_rescandb = true
when '-I'
opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
when '-E'
opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
else
opt_ranges << val
end
end
return if not openvas_verify
# Include all database hosts as scan targets if specified
if(opt_rescandb)
print_status("Loading scan targets from the active database...") if opt_verbose
framework.db.hosts.each do |host|
next if host.state != ::Msf::HostState::Alive
opt_ranges << host.address
end
end
opt_ranges = opt_ranges.join(',')
if(opt_ranges.strip.empty?)
print_line("Usage: openvas_scan [options] <Target IP Ranges>")
print_line(opts.usage)
return
end
if(opt_verbose)
print_status("Creating a new scan using config #{opt_template} against #{opt_ranges}")
end
range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges)
range = ::Rex::Socket::RangeWalker.new(range_inp)
include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil
exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil
completed = 0
total = range.num_ips
count = 0
print_status("Scanning #{total} addresses with config #{opt_template}")
while(completed < total)
count += 1
queue = []
while(ip = range.next_ip )
if(exclude_range and exclude_range.include?(ip))
print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose
next
end
if(include_range and ! include_range.include?(ip))
print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose
next
end
opt_scanned << ip
queue << ip
end
break if queue.empty?
print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose
msfid = Time.now.to_i
ipstr=''
queue.each do |ip|
ipstr=ipstr+","+ip
end
# Create a temporary site
mname="Metasploit-#{msfid}"
mcomment="Autocreated by the Metasploit Framework"
mtarget=@ov.target_create({"name"=>mname, "hosts"=>ipstr, "comment"=>mcomment})
print_status(" >> Created temporary target #{mname} with id #{mtarget}") if opt_verbose
mconfig=@ov.config_get().index(opt_template)
if mconfig
print_status(" >> Found config #{opt_template} with id #{mconfig}") if opt_verbose
else
print_error("Config not found")
break
end
# Create temporary task
mtask = @ov.task_create({"name"=>mname,"comment"=>mcomment, "target"=>mtarget, "config"=>mconfig})
if mtask
print_status(" >> Created task #{mname} with id #{mtask}") if opt_verbose
else
print_error("Task could not be created")
break
end
@ov.task_start(mtask)
print_status(" >> Scan has been launched with ID #{mtask}") if opt_verbose
rep = true
begin
prev = nil
while(true)
stat = @ov.task_get_byid(mtask)
break if stat["status"] == "Done"
percent=stat["progress"]
stat = "Progress: #{percent} %"
if(stat != prev)
print_status(" >> #{stat}") if opt_verbose
end
prev = stat
select(nil, nil, nil, 5.0)
end
print_status(" >> Scan has been completed with task ID #{mtask}") if opt_verbose
rescue ::Interrupt
rep = false
print_status(" >> Terminating task ID #{mtask} due to console interupt") if opt_verbose
@ov.task_stop(mtask)
break
end
# Wait for the automatic report generation to complete
if(rep)
print_status(" >> Getting report...") if opt_verbose
stat = @ov.task_get_byid(mtask)
if stat["status"] != "Done"
content=@ov.report_get_byid(stat["lastreport"],'NBE')
print_status(" >> Importing the report data from OpenVAS...") if opt_verbose
framework.db.import({:data => content})
end
if ! opt_preserve
print_status(" >> Deleting the temporary task and target...") if opt_verbose
@ov.task_delete(mtask)
@ov.target_delete(mtarget)
end
end
print_status("Completed the scan of #{total} addresses")
if(opt_autopwn)
print_status("Launching an automated exploitation session")
driver.run_single("db_autopwn -q -r -e -t #{opt_autopwn} -R #{opt_minrank} -I #{opt_scanned.join(",")}")
end
end
end
def cmd_openvas_disconnect(*args)
@ov.logout if @ov
@ov = nil
end
end
#
# Plugin initialization
#
def initialize(framework, opts)
super
add_console_dispatcher(OpenVASCommandDispatcher)
print "Welcome to OpenVAS integration by kost\n\n"
print "Use openvas_help for list of commands you can use.\n"
print "Note that you should have database ready. After that you should connect with:\n"
print "openvas_connect (try with -h for help on connecting)\n"
print_status("OpenVAS integration has been activated")
end
def cleanup
remove_console_dispatcher('OpenVAS')
end
def name
"OpenVAS"
end
def desc
"Integrates with the OpenVAS - open source vulnerability management"
end
end
end