Published on: 28 10 2016 |
Modified on: 20 06 2024
Author: Benoit Ancel
6minutes
During the last decade, different types of malware have been targeting Linux servers; Elknot, Encoder, Mirai, LuaBot, NyaDrop, Gayfgt etc. Most of them are used for DDoS purpose but there are some exceptions. Rex is one of them. In this article, we’ll try to present a detailed analysis of Rex.
Rex is a new malware developed in Go. Monitoring its activity over the last seven months brought out the efforts for developing various features.
Malware overview
Rex is a hybrid between a malware and a tool. The behavior depends on a list of arguments. You can use it in two different ways:
Scan mode: with the “scan” command line argument, the binary file uses embedded exploits to infect new Linux servers.
Without scan mode: Rex contacts other bots through P2P protocol (DHT over HTTPS) and waits for commands.
Rex is always installed as a hidden file in the directory /tmp/, the malware does not have persistence mechanisms or any other hiding features. Quite the contrary, a help menu is available (-h).
benkow@stormshield:/home/rex/tmp$ ./.Z9g5aas0p0 -h Usage of ./.Z9g5aas0p0: -debug enable debugging -elevate.ignore string credentials to ignore during elevation (default "root") -elevate.skip skip elevation (default true) -ipc enable stdio ipc -log.dht log DHT requests -log.http log HTTP requests -socks string SOCKS5 proxy address -strategy string scan strategy [random, sequential] (default "random") -target string target(s) (default "0.0.0.0/0") -wait int wait for PID to exit before starting (0: disable) -wordpress.pingback enable WordPress Pingback
The help menu describes all the features available for both modes (scan or c&c). Arguments details:
Debug/log: launch the malware in debug mode, it is useful for analysis.
Elevate: Rex can try to run itself as root by bruteforcing SSH service, you can ignore specific credentials with elevate.ignore pwd
Ipc: we have not seen this feature used yet
Socks: launch Rex through a socks proxy
Strategy: configure how Rex scan IPs (random or sequential)
There are also some hidden arguments. You can use Rex as a DDoS tool with the argument "–stresser target".
The main process is used for malware communication, when the bot master sends a command, the main process forks with the command in argument.
This is why, when you look at an infected host, Rex uses several processes:
Development cycle
Rex is a very active botnet. The binary file is updated on a daily basis. We’ll try to give an overview seven months of new features (click to enlarge).
The first version was mainly used for infecting a first group of servers. It contained several exploits but no useful features. Rex tries to infect other servers via Web based exploits (WordPress, Drupal…).
In order to exploit a remote file inclusion vulnerability, the remote file is hosted on infected machines on port 5099. I.E.: https://%s:5099/payload/php/%s/wp-gwollegb/ for gwollegb RFI exploit.
Drupal
Rex infects Drupal websites via CVE-2014-3704, a SQLi that allows an attacker to change the admin password. It serves two purposes, first getting access to the server and second locking the website in order to ask for a ransom. After exploitation, Rex wrote a blogpost on the homepage with the following message: “Website is locked. Please transfer 1.4 BitCoin to address 3M6SQh8Q6d2j1B4JRCe2ESRLHT4vTDbSM9 to unlock content.”
In the first version, Drupal locker was the only “visible” feature.
In this example, Rex exploits a Revslider WordPress module in order to upload a zip file Showbiz.zip / revslider.zip which contains a PHP script used for PHP verification:
We are Armada Collective. All your servers will be DDoS-ed starting {{ .Time.Weekday.String }} ({{ .Time.Format "Jan 2 2006" }}) if you don't pay {{ .Amount }} Bitcoins @ {{ .Address }} When we say all, we mean all - users will not be able to access sites host with you at all. If you don't pay by {{ .Time.Weekday.String }}, attack will start, price to stop will increase by {{ .Step }} BTC for every day of attack. If you report this to media and try to get some free publicity by using our name, instead of paying, attack will start permanently and will last for a long time. This is not a joke. Our attacks are extremely powerful - sometimes over 1 Tbps per second. So, no cheap protection will help. Prevent it all with just {{ .Amount }} BTC @ {{ .Address }} Do not reply, we will probably not read. Pay and we will know its you. AND YOU WILL NEVER AGAIN HEAR FROM US! Bitcoin is anonymous, nobody will ever know you cooperated.
Interesting fact with this ransom note, CloudFlare reported detection of this threat in March 2016. But we spot the first version of Rex with this ransom note at the end of May 2016.
A deeper look at the ransom note shows that it is not exactly the same; we have the same bullshit about 1Tb DDoS attacks but sender email is different (we’ve seen armada-collective@gmail.com / armada-collective@hotmail.com and CloudFlare see armada.collective@openmailbox.org ).
This coincidence lets us thinks that Rex developers have done some tests with this threat before creating Rex. At this time no real DDoS feature were present in the binary file.
Three days after (24-05-2016), another update came with one real DDoS implementation, DnsAmpl.
Optimizations time – June 2016.
During June 2016 we did not notice important updates, but we have seen that the bot master has refactored the source code until the end of June.
At the end of June, Rex has implemented a complete “stresser” module. Now the malware supports many different DDoS types (HTTP, SlowLoris, DNSAmp…) and the builder moved on another machine “/home/user/src/rex/”.
“We are anonymous” – July 2016
Some days after (09-07-2016) Rex added 3 new exploits:
- Drupal RESTWS REC exploit
- Magento RCE exploit (CVE-2015-1397)
- Airos Arbitrary File Upload Exploit
The ransom note has been rewritten. Now they did not mention Armada Collective anymore but call themself “anonymous”.
FORWARD THIS MAIL TO WHOEVER IS IMPORTANT IN YOUR COMPANY AND CAN MAKE DECISION! We are Anonymous. All your servers will be DDoS-ed starting {{ .Time.Weekday.String }} ({{ .Time.Format "Jan 2 2006" }}) if you don't pay {{ .Amount }} Bitcoins @ {{ .Address }} When we say all, we mean all - users will not be able to access sites host with you at all. Right now we will start 15 minutes attack on your site's IP {{ .IP }}. It will not be hard, we will not crash it at the moment to try to minimize eventual damage, which we want to avoid at this moment. It's just to prove that this is not a hoax. Check your logs! If you don't pay by {{ .Time.Weekday.String }}, attack will start, price to stop will increase by {{ .Step }} BTC for every day of attack. If you report this to media and try to get some free publicity by using our name, instead of paying, attack will start permanently and will last for a long time. This is not a joke. Our attacks are extremely powerful - sometimes over 1 Tbps per second. So, no cheap protection will help. Prevent it all with just {{ .Amount }} BTC @ {{ .Address }} Do not reply, we will probably not read. Pay and we will know its you. AND YOU WILL NEVER AGAIN HEAR FROM US! Bitcoin is anonymous, nobody will ever know you cooperated.
The ransom note tries to be more credible, It ask for log checking. Something it could not do before because of the lack of DDoS feature. But it is not enough to earn money. We checked some bitcoin addresses and all these wallets were empty.
BTCBrute and Clicky – August 2016.
Early in August, two new important updates came. The malware size has increased of 1.5mo and now embeds a bitcoin miner based on Btcsuite and a click fraud module called “clicky”.
The click fraud part is really interesting. Rex uses the botnet to display ads hosted on a-ads.com. The game here is to use each bot for clicking on ads and earn money from advertiser. The good news is that it is easy to track ads campaign of a-ads and to retrieve nice statistics.
We have spotted three ad units: 218355 (code name "Unicorns!"), 261029 (code name "Porkupines!") and 251270 (code name "Ferries!"). Two of them are associated to the bitcoin address 1HebiSQX2WfE2kXUuva79US4zNUxcYrHjZ and the last one used 1Q6mA6ERbwmaHX1nYwkrKuDiVjCYe2xma3.
unit 218355 - income details
unit 218355 - impressions details
The ads displayed looks like:
At the time we wrote this article, the clicky module has generated ~1€.
History of a fail – September 2016.
At the end of August, the first big fail of Rex starts (91164673cda591a9a4dec91ecda6dbb515d48df7b56108b5fa0053395c733188). Rex implements a feature for creating a lot of Instagram accounts, probably for social network fraud. But bypassing Instagram anti-spam is not so easy 🙂
First, Rex tries to use the botnet to create Instagram account via https://www.instagram.com/accounts/web_create_ajax/.
Each bot used his own IP to create these fakes accounts. But Instagram has some anti-spam features and all nodes of the botnet have been blacklisted in a few minutes.
One week later, due to node blacklist, Bot master has implemented a proxy socks feature in order to bypass the Instagram blacklist. This new feature results again in 2 fails:
- First implementation failed due to the length of the password.
{"status": "ok", "errors": {"password": ["Create a password at least 6 characters long."]}, "account_created": false}instagram.AccountCreate &{ID: Email:ZRSlnk3uH@gmail.com Name:ZRSlnk3uH Username:ZRSlnk3uH Password:A1EtB} (via &{Addr:X.XXX.XXX.XX:80 Type:2 Node:<nil> Created:0001-01-01 00:00:00 +0000 UTC Updated:0001-01-01 00:00:00 +0000 UTC}): not created
- Second fails resides in the fact that Rex uses known proxy socks list that is already blocked by Instagram.
{"status": "ok", "errors": {"ip": ["The IP address you are using has been flagged as an open proxy. If you believe this to be incorrect, please visit http://help.instagram.com/"]}, "account_created": false}instagram.AccountCreate &{ID: Email:LOT8mWL@gmail.com Name:LOT8mWL Username:LOT8mWL Password:yF7QO3} (via &{Addr:XXX.XX.XX.XX:80 Type:2 Node:<nil> Created:0001-01-01 00:00:00 +0000 UTC Updated:0001-01-01 00:00:00 +0000 UTC}): ip blacklisted
After one month of fails, we have not seen this feature used anymore by the bot master.
After the leak of the source code of Mirai, Rex developer tried to implement the Mirai telnet scanner in Rex.
*scanner.telnet.mirai 81.196.136.114:23 - trying ubnt:ubnt *scanner.telnet.mirai 81.196.136.114:23 - prompt at 36 in "ubnt\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 38 in "enable\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 38 in "system\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 37 in "shell\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 34 in "sh\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - credentials incorrect "/bin/busybox MIRAI\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - trying 888888:888888 *scanner.telnet.mirai 81.196.136.114:23 - prompt at 38 in "888888\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 38 in "enable\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 38 in "system\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 37 in "shell\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 34 in "sh\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - credentials incorrect "/bin/busybox MIRAI\r\nUser name is incorrect\r\n\rLogin: " *scanner.telnet.mirai 81.196.136.114:23 - trying root:xc3511 *scanner.telnet.mirai 81.196.136.114:23 - prompt at 35 in "\r\n\rPassword is incorrect\r\n\rPassword: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 35 in "\r\n\rPassword is incorrect\r\n\rPassword: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 35 in "\r\n\rPassword is incorrect\r\n\rPassword: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 35 in "\r\n\rPassword is incorrect\r\n\rPassword: " *scanner.telnet.mirai 81.196.136.114:23 - prompt at 35 in "\r\n\rPassword is incorrect\r\n\rPassword: "
As usual, this first buggy version of Rex Telnet scanner was tested directly in the wild. Unfortunately for the bot master, after one week of telnet scanning, only few new victims were infected (less than 10). But now, when you want to retrieve Mirai sample via Honeypots, you have to be sure that it is not Rex ;).
Last funny fact, this version includes a set of commands used for QA and benchmarking purpose. Maybe they hired a Quality Engineer.
benkow_@stormshield:/home/rex# ./rex -h Usage of ./rex: -debug enable debugging -elevate.ignore string credentials to ignore during elevation (default "root") -elevate.skip skip elevation (default true) -ipc enable stdio ipc -log.dht log DHT requests -log.http log HTTP requests -socks string SOCKS5 proxy address -strategy string scan strategy [random, sequential] (default "random") -target string target(s) (default "0.0.0.0/0") -test.bench string regular expression per path component to select benchmarks to run -test.benchmem print memory allocations for benchmarks -test.benchtime duration approximate run time for each benchmark (default 1s) -test.blockprofile string write a goroutine blocking profile to the named file after execution -test.blockprofilerate int if >= 0, calls runtime.SetBlockProfileRate() (default 1) -test.count n run tests and benchmarks n times (default 1) -test.coverprofile string write a coverage profile to the named file after execution -test.cpu string comma-separated list of number of CPUs to use for each test -test.cpuprofile string write a cpu profile to the named file during execution -test.memprofile string write a memory profile to the named file after execution -test.memprofilerate int if >=0, sets runtime.MemProfileRate -test.outputdir string directory in which to write profiles -test.parallel int maximum test parallelism (default 8) -test.run string regular expression to select tests and examples to run -test.short run smaller test suite to save time -test.timeout duration if positive, sets an aggregate time limit for all tests -test.trace string write an execution trace to the named file after execution -test.v verbose: print additional output -wait int wait for PID to exit before starting (0: disable) -wordpress.pingback enable WordPress Pingback
We’ll continue to monitor all these features, the developer seems to be creative.
Crawling the botnet
As reminding, Rex use DHT P2P over HTTPS for communication. Due to certificate pining failure it is easy for us to do some man-in-the-middle on the malware and then implement a crawler. This is how looks like Rex DHT request:
As you can see, Rex uses the default Go User-Agent “Go-http-client/1.1” and sends gzip encoded requests. We know that DHT supports the following commands:
DHT.Store
DHT.Ping
DHT.FindValue
DHT.FindNode
DHT.Neighbors
So, it is easy to implement a quick crawler. At the time of writing, despite the efforts of the bot master, the botnet is still harmless (~150 bots). Not enough for doing any significant DDoS.
We try to identify the most affected country but due to the random scan strategy this do not allow us to conclude something useful.
Conclusion
Linux malware is a trendy topics, we can find new families every week. The huge amount of vulnerable servers available and the absence of anti-virus attracts crooks on the Linux side. They can stay on a compromised server for several months without being detected. In the case of Rex, if they did not implement “visible” features like Drupal locker, the malware would still be hidden. Regarding how the bot master uses this botnet, we can easily conclude that it may not be part of a big cyber gang, Rex Botnet looks more like an experimental botnet. 2017 promises us some funny crapware on Linux.
Annexes
Quick and dirty yara rules for VTi
rule Rex { meta: description = "Quick and dirty rule for Rex malware" author = "Benkow_@Stormshield" strings: $string1= {6d 61 69 6e 2e 67 6f} $string2 = {72 65 78} $string3= {64 72 75 70 61 6c} condition: all of them }