我下载了一个 ruby 脚本,它连接到 API,可以一次性下载所有报告。但是,当我下载这些报告时,在实际文件名中包含开始时间和结束时间对我来说至关重要。我能够修改我的脚本以删除它自动附加到文件名的 6 个垃圾字符,并且为了进行测试,我可以在 csv 扩展名之前添加“2x”(我打算只下载 csv 格式)。
问题是 Nessus API 没有提供能够提取此开始和结束时间的变量。我唯一的其他选择是从网站本身获取它。实际报告位于https://nessus-scanner-ip/#/scans/<id>/hosts
。当作为内容访问该链接时,会列出开始时间和结束时间。如何提取这个时间并将其附加到军事时间的报告文件名中?
谢谢你的帮助。
#!/usr/bin/env ruby
#################################################################################################
# Name: Nessus 6 Report Downloader
# Author: Travis Lee
#
# Version: 1.0
# Last Updated: 2/28/2016
#
# Description: Interactive script that connects to a specified Nessus 6 server using the
# Nessus REST API to automate mass report downloads. It has the ability to download
# multiple or all reports/file types/chapters and save them to a folder of
# your choosing. This has been tested with Nessus 6.5.5 and *should* work with
# Nessus 6+, YMMV.
#
# File types include: NESSUS, HTML, PDF, CSV, and DB.
#
# Chapter types include: Vulnerabilities By Plugin, Vulnerabilities By Host,
# Hosts Summary (Executive), Suggested Remediations, Compliance Check (Executive),
# and Compliance Check.
#
# Usage: ruby ./nessus6-report-downloader.rb
#
# Reference: https://<nessus-server>:8834/api
#
#################################################################################################
require 'net/http'
require 'fileutils'
require 'io/console'
require 'date'
require 'json'
require 'openssl'
# This method will download the specified file type from specified reports
def report_download(http, headers, reports, reports_to_dl, filetypes_to_dl, chapters_to_dl, rpath, db_export_pw)
begin
puts "\nDownloading report(s). Please wait..."
# if all reports are selected
if reports_to_dl[0].eql?("all")
reports_to_dl.clear
# re-init array with all the scan ids
reports["scans"].each do |scan|
reports_to_dl.push(scan["id"].to_s)
end
end
# iterate through all the indexes and download the reports
reports_to_dl.each do |rep|
rep = rep.strip
filetypes_to_dl.each do |ft|
# export report
puts "\n[+] Exporting scan report, scan id: " + rep + ", type: " + ft
path = "/scans/" + rep + "/export"
data = {'format' => ft, 'chapters' => chapters_to_dl, 'password' => db_export_pw}
resp = http.post(path, data.to_json, headers)
fileid = JSON.parse(resp.body)
# check export status
status_path = "/scans/" + rep + "/export/" + fileid["file"].to_s + "/status"
loop do
sleep(5)
puts "[+] Checking export status..."
status_resp = http.get(status_path, headers)
status_result = JSON.parse(status_resp.body)
break if status_result["status"] == "ready"
puts "[-] Export not ready yet, checking again in 5 secs."
end
# download report
puts "[+] Report ready for download..."
dl_path = "/scans/" + rep + "/export/" + fileid["file"].to_s + "/download"
dl_resp = http.get(dl_path, headers)
# create final path/filename and write to file
fname_temp = dl_resp.response["Content-Disposition"].split('"')
fname = "#{rpath}/#{fname_temp[1]}"
# save ext, then remove last 10 chars (6 garbage plus ext; we'll add ext next
ext = File.extname(fname)
ff = fname[0..-11]
f = ff + ext
# append start and end time to filename
f2x = f.gsub(ext, "2x"+ext)
# write file
open(f2x, 'w') { |f|
f.puts dl_resp.body
}
puts "[+] Downloading report to: #{fname}"
#puts reports.scan["starttime"]
#reports["scans"].each do |scan|
# printf("%s\n", scan["starttime"])
#end
end
end
rescue StandardError => download_report_error
puts "\n\nError downloading report: #{download_report_error}\n\n"
exit
end
end
# This method will return a list of all the reports on the server
def get_report_list(http, headers)
begin
# Try and do stuff
path = "/scans"
resp = http.get(path, headers)
#puts "Number of reports found: #{reports.count}\n\n"
results = JSON.parse(resp.body)
printf("%-7s %-50s %-30s %-15s %-15s %-3s\n", "Scan ID", "Name", "Last Modified", "Status", "Start", "End")
printf("%-7s %-50s %-30s %-15s %-15s %-3s\n", "-------", "----", "-------------", "------", "-----", "---")
# print out all the reports
results["scans"].each do |scan|
#d = DateTime.parse(scan["starttime"])
#year, month, day, t, hour, min, sec = scan["starttime"].unpack("A4A2A2c1A2A2A2")
printf("%-7s %-50s %-30s %-15s %-15s %-3s\n", scan["id"], scan["name"], DateTime.strptime(scan["last_modification_date"].to_s,'%s').strftime('%b %e, %Y %H:%M %Z'), scan["status"], scan["starttime"], scan["not-being-used"])
# printf("%-7s %-50s %-30s %-15s %-5s %-3s\n", scan["id"], scan["name"], DateTime.strptime(scan["last_modification_date"].to_s,'%s').strftime('%b %e, %Y %H:%M %Z'), scan["status"], DateTime.parse(scan["starttime"].to_s,'%s').strftime('%2H:%2M:%2s'), scan["starttime"])
end
return results
rescue StandardError => get_scanlist_error
puts "\n\nError getting scan list: #{get_scanlist_error}\n\n"
exit
end
end
# This method will make the initial login request and set the token value to use for subsequent requests
def get_token(http, username, password)
begin
path = "/session"
data = {'username' => username, 'password' => password}
resp = http.post(path, data.to_json, 'Content-Type' => 'application/json')
token = JSON.parse(resp.body)
headers = {
"User-Agent" => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0',
"X-Cookie" => 'token=' + token["token"],
"Accept" => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
"Accept-Language" => 'en-us,en;q=0.5',
"Accept-Encoding" => 'text/html;charset=UTF-8',
"Cache-Control" => 'max-age=0',
"Content-Type" => 'application/json'
}
return headers
rescue StandardError => get_token_error
puts "\n\nError logging in/getting token: #{get_token_error}\n\n"
exit
end
end
### MAIN ###
puts "\nNessus 6 Report Downloader 1.0"
# Collect server info
print "\nEnter the Nessus Server IP: "
nserver = gets.chomp.to_s
print "Enter the Nessus Server Port [8834]: "
nserverport = gets.chomp.to_s
if nserverport.eql?("")
nserverport = "8834"
end
# https object
http = Net::HTTP.new(nserver, nserverport)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
# Collect user/pass info
print "Enter your Nessus Username: "
username = gets.chomp.to_s
print "Enter your Nessus Password (will not echo): "
password = STDIN.noecho(&:gets).chomp.to_s
# login and get token cookie
headers = get_token(http, username, password)
# get list of reports
puts "\n\nGetting report list..."
reports = get_report_list(http, headers)
print "Enter the report(s) your want to download (comma separate list) or 'all': "
reports_to_dl = (gets.chomp.to_s).split(",")
if reports_to_dl.count == 0
puts "\nError! You need to choose at least one report!\n\n"
exit
end
# select file types to download
puts "\nChoose File Type(s) to Download: "
puts "[0] Nessus (No chapter selection)"
puts "[1] HTML"
puts "[2] PDF"
puts "[3] CSV (No chapter selection)"
puts "[4] DB (No chapter selection)"
print "Enter the file type(s) you want to download (comma separate list) or 'all': "
filetypes_to_dl = (gets.chomp.to_s).split(",")
if filetypes_to_dl.count == 0
puts "\nError! You need to choose at least one file type!\n\n"
exit
end
# see which file types to download
formats = []
cSelect = false
dbSelect = false
filetypes_to_dl.each do |ft|
case ft.strip
when "all"
formats.push("nessus")
formats.push("html")
formats.push("pdf")
formats.push("csv")
formats.push("db")
cSelect = true
dbSelect = true
when "0"
formats.push("nessus")
when "1"
formats.push("html")
cSelect = true
when "2"
formats.push("pdf")
cSelect = true
when "3"
formats.push("csv")
when "4"
formats.push("db")
dbSelect = true
end
end
# enter password used to encrypt db exports (required)
db_export_pw = ""
if dbSelect
print "\nEnter a Password to encrypt the DB export (will not echo): "
db_export_pw = STDIN.noecho(&:gets).chomp.to_s
print "\n"
end
# select chapters to include, only show if html or pdf is in file type selection
chapters = ""
if cSelect
puts "\nChoose Chapter(s) to Include: "
puts "[0] Vulnerabilities By Plugin"
puts "[1] Vulnerabilities By Host"
puts "[2] Hosts Summary (Executive)"
puts "[3] Suggested Remediations"
puts "[4] Compliance Check (Executive)"
puts "[5] Compliance Check"
print "Enter the chapter(s) you want to include (comma separate list) or 'all': "
chapters_to_dl = (gets.chomp.to_s).split(",")
if chapters_to_dl.count == 0
puts "\nError! You need to choose at least one chapter!\n\n"
exit
end
# see which chapters to download
chapters_to_dl.each do |chap|
case chap.strip
when "all"
chapters << "vuln_hosts_summary;vuln_by_plugin;vuln_by_host;remediations;compliance_exec;compliance;"
when "0"
chapters << "vuln_by_plugin;"
when "1"
chapters << "vuln_by_host;"
when "2"
chapters << "vuln_hosts_summary;"
when "3"
chapters << "remediations;"
when "4"
chapters << "compliance_exec;"
when "5"
chapters << "compliance;"
end
end
end
# create report folder
print "\nPath to save reports to (without trailing slash): "
rpath = gets.chomp.to_s
unless File.directory?(rpath)
FileUtils.mkdir_p(rpath)
end
# run report download
if formats.count > 0
report_download(http, headers, reports, reports_to_dl, formats, chapters, rpath, db_export_pw)
end
puts "\nReport Download Completed!\n\n"