Lucene search

K
attackerkbAttackerKBAKB:25D0B693-7B42-43C3-A0FA-32E0D22A856D
HistoryJul 24, 2024 - 12:00 a.m.

CVE-2023-45249

2024-07-2400:00:00
attackerkb.com
14
remote command execution
default passwords
acronis cyber infrastructure (aci)
vulnerability
cve-2023-45249

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

10

Confidence

High

Remote command execution due to use of default passwords. The following products are affected: Acronis Cyber Infrastructure (ACI) before build 5.0.1-61, Acronis Cyber Infrastructure (ACI) before build 5.1.1-71, Acronis Cyber Infrastructure (ACI) before build 5.2.1-69, Acronis Cyber Infrastructure (ACI) before build 5.3.1-53, Acronis Cyber Infrastructure (ACI) before build 5.4.4-132.

Recent assessments:

h00die-gr3y at September 15, 2024 5:15pm UTC reported:

On 24 July, Acronis published the security advisory SEC-6452: Remote command execution due to use of default passwords where default passwords are exploited to gain admin access to the Acronis Cyber Infrastructure. It was also reported by Acronis that this vulnerability was actively exploited by cyber criminals and patched 9 months ago.

If you search for actual examples of the exploit, no detailed technical publications are available, so I thought let’s give it a go and figure out what this vulnerability is all about.

So I downloaded a Acronis Cyber Infrastructure (ACI) appliance 4.7 from their website and installed it on VirtualBox (see this article).
After completing the installation process, you access the Acronis Web Portal on port 8888 via HTTPS with the admin credentials set during the installation. You can also the access the appliance directly by logging in as root. These credentials are also asked and set during the installation process. This is of course very helpful to analyze the server image because you have full access to appliance and the installed software.

So lets start the search for our default passwords!!!

Let’s check first the user credentials available on the appliance itself by checking the /etc/password and /etc/shadow files.
Not much to gain here. The only password hash available in the /etc/shadow file is for user root which is set during the initial setup of the ACI appliance.

Next in line is to investigate the user credentials available in the Acronis Web Portal.
If you login as admin, you will find in the settings->user and projects section, three default users:

  • admin

  • backup-service-user

  • vstorage-service-user

User admin credentials are set during the installation process so that rules out the default password.
Both the backup-service-user and storage-service-user are potential candidates where the storage-service-user is the most promising candidate because this user is default enabled and has the role system-administrator assigned.

The appliance has a PostgreSQL DB that stores all configuration information. The users, passwords and roles are stored in the keystone database.

You can easily query the database by logging into the appliance as root and switch to the postgres user and access the database with psql.

Acronis Cyber Infrastructure release 4.7
========================================================================
= Warning! Do not enable third-party repositories. Install third-party =
= software only from the default repository. Use only commands allowed =
= in the product documentation.                                        =
========================================================================
[root@aci-471-53 ~]# su postgres
bash-4.2$ psql
could not change directory to "/root": Permission denied
psql (11.16)
Type "help" for help.

postgres=# \l
                                   List of databases
    Name    |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges
------------+------------+----------+-------------+-------------+-----------------------
 coredns    | coredns    | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 grafana    | grafana    | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 keystone   | vstoradmin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres   | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
            |            |          |             |             | postgres=CTc/postgres
 template1  | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
            |            |          |             |             | postgres=CTc/postgres
 vstoradmin | vstoradmin | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
(7 rows)

postgres=# \c keystone
You are now connected to database "keystone" as user "postgres".
keystone=# select * from "local_user";
 id |             user_id              | domain_id |         name          | failed_auth_count | failed_auth_at
----+----------------------------------+-----------+-----------------------+-------------------+----------------
  1 | a56cc7f698fa41d99d1c9dd22aa73580 | default   | vstorage-service-user |                 0 |
  2 | 57bf107a224145c6a1217c71da5f4911 | default   | backup-service-user   |                   |
  3 | fad1606d29a64ff6b7c45b1128551a97 | default   | admin                 |                 0 |
(3 rows)

keystone=# select * from "password";
 id | local_user_id |         expires_at         | self_service |                        password_hash                         |  created_at_int  |  expires_at_int  |         created_at
----+---------------+----------------------------+--------------+--------------------------------------------------------------+------------------+------------------+----------------------------
  1 |             1 | 2024-08-05 11:31:58.573171 | f            | $2b$12$/.ZPGchRUlOGJcNO2S.bOOF3ykww0vShNEr/jwZxvQtksCzGHYcrO | 1653058897767616 | 1722857518573171 | 2022-05-20 15:01:37.767616
  2 |             1 |                            | f            | $2b$12$YKyODw1N3mTO9qj7ch1h6O2qZQGjSgW/CIKyQ2Tz7A49sJvHI0r/q | 1722857518573171 |                  | 2024-08-05 11:31:58.573171
  3 |             3 |                            | f            | $2b$12$3PT2/rbf4rkNBPThiZclyeD/FFP5UXLs4bTfg0L27LeSjqyxNQ2xO | 1722857529542615 |                  | 2024-08-05 11:32:09.542615
(3 rows)

We can query the users and the password hashes, which is promising but “Are these default passwords?”, and if yes, “What is the password?”
To answers these questions, we need to dig a bit deeper within the appliance and figure out what happens during the initial installation and configuration setup of the appliance.
One very interesting directory is /usr/libexec/vstorage-ui-backend/libexec that holds most of initial configuration shell scripts called during the installation of the appliance.

[root@aci-471-53 libexec]# pwd
/usr/libexec/vstorage-ui-backend/libexec
[root@aci-471-53 libexec]# ls
alua-functions.sh      keystone-service-init.sh                                                     oneshot-0021-enable-russian-language.sh
bouncer-functions.sh   logging.sh                                                                   oneshot-cleanup-wal-archive.sh
check-backend.sh       oneshot-0008-upgrade-to-roles-sets.sh                                        oneshot-disable-wal-archiving.sh
clear-vips.sh          oneshot-0009-upgrade-db.sh                                                   oneshot-init-keystone.sh
db-functions.sh        oneshot-0010-disable-pghba-ident-entry.sh                                    oneshot-migrate-roles-to-agent.sh
dns-functions.sh       oneshot-0011-init-coredns.sh                                                 on-master.sh
functions.sh           oneshot-0012-enable-ha-for-postgresql.sh                                     on-standby.sh
gen-certificate.sh     oneshot-0013-clean-up-mdses-in-order-to-add-dns-srv-recs.sh                  pg-convert-layout.sh
ha-ovh-setup           oneshot-0014-create-self-service-roles-in-keystone.sh                        pg-scripts.sh
ha-ovh-teardown        oneshot-0015-clean-up-mdses-in-order-to-activate-vstorage-target-manager.sh  pg-switch-to-hot-standby.sh
ha-scripts.sh          oneshot-0016-update-internal-endpoint-in-keystone.sh                         pg-switch-to-master.sh
init-backend.sh        oneshot-0017-create-roles-implication-in-keystone.sh                         set-vips.sh
init-grafana.sh        oneshot-0018-create-backup-service-user.sh                                   takeover-management-node.sh
init-postinstall.sh    oneshot-0019-create-compute-cert.sh                                          utils-functions.sh
keystone-functions.sh  oneshot-0019-enable-postgres-backup.sh                                       uwsgi-backend-stop.sh
keystone-gen-env.sh    oneshot-0020-turn-off-aip-early-access.sh
[root@aci-471-53 libexec]#

I am not gonna dwell on all scripts, but the oneshot-init-keystone.sh is an interesting script to explore what is happening during initial installation.

#!/usr/bin/env bash

#set -x

. ~vstoradmin/libexec/logging.sh
LOG_FILE="/var/log/vstorage-ui-backend/init_keystone.log"
BACKEND_CONFIG=/usr/libexec/vstorage-ui-backend/etc/backend.cfg
log_init "${LOG_FILE}"
exec &>>"${LOG_FILE}"

. ~vstoradmin/libexec/db-functions.sh
. ~vstoradmin/libexec/keystone-functions.sh

grep -q -w "KEYSTONE_SERVICE_PASSWORD" ${BACKEND_CONFIG} || init_keystone

It calls two other scripts db-functions.sh and keystone-functions.sh which are worthwhile to explore and the last grep command is very interesting where a KEYSTONE_SERVICE_PASSWORD is queried from the file /usr/libexec/vstorage-ui-backend/etc/backend.cfg.

We are getting closer…

If we check the file /usr/libexec/vstorage-ui-backend/etc/backend.cfg it actually reveals the password of the vstorage-service-user!!!

KEYSTONE_USER_MIGRATION=True
KEYSTONE_SERVICE_USER='vstorage-service-user'
KEYSTONE_SERVICE_PASSWORD='3bfda47e79d62f7798e38acc7ff6'
KEYSTONE_SERVICE_PROJECT='admin'
KEYSTONE_ENDPOINT='https://127.0.0.1:5000/v3'

Is this the famous default password? Mmm, this looks too simple…
Let’s check how this password is ending up in this config file.

Let’s explore the other two scripts.
Browsing thru keystone-functions.sh the first function gen_keystone_passwd() already shows that the password gets randomly generated with openssl for the vstorage-service-user. This looks like a dead-end street.

function gen_keystone_passwd() {
    sudo -u vstoradmin openssl rand -hex 14 2>/dev/null

    [ $? -ne 0 ] && error "Unable to generate password for keystone service user" || :
}

But…

After exploring the second script db-functions.sh, interesting new information is revealed because it seems that during the creation and configuration of the database, default passwords are indeed being used.

configure_db() {
    log_inf "Configure database..."
    create_user "vstoradmin" "CREATEDB CREATEROLE LOGIN REPLICATION PASSWORD 'vstoradmin'"
    create_database "vstoradmin" "vstoradmin"
    log_inf "Database has been configured"
}

Database user vstoradmin seems to have a default password vstoradmin.
That is really interesting, so let’s validate this in the database by querying the passwords for these DB users which are stored in the postgres database table below.

postgres=# select * from "pg_authid";
          rolname          | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit |             rolpassword             | rolvaliduntil
---------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------------------------------+---------------
 postgres                  | t        | t          | t             | t           | t           | t              | t            |           -1 |                                     |
 pg_monitor                | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_read_all_settings      | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_read_all_stats         | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_stat_scan_tables       | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_read_server_files      | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_write_server_files     | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_execute_server_program | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 pg_signal_backend         | f        | t          | f             | f           | f           | f              | f            |           -1 |                                     |
 vstoradmin                | f        | t          | t             | t           | t           | t              | f            |           -1 | md5dc23b46758bc7e2c4d3d19493c492aae |
 coredns                   | f        | t          | f             | f           | t           | f              | f            |           -1 | md56e2738b0f8848df4ed98977974c83a7e |
 grafana                   | f        | t          | f             | f           | t           | f              | f            |           -1 |                                     |
(12 rows)

We can see the md5 hashed passwords in the table for user vstoradmin and coredns which are in the typical PostgreSQL format of the string “md5” followed by the md5 hash of a string comprised of the password followed by the postgres username.

Let’s use this logic and check if these accounts are using default passwords with hashcat.

# cat md5.hash
6e2738b0f8848df4ed98977974c83a7e
dc23b46758bc7e2c4d3d19493c492aae
# cat password.txt
vstoradminvstoradmin
corednscoredns
# hashcat --show  -a 0 -m 0 md5.hash password.txt
6e2738b0f8848df4ed98977974c83a7e:corednscoredns
dc23b46758bc7e2c4d3d19493c492aae:vstoradminvstoradmin

Or you can do the other way around .

[root@aci-471-53 libexec]# echo -n "md5"; echo -n "vstoradminvstoradmin" | md5sum | awk '{print $1}'
md5dc23b46758bc7e2c4d3d19493c492aae
[root@aci-471-53 libexec]# echo -n "md5"; echo -n "corednscoredns" | md5sum | awk '{print $1}'
md56e2738b0f8848df4ed98977974c83a7e
[root@aci-471-53 libexec]#

And BINGO, the md5 hashed passwords are matching and we have found default passwords!!!

The final confirmation is to validate a patched version of the Acronis Cyber Infrastructure and check if these flaws have been mitigated.
Checking the db-functions.sh on a patched ACI 5.0.1-61 appliance, shows that no default password is set in the configure_db() function.

configure_db() {
    log_inf "Configure database..."
    create_user "vstoradmin" "CREATEDB CREATEROLE LOGIN REPLICATION"
    create_database "vstoradmin" "vstoradmin"
    log_inf "Database has been configured"
}

Also the pg_auth table in the postgres database does not show any passwords for the vstoradmin and coredns database users.
This confirms that the use of default passwords for these accounts have been mitigated.

postgres=# select * from "pg_authid";
          rolname          | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvalidu
ntil
---------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+----------
-----
 postgres                  | t        | t          | t             | t           | t           | t              | t            |           -1 |             |
 pg_monitor                | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_read_all_settings      | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_read_all_stats         | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_stat_scan_tables       | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_read_server_files      | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_write_server_files     | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_execute_server_program | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 pg_signal_backend         | f        | t          | f             | f           | f           | f              | f            |           -1 |             |
 vstoradmin                | f        | t          | t             | t           | t           | t              | f            |           -1 |             |
 coredns                   | f        | t          | f             | f           | t           | f              | f            |           -1 |             |
 grafana                   | f        | t          | f             | f           | t           | f              | f            |           -1 |             |
(12 rows)

postgres=#

The exploit

Now we want to understand how we can exploit this vulnerability.
After digging into the keystone db with the privileges of the vstoradmin user, it already shows that we can easily add a new administrative user by editing it directly in the keystone database. This administrative user allows us to upload ssh-keys via the ACI Web Portal that enables direct root access via SSH to the appliance.
You will need access to Acronis Web Portal, the PostgreSQL database and the SSH service, but if these three services are available and accessible from the outside world, you can easily hack yourself into any non-patched ACI appliance as user root.

I have created an Metasploit module that does all the magic for you.
You can find this module in Metasploit as PR 19463 – Acronis Cyber Infrastructure default password remote code execution.

Mitigation

You should patch your ACI appliance immediately following the Acronis security advisory SEC-6452.

References

CVE-2023-45249
Acronis security advisory SEC-6452
Acronis ACI Downloads
Metasploit PR 19463 – Acronis Cyber Infrastructure default password remote code execution

Assessed Attacker Value: 5
Assessed Attacker Value: 5Assessed Attacker Value: 4

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

10

Confidence

High

Related for AKB:25D0B693-7B42-43C3-A0FA-32E0D22A856D