0

我下载了一个 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"
4

1 回答 1

0

Nessus API ( https://Nessus_scanner:8834/api#/resources/scans/details )的“详细信息”方法将返回给定扫描的开始和结束 Epoch 时间戳:

  • “scan_start”:{字符串}
  • “scan_end”:{字符串}

然后你可以使用:

Time.at(xxxxxxxxxx).strftime("%H%M")

其中“ xxxxxxxxxx ”是 10 位 Epoch 时间戳,用于获取军用格式 (HHMM)。

于 2016-11-28T21:17:22.200 回答