In early October, Flashpoint released an analysis of an underground forum advertisement for a new malware family known as FlokiBot. It took some time before a sample was found in the wild, but a researcher known as hasherezade flagged one on VirusTotal in early November. She also wrote an analysis of its dropper here. This post takes a look at our analysis of FlokiBot so far.
As the forum advertisement says, FlokiBot is another Zeus-based banking trojan variant developed on the leaked Zeus 184.108.40.206 source code. A quick skim of the binary confirms this and this sample even includes a few debugging statements to help get the reverse engineer started:
- Core::init() called.
- initOsBasic() failed.
- initBaseConfig() failed.
In addition, the command and control (C2) panel login is very Zeus-like:
Zeus-based malware tend to have a readily identifiable code snippet that identifies its version number:
This particular sample is version 12. FlokiBot’s versioning differs from other variants as it doesn’t map to the traditional a.b.c.d format—e.g. 0x02000809 to 220.127.116.11.
In Zeus parlance, a sample’s “base config” is its static configuration that is built into each binary. It is an encrypted structure that usually contains various items, but in FlokiBot’s case it only contains an encryption key and a C2 URL. It uses the traditional Zeus method of decrypting the base config, which is just an XOR:
This sample’s decrypted base config is 1076 bytes in length and looks like this:
The C2 is highlighted in red and the other non-\x00 chunk of data is a 258-byte encryption key that will be used to protect C2 communications.
Review of Zeus C2 Communications
Zeus uses a data structure that they call “binstorage” in its C2 communications. It is composed of a header and a variable number of sections. The header is 48 bytes in length and can be represented in this structure:
- 20 random bytes
- Length (DWORD)
- Flags (DWORD)
- Number of sections (DWORD)
- 16 byte MD5 digest of section data
A section is variable length and can be represented in this structure:
- Type (DWORD)
- Flags (DWORD)
- Compressed size (DWORD)
- Uncompressed size (DWORD)
- Data (variable length)
For C2 communications the binstorage data is encrypted twice, first using a XOR-based algorithm that Zeus calls “visual encrypt”:
The second round of encryption uses the 258-byte key from the base config. This key is actually an “S” value generated by RC4’s key-scheduling algorithm (KSA). It is used with RC4’s pseudo-random generation algorithm (PRGA) to encrypt the visual encrypted data.
Traditionally Zeus would send this encrypted binstorage via HTTP POST requests to the C2, but FlokiBot changes things a bit.
FlokiBot C2 Communications
Per their advertisement we expected some changes to the communication protocol:
“Payload uses a different communication protocol that cannot be detected by Deep-Packet-Inspection unlike Zeus (Packets dont look like Zeus). Config is transfered to bot directly through gate.php encrypted. All reports are written to HDD and then transfered in a single request to command and control center.” (sics)
From our analysis the changes boil down to two things: adding an additional header and, depending on the type of communication, base64 encoding the data. As basic as the changes sound, they do implement the features as advertised above–namely something that is “unlike Zeus” and can be “transferred in a single request”.
The additional header can be separated into a sub header and a variable number of binstorage sections. The sub header looks like this:
- Five magic bytes: \x00\x08\x00\x00\x00
- Random number between 111111 and 999999 (DWORD)
- Number of binstorage sections (DWORD)
Each binstorage section includes two sub headers. The first sub header looks like:
- Hardcoded 1 (BYTE)
- Length of binstorage data + length of second sub header (12 bytes)
The second sub header looks like:
- Same random number as above
- Index number of binstorage chunk, zero based (DWORD)
- Length of binstorage data
- Binstorage data as described above
This newly packaged binstorage is then encrypted as above with visual encrypt and RC4. Depending on the type of communication, the encrypted data is also encoded using base64 before being sent to the C2 in an HTTP POST request. Also depending on the type of communication, the response from the C2 is handled differently—more on this below.
At the time of this writing, all the C2 URLs that we have seen have used HTTPS. Additionally, there is also code to check for .onion based C2 URLs and if that is the case, it routes the C2 traffic through a local TOR proxy.
In Zeus parlance, the “dynamic config” is a configuration file fetched from the C2 server. Among other data, it usually contains additional C2 URLs and webinjects (the bread and butter of banking trojans). FlokiBot has two code paths for fetching the dynamic config. The first path chooses a more traditional method of requesting the config by sending system information to the C2 server. An overview of the information collected and the binstorage section types used are:
- SBCID_BOT_ID (10001) – bot ID
- 10021 – desktop window dimensions
- 10022 – MD5 hex digest of the original malware sample
- 10023 – Unknown 16 byte hex encoded value from the registry
- SBCID_BOT_VERSION (10003) – bot version
- SBCID_TIME_SYSTEM (10009) – system time
- SBCID_TIME_LOCALBIAS (10011) – GMT offset
- SBCID_TIME_TICK (10010) – tick count
- SBCID_OS_INFO (10012) – Windows version
- SBCID_LANGUAGE_ID (10013) – language ID
- SBCID_IPV4_ADDRESSES (10016) – IPv4 address
- SBCID_IPV6_ADDRESSES (10017) – IPv6 address
- SBCID_NET_LATENCY (10005) – latency
- SBCID_TCPPORT_S1 (10006) – SOCKS port
Most of these (SBCID_Xs) are generic Zeus types and more information can be found in the leaked source code. This data is packaged up as described above and sent to the C2 without the additional base64 encoding.
On response, the data is first decrypted using RC4 and visual decrypt. Next, a chunk of data is unpackaged from the newly added header (as described above). This data can be separated into two pieces: a command byte and command data.
A command byte of zero indicates an updated dynamic config. The remaining command data is decrypted using RC4 and visual decrypt and the plain text is a binstorage structure containing the dynamic config. An example and more human readable parsing of the dynamic config looks like:
There is a second code path in FlokiBot to Zeus’ DynamicConfig::updateConfig function (see leaked source code), but it feels like a work in progress—something still being developed maybe. It creates one binstorage section of type 11003 which stores a “1” in a DWORD. It is packaged and encrypted as above, but encodes the encrypted data with base64 before sending it to the C2 server.
On response from the C2, the data is decrypted and unpackaged as above. At the time of this writing the only response we’ve observed is a command byte of “2” and an empty plaintext binstorage as command data.
Noticeably absent from the example dynamic config above are webinjects. While a deep-dive of the man-in-the-browser (MITB) implementation wasn’t done for this analysis, glossing over the code suggests that it is the standard Zeus implementation. The lack of webinjects in this example is likely due to the specific campaign/version we analyzed not using them yet. Based on trends of other Zeus variants though, we expect to see webinjects when/if new FlokiBot campaigns/versions appear in the wild.
FlokiBot contains encrypted strings. It uses the traditional Zeus method to decrypt them:
A full list of decrypted strings will be available here. Among them are 26 bot commands that provide an overview of some of the other functionality available in FlokiBot:
The bot_ddos_start and bot_ddos_stop commands are interesting. While other Zeus variants have included DDoS functionality—most notably Zeus Gameover—it is not a common feature. There are three basic DDoS attacks implemented:
- UDP flood
- TCP connection flood
- HTTP GET flood
Past research on Zeus Gameover suggest that its operators used DDoS functionality to attack banks while they were stealing money from them as a way to distract them from the heist. It’s too early to determine if this is the same idea behind FlokiBot’s DDoS functionality. Regardless, Arbor customers have countermeasure available to mitigate these attack should they be targeted.
Credit Card Track 2 Memory Scraping
As indicated in its advertisement and can be seen in its decrypted strings, FlokiBot also contains functionality to scrape memory for credit card track 2 data. Like its DDoS functionality, the implementation is fairly basic. It scans memory of infected processes for chunks of data that look like the track 2 data format. It does this mostly by looking whether the possible bank identification number (BIN) of the primary account number (PAN) starts with 3, 4, 5, or 6 and maps them to the following card types:
- Amex, Dinners, JP
- Discovery (sic)
It runs a few more checks on the data to make sure digits are where they’re supposed to be and whether the “=” field separator is in the correct place. If any potential track 2 data is found it is exfiltrated back to the C2 using Zeus’ Report::writeStringFormat function (see leaked source code) and a format string of:
Track 2: %s
FlokiBot is a new Zeus-based banking trojan variant that is being sold on underground forums for a hefty price ($1000) and recently started appearing in the wild. Its “killer features” seem to be a slightly modified C2 protocol, basic DDoS attack functionality, and a basic credit card memory scraper. It is too soon to assess how active and widespread this new variant will become, but coincidently during the writing of this post, a new version (13) appeared in the wild. ASERT will continue to monitor this threat to see if FlokiBot becomes a flock of bots.