module PWN::Plugins::IBMAppscan

def self.appscan_rest_call(opts = {})

def self.appscan_rest_call(opts = {})
:appscan_obj]
ts[:http_method].nil?
t
s[:http_method].to_s.scrub.to_sym
est_call].to_s.scrub
ttp_body].to_s.scrub
n_obj[:appscan_ip].to_s.scrub
pscan_obj[:cookie]
i = "https://#{appscan_ip}/ase/services".to_s.scrub
Plugins::TransparentBrowser.open(browser_type: :rest)
er_obj[:browser]::Request
lient.execute(
pscan_api_uri}/#{rest_call}",
ie: appscan_cookie },
se
lient.execute(
pscan_api_uri}/#{rest_call}",
ie: appscan_cookie },
ody,
se
rror("Unsupported HTTP Method #{http_method} for #{self} Plugin")
=> e
01 Unauthorized') && retry_count.positive? && appscan_obj[:logged_in]
k in to refresh the connection
t Response: #{e}...Attempting to Re-Authenticate; Retries left #{retry_count}")
ogin(
scan_obj[:appscan_ip],
an_obj[:username],
4.decode64(appscan_obj[:password])
n_appscan_obj[:cookie]
app obj over the old app obj
key do |k|
= n_appscan_obj[k]

def self.authors

def self.authors
st.pentest@0dayinc.com>

def self.configure_scan_options(opts = {})

def self.configure_scan_options(opts = {})
[:appscan_obj]
pts[:folder_item_id].to_i
ion].to_s.scrub
e]

fStartingUrls
.split(',').each_with_index do |url, index|
&' unless index.zero?
value=#{URI.encode_www_form(url.strip.chomp)}"
hentication
alue == false
alue=0' # Don't require authentication

alue=1' # Require authentication
r, :esCOTHttpPassword, :elCOTScanLimit
ue=#{value.to_s.scrub}"
s = ''
options(
ppscan_obj,
: folder_item_id
 { |url| available_options << "#{File.basename(url)}\n" }
info("Valid Options are:\n\n#{available_options}")
s = ''
options(
ppscan_obj,
: folder_item_id
 { |url| available_options << "#{File.basename(url)}\n" }
error("Invalid option '#{option}' parameter passed.\nValid Options are:\n\n#{available_options}")
 Existing Option Values
_rest_call(
scan_obj,
st,
eritems/#{folder_item_id}/options/#{option}?put=1",
body.to_s
esponse] = response
esponse] = Nokogiri::XML(response)
ns] = scan_config[:xml_response].xpath('//xmlns:option/@value')
 => e

def self.create_scan_based_on_template(opts = {})

def self.create_scan_based_on_template(opts = {})
[:appscan_obj]
[:template_id].to_i
scan_name].to_s.scrub
scan_desc].to_s.scrub
_rest_call(
scan_obj,
st,
eritems?templateId=#{template_id}",
=#{scan_name}&description=#{scan_desc}"
o Use Data Structure
ng it to the End User
 XML on their own.
] = response
] = Nokogiri::XML(response)
= scan[:xml_response].xpath(
tems/xmlns:content-scan-job/@href'
id] = scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:id'
 scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:name'
 scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:description'
r_url] = scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:parent/@href'
r_id] = scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:parent/xmlns:id'
can[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:contact'
scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:state/xmlns:id'
= scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:state/xmlns:name'
 scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:action/xmlns:id'
 = scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:action/xmlns:name'
 = scan[:xml_response].xpath(
tems/xmlns:content-scan-job/xmlns:options/@href'
url] = scan[:xml_response].xpath(
tems/xmlns:report-pack/@href'
id] = scan[:xml_response].xpath(
tems/xmlns:report-pack/xmlns:id'
 = scan[:xml_response].xpath(
tems/xmlns:report-pack/xmlns:reports/@href'
t] = scan[:xml_response].xpath(
tems/xmlns:report-pack/xmlns:reports/xmlns:count'
 => e
ror #{e}:\nREST response returned:\n#{response}")

def self.folder_item_scan_action(opts = {})

def self.folder_item_scan_action(opts = {})
[:appscan_obj]
pts[:folder_item_id].to_i
ion].to_s.scrub.to_sym
 opts[:poll_interval].nil?
60
se
opts[:poll_interval].to_i
d
 is in a Ready state
 = PWN::Plugins::IBMAppscan.get_folder_item_by_id(
ppscan_obj,
: folder_item_id
der_item[:state]
error("Scan isn't in a Ready state.  Current state: #{state}, abort.") if state != 'Ready'
icking Off Scan for Folder Item: #{folder_item_id}")
an_rest_call(
ppscan_obj,
post,
lderitems/#{folder_item_id}",
tion=2'
to Monitor Scan Completion
Ready'
erval
em = PWN::Plugins::IBMAppscan.get_folder_item_by_id(
 appscan_obj,
id: folder_item_id
older_item[:state]
"Current Scan State: #{state}...")
can Completed @ #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}")
an_rest_call(
ppscan_obj,
post,
lderitems/#{folder_item_id}",
tion=3'
an_rest_call(
ppscan_obj,
post,
lderitems/#{folder_item_id}",
tion=4'
an_rest_call(
ppscan_obj,
post,
lderitems/#{folder_item_id}",
tion=5'
error("Invalid action.  Valid actions are:\n:run\n:suspend\n:cancel\n:end\n")
esponse] = response
esponse] = Nokogiri::XML(response)
 => e

def self.generate_scan_report(opts = {})

def self.generate_scan_report(opts = {})
[:appscan_obj]
scan_name]
[:output_path]
an_obj[:appscan_ip].to_s.scrub
://#{appscan_ip}:9443/ase/pages/Login.jsp"
 "https://#{appscan_ip}/ase/FolderExplorer.aspx"
s://#{appscan_ip}/ase/LogOut.aspx"
t path actually exists
ror("Output directory does not exist: #{output_path}") unless File.directory?(output_path)
:Plugins::TransparentBrowser.open(
eadless,
27.0.0.1:8080'
r_obj[:browser]
tem
in_uri.to_s.to_s.scrub
ld(name: 'j_username').when_present.set(appscan_obj[:username])
ld(name: 'j_password').when_present.set(Base64.decode64(appscan_obj[:password]))
ame: 'login').when_present.click
 reports page and click on the report link
e_appscan_uri.to_s.to_s.scrub
xt, 'ASE').when_present.click
eport link with a matching name and click it
ch do |link|
k.text == scan_name.to_s) && link.href =~ /^https:.+XReports.+/
t.click
ror("Could not find matching scan name for name #{scan_name}") unless clicked
utput_path}/#{scan_name.gsub(/[^\w.-]/, '_')}/"
tput_path if File.directory?(output_path)
utput_path
 level report
_browser.url}&exportformat=pdf&exportdelivery=download"
utput_path}Top_Level.pdf"
scan_obj,
ort_link,
put_name
 => e
ror retrieving report for '#{scan_name}': #{e}")
ays logout
out_uri.to_s.to_s.scrub

def self.get_a_folders_folder_items(opts = {})

def self.get_a_folders_folder_items(opts = {})
[:appscan_obj]
folder_item_id].to_i
_rest_call(appscan_obj: appscan_obj, rest_call: "folders/#{folder_id}/folderitems")
tems = {}
tems[:raw_response] = response
tems[:xml_response] = Nokogiri::XML(response)
tems
 => e

def self.get_folder_by_id(opts = {})

def self.get_folder_by_id(opts = {})
[:appscan_obj]
folder_id].to_i
_rest_call(appscan_obj: appscan_obj, rest_call: "folders/#{folder_id}")
se] = response
se] = Nokogiri::XML(response)
 => e

def self.get_folder_item_by_id(opts = {})

def self.get_folder_item_by_id(opts = {})
[:appscan_obj]
pts[:folder_item_id].to_i
_rest_call(appscan_obj: appscan_obj, rest_call: "folderitems/#{folder_item_id}")
esponse] = response
esponse] = Nokogiri::XML(response)
us of a Scan
:
 10;
] = folder_item[:xml_response].xpath('//xmlns:state/xmlns:name').text
 => e
ror: #{e} | #{e.class}\nResponse Returned: #{folder_item[:raw_response]}")

def self.get_folder_item_options(opts = {})

def self.get_folder_item_options(opts = {})
[:appscan_obj]
pts[:folder_item_id].to_i
hy not all options are returned
ormFillUserNameValue & esCOTAutoFormFillPasswordValue)
_rest_call(appscan_obj: appscan_obj, rest_call: "folderitems/#{folder_item_id}/options")
s = {}
s[:raw_response] = response
s[:xml_response] = Nokogiri::XML(response)
s[:options] = folder_item_options[:xml_response].xpath(
le-option/@href'
s
 => e

def self.get_folder_items(opts = {})

def self.get_folder_items(opts = {})
[:appscan_obj]
_rest_call(appscan_obj: appscan_obj, rest_call: 'folderitems')
response] = response
response] = Nokogiri::XML(response)
 => e

def self.get_folders(opts = {})

def self.get_folders(opts = {})
[:appscan_obj]
_rest_call(appscan_obj: appscan_obj, rest_call: 'folders')
nse] = response
nse] = Nokogiri::XML(response)
 => e

def self.get_issue_collection(opts = {})

def self.get_issue_collection(opts = {})
[:appscan_obj]
report_id].to_i
_rest_call(
scan_obj,
rts/#{report_id}/issues?mode=all"
 {}
raw_response] = response
xml_response] = Nokogiri::XML(response)
rieved Issue Collection for Report ID: #{report_id}")
 => e

def self.get_report_collection(opts = {})

def self.get_report_collection(opts = {})
[:appscan_obj]
_id = opts[:report_folder_item_id].to_i
rieving Report Collection ID: #{report_folder_item_id} - Available Report Pack Collection:")
_rest_call(appscan_obj: appscan_obj, rest_call: "folderitems/#{report_folder_item_id}/reports")
= {}
:raw_response] = response
:xml_response] = Nokogiri::XML(response)
rt pack collection
:xml_response].xpath('//xmlns:report').each do |r|
 - #{r.xpath('xmlns:name').text}")
 => e

def self.get_report_data(opts = {})

def self.get_report_data(opts = {})
:appscan_obj]
:report_link]
:output_name]

ort_link)
Plugins::TransparentBrowser.open(browser_type: :rest)
rowser]
_link, 'Cookie' => appscan_obj[:cookie], :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
/#{uri.host}#{res.headers['location']}"
ocation}"
rt on the server side
on, 'Cookie' => appscan_obj[:cookie], :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
t_name, 'wb')
= 'Stream'
'Cookie' => appscan_obj[:cookie], :verify_ssl => OpenSSL::SSL::VERIFY_NONE) do |resp|
do |seg|
=> e
ld not get report data: #{e}")

def self.get_scan_templates(opts = {})

def self.get_scan_templates(opts = {})
[:appscan_obj]
_rest_call(appscan_obj: appscan_obj, rest_call: 'templates')
ponse] = response
ponse] = Nokogiri::XML(response)
 => e

def self.get_single_report(opts = {})

def self.get_single_report(opts = {})
[:appscan_obj]
report_id].to_i
_rest_call(appscan_obj: appscan_obj, rest_call: "reports/#{report_id}")
se] = response
se] = Nokogiri::XML(response)
rieved Report ID/Name: #{report_id}/#{report[:xml_response].xpath('//xmlns:report/xmlns:name').text}")
 => e

def self.get_single_report_data(opts = {})

def self.get_single_report_data(opts = {})
[:appscan_obj]
report_id].to_i
_rest_call(
scan_obj,
rts/#{report_id}/data?mode=all"
esponse] = response
esponse] = Nokogiri::XML(response)
rieved Report Data for Report ID: #{report_id}")
 => e

def self.get_single_report_schema(opts = {})

def self.get_single_report_schema(opts = {})
[:appscan_obj]
report_id].to_i
_rest_call(
scan_obj,
rts/#{report_id}/data?metadata=schema"

_response] = response
_response] = Nokogiri::XML(response)
rieved Report Schema for Report ID: #{report_id}")
 => e

def self.get_subfolders_of_folder(opts = {})

def self.get_subfolders_of_folder(opts = {})
[:appscan_obj]
folder_id].to_i
_rest_call(appscan_obj: appscan_obj, rest_call: "folders/#{folder_id}/folders")
sponse] = response
sponse] = Nokogiri::XML(response)
 => e

def self.help

def self.help
self}.login(
equired host/ip of Nexpose Console (server)',
uired username',
ional password (will prompt if nil)'
.schema(
required appscan_obj returned from login method'
}.version(
required appscan_obj returned from login method'
}.get_folders(
required appscan_obj returned from login method'
elf}.get_subfolders_of_folder(
required appscan_obj returned from login method',
quired folder to retrieve'
.get_folder_by_id(
required appscan_obj returned from login method',
quired folder to retrieve'
{self}.get_folder_items(
required appscan_obj returned from login method'
self}.get_folder_item_by_id(
required appscan_obj returned from login method',
: 'required folder item to retrieve'
_items = #{self}.get_a_folders_folder_items(
required appscan_obj returned from login method',
quired folder to retrieve'
ons = #{self}.get_folder_item_options(
required appscan_obj returned from login method',
: 'required folder item to retrieve'
reate_scan_based_on_template(
required appscan_obj returned from login method'
required template id returned from get_scan_templates method'
quired name of scan'
quired description of scan'
lf}.get_scan_templates(
required appscan_obj returned from login method'
self}.configure_scan_options(
required appscan_obj returned from login method',
: 'required folder item id',
red option to change within the scan (folder item).  Pass :help for a list of options.',
ed option value(s)'
self}.folder_item_scan_action(
required appscan_obj returned from login method',
: 'required folder item id',
red action for scan to follow. Available actions are: :run, :suspend, :cancel, & :end',
 'optional setting to determine length in seconds to poll for scan state (defaults to 60)'
n = #{self}.get_report_collection(
required appscan_obj returned from login method',
item_id: 'required report folder item id'
.get_single_report(
required appscan_obj returned from login method',
quired report id'
self}.get_single_report_data(
required appscan_obj returned from login method',
quired report id'
#{self}.get_single_report_schema(
required appscan_obj returned from login method',
quired report id'
 = #{self}.get_issue_collection(
required appscan_obj returned from login method',
quired report id'
_scan_report(
required appscan_obj returned from login',
quired name of scan for which to generate a report',
required path to save generated report'
required appscan_obj returned from login method'

def self.login(opts = {})

def self.login(opts = {})
:appscan_ip]
sername].to_s.scrub
ri = "https://#{appscan_ip}/ase/services".to_s.scrub
[:password].nil?
Plugins::AuthenticationHelper.mask_password
:password].to_s.scrub
ging into IBM Appscan Enterprise Server: #{appscan_ip}")
:Plugins::TransparentBrowser.open(browser_type: :rest)
ser_obj[:browser]::Request
ient.execute(
scan_api_uri}/login",
=#{username}&password=#{password}",
e
taining the Appscan Server host/ip & post-authenticated Appscan REST cookie
arse(response.args[:url]).host
asc_session_id=#{response.cookies['asc_session_id']}; ASP.NET_SessionId=#{response.cookies['ASP.NET_SessionId']}"
an_ip] = appscan_ip
e] = appscan_cookie
esponse] = response
esponse] = Nokogiri::XML(response)
] = appscan_obj[:xml_response].xpath(
xmlns:build'
sion] = appscan_obj[:xml_response].xpath(
xmlns:dbversion'
_version] = appscan_obj[:xml_response].xpath(
xmlns:rules-version'
ame] = appscan_obj[:xml_response].xpath(
xmlns:user-name'
ord] = Base64.strict_encode64(password)
d_in] = true
 => e

def self.logout(opts = {})

def self.logout(opts = {})
[:appscan_obj]
ging out...')
_rest_call(appscan_obj: appscan_obj, rest_call: 'logout')
ged_in] = false
ul'
 => e

def self.schema(opts = {})

def self.schema(opts = {})
[:appscan_obj]
_rest_call(appscan_obj: appscan_obj, rest_call: 'schema')
se] = response
se] = Nokogiri::XML(response)
 => e

def self.version(opts = {})

def self.version(opts = {})
[:appscan_obj]
_rest_call(appscan_obj: appscan_obj, rest_call: 'version')
nse] = response
nse] = Nokogiri::XML(response)
version[:xml_response].xpath(
xmlns:build'
] = version[:xml_response].xpath(
xmlns:dbversion'
sion] = version[:xml_response].xpath(
xmlns:rules-version'
 = version[:xml_response].xpath(
xmlns:user-name'
 => e