Banks are big targets for cybercrime and advanced adversaries, it’s definitely not new. They own the money, and as a big part of cyber attacks are money driven, it’s not really surprising that threat actors love to target financial institutions. In the past years cybersecurity became a real problem for everyone, banks are absolutely not spared by the problem.
Of course like any company they are concerned by ransomware, and urgh, no one wants to see a systemic bank falling down under a ransomware attack. The economic impact and social reaction could be really ugly.
We’ve also seen APT cases, most of the time spectacular, like Carbanak gang making ATMs spit money like the Exxon Valdez spilled oil. Or the Central Bank of Bangladesh, whose SWIFT router has been compromised to make huge fraudulent wire transfers (North Korea’s Bureau 39 is suspected to be behind that attack).
These cases are super interesting, but we’ll talk today of another type of cyber threat concerning banks, a specific kind of indirect threat targeting their customers: banking trojans.
It’s kinda hard to detect, handle and manage cyber threats on your network. But dealing with threats aimed at you but living on someone else information system is damn challenging.
It’s probably not the case everywhere but in France, banks usually refund defrauded customers (depending on the amount and the client), so keeping an eye on those threats is important when you run operational cybersecurity team for a financial institution.
Interestingly no one outside the small cybersecurity circus really talks about them and it’s very hard to have an idea of the amount of money fraudsters stole in the past years. Banks prefer to stay silent about that and I won’t tell you but you can trust me: it’s a lot. Curiously the amount of stolen money is not enough for our greedy threat actors as most of them moved to the ransomware business.
In 2018 and 2019, I was working for a French banking CERT, and I spent a lot of time tracking threat actors stealing money in France using Tinynuke, a banking trojan with an interesting story.
Complaints were filed, people were arrested, but several years later the case is still under investigation so unfortunately I won’t be able to give you all the crunchhy details…
How works a banking trojan
Before digging into Tinynuke, I guess we need to explain how works a banking trojan.
That kind of malware aims at stealing money (no shit Sherlock), to do that, the main goal is to infect a computer and inject itself in the victim’s browser (technique called “man in the browser") to detect accessed websites of interest and interact with them.
Main used techniques can be:
- form grabbing: automatic capture of data submitted in web forms to steal passwords
- web injects: manipulation of some websites' source code when you visit them to collect information or credentials
- video casting: to record your screen when you log into your bank account (when it uses a pin pad for example)
- a combination of everything mentionned above…
What if a validation is required by your bank you may ask? Not a problem, the malware just displays a popup asking for the token code by letting the victim believe the bank wants to double check her identity. The crooks can now intercept the token and use it to validate the transfer.
Brilliant no? That technique was highly used between 2007 and 2018, and one the most famous banking trojan on that period was Zeus.
A tool less efficient?
But banks ain’t fools, they added mechanisms to detect “injected” browser by checking webpages integrity, and also temporisations or multiple factor authentications. You know, the awful time to wait to add a beneficiary? Well that’s one of the reasons it has been implemented!
So it became more and more difficult for crooks to exploit banking trojans, but not impossible. It’s just less efficient. Fraudsters now have to detect interesting targets (victims with money, or with a specific e-banking configuration) using casted screens for example, take control of computers, use the ebanking application instead of them and find a way to bypass “two factors” authentication systems…
To add a bit more difficulty to the business, you also need a financial mules network. Mules are people willing to receive money stolen by crooks on their account. The goal is of course to launder the stolen money and to avoid setting up a wire transfer directly to the crooks own bank account…
But maintaining a mules network is hard and expensive. The mules must be paid, you also need to pay henchmen to deal with mules keeping the money you stole… and at the end the fraudsters will lose around 50% of the money. How unfair is that?
All those reasons explain why banking trojans are not popular anymore. In that paper, Brandon Levene (formerly from Chronicle, the company behind VirusTotal) studied crimeware evolution in the past years. And we can clearly see in the following graph how banking trojans became extremely popular in 2017 and almost disappeard in 2018. You can also see that miners and ransomware exploded after that…
Bitcoins and other cryptocurrencies became extremely profitable starting 2017 / 2018 making miners very interesting, even more if you can run them on someone else computer.
Ransomware asking to pay ransoms in cryptocurrencies directly also allowed to reduce the cost of money laundering networks. Handy!
At the end of 2019, we started to see massive ransomware attacks, instead of encrypting a single endpoint, threat actors embraced the APT style and started encrypting a whole network almost instantly. Allowing them to ask for bigger ransom but also raising the chance to see the victim pay.
The business became so profitable that the most famous banking trojans, Emotet, Trickbot and Qakbot moved massively from the banking frauds to standard Remote Access Tools (RATs). Instead of defrauding their victims directly they started selling access to network of interest to ransomware actors.
Since that time, being infected by one of these (okay Emotet is dead now) probably means someone will drop a Cobalt Strike on your computer and start compromising your network to encrypt most part of it using a ransomware.
That business remains one of the most lucrative and efficient so far, with hundreds of companies compromised, and a good proportion of them accepting to pay the ransoms… But that’s not the topic today.
Now we’re fluent with banking trojans generalities, let’s dig into a specific one: Tinynuke.
In 2018, France started being “targeted” by malicious mails propagating a malware I hadn’t encountered before. The mails were mimicking official French Post, or “fake invoices” to spread a quite big payload between 1 and 4 megabytes.
Firstly I didn’t really pay attention. Banking trojans were starting to become “less” a problem at that time. But it started to become quite frequent, and we started receiving them at $dayjob too. So I had to look at it a bit more deeply, and we discovered Tinynuke and its story.
Tinynuke is an interesting piece of malware, quite simple but effective. Written by a young French boy called Augustin I. He wrote it a few years ago to prove he was able to bypass banks' security.
Originally it’s a standard Zeus like banking trojan using injects and form grabbing. But it evolved in a “RAT like” malware, with hidden desktop remote access.
He then tried to sell it on different forums but was mocked and considered as not enough experienced for that business. Offended by that reaction, he published the sources on Github under his real name. Brian Krebs interviewed Augustin after that publication.
Nothing happened in the next months, some skiddies probably used the malware or tried it but nothing really important. But in 2018, it started raining Tinynuke in France and that was the beginning of a long love story between me and the guys behind it.
With the raise of diffusion campaigns, frauds on banks' customers started to become a problem for several institutions. So we had to monitor that closely and try to find some counter measures…
What did we do ?
The first things to do is to collect the maximum of information about the threat actor, we needed to “know the enemy”. Of course we didn’t have a lot of things to study except payloads.
So we studied them, we had to understand how they worked, who they targeted…
Unpacking and first analysis
As previously explained, payloads used by the threat actor were quite big, around 1.5/5mb, which is unusual so I guessed they were “packed” with dependencies or something like that. Anyway, we needed have a look under the hood so the first step was to identity the packing mechanism.
Fortunately for us, the packing system was easy to identify and exploit (I hate unpacking). By looking at the code and the exifs it was explicit the file was indeed packaged using Inno Setup installer.
$ exiftool payload.exe ExifTool Version Number : 11.88 [...] Comments : This installation was built with Inno Setup. Company Name : Draper Laboratory File Description : Watson Librarian Server File Version : Watson 1.0 Legal Copyright : 2012 Original File Name : Product Name : EGPL Librarian Product Version : Watson 1.0
And like we imagined, it’s indeed packaged with a lot of Dlls and other binaries and even Firefox.exe which was super curious.
Example on sample facture logistique.exe:
$> innoextract facture\ logistique.exe Extracting "setup" - setup data version 5.5.7 - "tmp/api-ms-win-core-console-l1-1-0.dll" [temp] - "tmp/api-ms-win-core-datetime-l1-1-0.dll" [temp] [...] - "tmp/api-ms-win-crt-runtime-l1-1-0.dll" [temp] - "tmp/api-ms-win-crt-stdio-l1-1-0.dll" [temp] - "tmp/api-ms-win-crt-string-l1-1-0.dll" [temp] - "tmp/api-ms-win-crt-time-l1-1-0.dll" [temp] - "tmp/api-ms-win-crt-utility-l1-1-0.dll" [temp] - "tmp/b0tn3t'd.png" [temp] - "tmp/data.dll" [temp] - "tmp/dependentlibs.list" [temp] - "tmp/firefox.exe" [temp] - "tmp/mozglue.dll" [temp] - "tmp/msvcp140.dll" [temp] - "tmp/msvcr110.dll" [temp] - "tmp/ucrtbase.dll" [temp] - "tmp/vcruntime140.dll" [temp] Done.
Spotting where Tinynuke’s code was stored was not really difficult, most of the files were clearly dependencies and the timestamps helped a lot. Indeed, only three files were recent (Inno Setup uses a compression method which preserves original timestamps): a DLL, a png and “dependentlibs.lit”, dated from 2018 in the following listing:
$> ls -l total 4165 -rwxrwxrwx 1 hmiser hmiser 18624 oct. 5 2017 api-ms-win-core-console-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 17600 oct. 5 2017 api-ms-win-core-datetime-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 17600 oct. 5 2017 api-ms-win-core-debug-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 18104 oct. 5 2017 api-ms-win-core-errorhandling-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 21696 oct. 5 2017 api-ms-win-core-file-l1-1-0.dll [...] -rwxrwxrwx 1 hmiser hmiser 24256 oct. 5 2017 api-ms-win-crt-string-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 20672 oct. 5 2017 api-ms-win-crt-time-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 18624 oct. 5 2017 api-ms-win-crt-utility-l1-1-0.dll -rwxrwxrwx 1 hmiser hmiser 184771 avril 3 2018 "b0tn3t'd.png" -rwxrwxrwx 1 hmiser hmiser 163840 avril 8 2018 data.dll -rwxrwxrwx 1 hmiser hmiser 8 oct. 14 2017 dependentlibs.list -rwxrwxrwx 1 hmiser hmiser 531408 oct. 5 2017 firefox.exe -rwxrwxrwx 1 hmiser hmiser 133072 oct. 5 2017 mozglue.dll -rwxrwxrwx 1 hmiser hmiser 440120 oct. 5 2017 msvcp140.dll -rwxrwxrwx 1 hmiser hmiser 875472 nov. 6 2012 msvcr110.dll -rwxrwxrwx 1 hmiser hmiser 917184 oct. 5 2017 ucrtbase.dll -rwxrwxrwx 1 hmiser hmiser 83784 oct. 5 2017 vcruntime140.dll
The “dependentlibs.list” was curious and it only contained the name of the above mentionned DLL:
$>cat dependentlibs.list data.dll
A few Google searches later, we understood that Firefox was used to load the malicious DLL by exploiting a bug in an old version. Indeed, by adding a DLL name in the “dependentlibs.list” firefox would load the malicious DLL. So our actor leveraged that bug to execute his code, but to do that he had to embed that version of Firefox in his package, weird.
Anyway there was a good news, from a gigantic list of files we reduced the analysis to a single DLL. So we dug into it. In 2018 Ghidra was not published yet and I mostly used IDA (Free version) but now we have a great free and open source tool with a decompiler so let’s analyze one payload again with that toolset.
Analyzing that DLL is not really difficult with Ghidra. There is only one function exported (the main entry point) and it starts by creating a thread. On the screenshot we can see the code executed by the thread is at
LAB_10001030, third parameter of the CreateThread function according to the documentation.
The thread code starts by checking a few locales (UI language and keyboard mapping), to ensure the malware is running in a French environment (it crashes otherwise by moving
0xdeadbeef into the void). So our threat actor was clearly aiming at French people and banks.
Then an interesting loop is easily spottable. It’s a rolling xor with a random generated key on a blob of data stored in the DLL. With the right Cyberchef recipe it’s easy to understand what’s happening there: the output starts by
MZ and you can see the famous
This program cannot be run in DOS mode., it’s a PE.
So the DLL is unxoring another embedded DLL, loading it into memory and executing it. So that first step is just a loader with a few checks to ensure the victim is French. We definitely need to go deeper in that level 2 DLL and to do that you can extract it easily by using Cyberchef’s “save output to file” function and import the output into Ghidra.
And there is the latest piece of the puzzle. That DLL is indeed the core of Tinynuke malware.
The entry point starts by a few functions calls, and the very first one is in charge of loading all the configuration and to resolve all the dynamically loaded imports.
Just by looking at the first lines we can see that it contains a gigantic list of weird strings (the function is too big for Ghidra’s decompiler), which are, after a short investigation, just xored strings.
And if you studied the original code published by the author, you probably remember it contained a subproject called “AutoEncrypt” which consisted in replacing every previously identified strings by a subcall of an unxor function with a random key and the xored version of the string.
This is exactly what we’re looking at and we can ensure it by using Cyberchef again to discover one of the two command and control server configured in that payload:
sepa[.]site (the other is
So now we have a better visibility on our threat actor’s toolkit. The technique explained in that part are still working for that actor, it almost never evolved.
Back in 2018, my goal was to protect my employer’s customers and French people from that threat. So my first goal was to identify command and control (C2) servers and to take them down.
Of course by “takedown” I mean “sending abuses” (well emails to abuse mailboxes…) to the companies involved in hosting the threat actor’s infrastructure: registrars and hosters. To do that we rely on those companies willingness to tackle threats which is, most of the time, uncertain.
The first payloads I’ve encountered discussed with an infrastructure hosted by a French company called Scaleway (called Online at that time) with Cloudflare as intermediary. Both of them were pretty serious to handle my abuse requests, but of course cybercriminals are resilient and persistent.
From the beginning, they used Namecheap to register domain names used to spread payloads and host C2, and contrary to Scaleway and Cloudflare, it was impossible make them move.
I guess after having confirmed that Namecheap was not really efficient at handling abuses, and after my first successfull takedown at Cloudflare and Scaleway, the crooks decided to move the hosting at Namecheap too.
Anyway, I was still motivated and, using my previously identified knowledges I started extracting C2s from the different payloads I received. As they started moving more swiftly with several campaigns per months I needed to get my hands on more payloads. I started with $dayjob telemetry but I needed to be more efficient.
You may not know that but Virustotal provides a professional access to hunt into the gigantic dataset of files people upload every day, most of the serious operational security teams leverage that access to hunt for adversaries toolsets or leaked data.
Associated with a friend researcher we started looking for interesting payloads:
- payloads with names like “facture” (invoice in French)
- payloads with interesting size (remember, they’re quite big)
- payloads built with innosetup
- payloads uploaded from France
Innosetup was not really helping there. It’s used for legitimate purposes and the builtin compression mechanism makes it harder to look for that’s inside the package, this led to a lot of false positive. But with a bit of tuning I was able to grab several samples uploaded by targeted people, extract Tinynuke DLL and find the C2.
Of course extracting C2 and trying to take them down was not the only interesting thing to do here, we also needed to grab and analyze malware’s config, and by config I mean injects.
Remember the banking trojan functionalities? A big part of the behavior is driven by the injects, so we definitely needed to get them to have more visibility on our actors' techniques and targets.
My investigation on the payloads didn’t give me an immediate hint on how to get my hands on Tinynuke’s injects, and I thought the best technique to get them was probably to mimick the malware’s protocol to grab them directly from the C2.
In 2018 I totally sucked at reversing (I still suck but just a bit less) and I was not really confident in reversing the whole protocol from the binary, but lucky for us we had access to Tinynuke’s source code including the panel written in PHP which is way more friendly to read than obfuscated x86 assembly.
Using that code I discovered that the protocol is quite simple and easy to mimick using a language like Python. The only thing you need is the C2 and the full URL to the main PHP script used as entry point, then the protocol can be described like this:
- the first step is to generate a bot ID, which could be a random 16 long hexadecimal ID
- then you call the C2 endpoint passing the ID in the query:
- the c2 will then serves you a “sha-1” sum which is basically the ID you gave but hashed
- then you just have to use that hash to xor commands you’ll pass to the c2 in a POST request
By analyzing the panel’s code it’s easy to understand that the command to get the injects is “injects|” xored with the above mentioned SHA-1. I decided to write a Python script to grab injects, you can find it on my github.
It’s always useful to have a look at injects. You often find side domains used to host ATS or complementary injects. Sometime you can find IBANs used to transfer money automatically, sometimes find a way to identify infected victims or other useful stuff like targeted urls or method used to take control of your customer’s bank account.
By monitoring them in time we were able to discover interesting stuff, but when the threat actor understood we were doing that he also decided to talk to me directly in them:
I discovered too that the version of Tinynuke used was different from the public version and new functionalities had been added. Indeed, keywords used in injects were not present in the public source code. This was interesting intel, it meant one of the crooks behind my campaigns was confident enough with Tinynuke’s code to update it.
I also discovered they quickly stopped using injects to use screen recording to grab customer’s credentials, combined to the very specific kind of customer’s they targeted that information permitted me to understand how they bypassed our security mechanism. And guess what, it was a well known risk accepted a few years ago by people working way too far from the battle lines.
By analyzing the protocol to grab injects I also discovered a mechanism used by the malware to update its engine. Indeed, using the same kind of request used to grab the injects, the c2 can also serve a DLL, the core DLL of Tinynuke.
Instead of sending a xored “injects” command I just had to send “bin|int32” command to grab X86 dll and “bin|” for the x64 version. My python script is also able to grab DLLs.
That mechanism, used to update code on bot’s side (to add functionalities or to avoid being detected by antiviruses), is a very interesting feature for a blue team analyst. Indeed, instead of looking for droppers, and spending a lot of time to extract the useful data from it, I could directly grab DLLs and extract C2s from them.
By monitoring DLLs served by the C2 I discovered that our beloved threat actor updated the binaries a few hours before running a new campaign, so I was able to predict his campaigns, extract potential new C2s and send abuses before the spam run. As most campaigns were prepared during the night, time was running against me, but with a bit of automation, I could have done something cool: a bot monitoring DLLs, looking for new C2 and able to send automatic abuses.
To achieve that, I used a nice toolset called Miasm written by CEA Sec, the cybersecurity team of the French Atomic Energy research center. This framework permits us to instrument the code present in binaries. I leveraged that functionality to unxor automatically the obfuscated strings in DLLs.
The code is published here and underlying mechanism is explained in the readme, but to be quick, it works by running the DLL in a “virtual machine” until the end of every unxor execution to grab the result directly in memory.
Binded to a bot extracting every new DLL pushed on the C2 I was able to identify every new C2 within seconds and send abuses right away.
I guess my harassement worked because at some time, Namecheap took down the whole infrastructure and Tinynuke disappeared for a while! But of course it came back with an efficient counter measure: a C2 hosted over Tor network.
A C2 hosted behind a .onion meant that it was nearly impossible for me to find where was the C2 hosted, except if a mistake in the config or a forgotten file leaked the actual IP address of the server, but I never found something like that.
Checkmate, I lost that battle…
Before publishing that paper, I decided to write Ghidra scripts to help further analysis of Tinynuke. You’ll find here different Ghidra scripts:
- a script to extract embedded xored DLL easily
- a script to unxor massively obfuscated strings
- a script to resolve dynamically imported functions
These tools can help if you want to dig into Tinynuke’s code in the different samples that were used to target France.
Complaints, arrests and aftermath
That cat and mouse play was interesting but to be honest, it was clearly inefficient. Several people / companies were defrauded every week, and almost every big French bank were concerned by that. At $dayjob we worked with the e-fraud team to limit the impacts but it was almost impossible to prevent every suspicious transactions. Changing the security mechanism exploited by threat actors was “impossible” and updating counter measures in a tool written by a third party was too slow to react swiftly. The update finally came after the threat actor stopped his business.
In the meantime I used my Twitter account to raise awareness, and talk about that threat targeting my country. I tweeted every new campaign with details about the identified C2s and injects. It was probably useless most of the time but I was told a few people detected infected computers and cleaned them with that poorly shared intel.
But by talking about Tinynuke I was also spotted by the threat actors, who started talking to me, in public or in direct messages, using compromised accounts or in the malware’s inject as explained above. It was not the first time they talked to me, a few weeks before that, I was surprised by my VM suddenly playing Gradur, a .txt file was waiting on the desktop with a link to an online chat:
I won’t spend too much time on that period but I started receiving explicits threats like pics of me with bullets, they also signed a spear phishing campaign with my real name or used a payload delivery domain with my familiy name… It was kinda weird, someday they were aggressive, someday they wanted to be my friend. I guess I was probably discussing with several people, or an unstable guy or both? I know have the answer to that question.
Anyway, after several months, I finally filed two complaints, one in my own name for the different threats I received, and one in the name of my employer. The second one was the hardest part as it took months to be authorized. I also spent a lot of time to motivate others banks to follow us, but my manager and I were the only one who spent hours explaining the complex mechanisms of a banking trojan driven fraud scheme to law enforcements. As I said before, most banks prefer to remain silent about the frauds they’re victim of.
The complaints were filed against an “unknown perpetrator” but I was convinced the original author of Tinynuke was involved in that business for several reasons:
- the quick and constant evolution of tinynuke code
- the different information collected in the exchanges we had with them
- an ego fight between threat actors in 2018 leading to the publication of his passeport on Twitter
At the end of 2018, Tinynuke disappeared and in 2019 a new malware called Varenyky with spambot like functionalities appeared. It was obvious to me the code style had similarities with Tinynuke and I was also convinced Augustin took part in the creation of that malware.
They used it to propagate porn related blackmail (with ransom paid in cryptocurrencies, remember the money laundering problem?) massively, probably too massively. I understood they frightened people they should have avoided, forcing law enforcements to act pretty quickly.
A few months later, a young boy called Jordan R. was arrested at Paris airport. He had been identified by following the ransom money using the Bitcoin blockchain.
Later, a version of Varenyky appeared with Tinynuke functionalities embedded, it was a perfect mashup of both worlds. From different exchanges I had with him I understood Augustin was traveling easily using real fake Romanian identification papers in the name of “Emil P.”. He was free to go everywhere without being arrested. Everything was about to start again but, without any warning, during the night between 4th and 5th of December 2019, Augustin surrendered in Paris 15th district precinct.
I never understood that unexpected move, a few days before he was still bragging on Twitter. He was probably tired to see his father, who lived in France, being put several times in custody by law enforcements. The 5th of December 2019 was also the first day of Botconf, a conference about botnets fighting, I don’t know if it was related as he mentioned that conference several times in the messages he sent me.
Augustin spent a few months imprisoned and was set free (under legal supervision) during COVID-19 crisis.
While he was in jail, Tinynuke reappeared to target France again. The version used was clearly not the one that was published at the very beginning but the latest version I’ve seen used against France in 2018 with a C2 over tor, a version I thought only Augustin had.
I guess that means not all the crooks behind that business have been arrested. The future we’ll tell us… At the time I write those lines, they are still operating the botnet using
fizi4aqe7hpsts3r[.]onion as C2.
P.S.: After having published my analysis toolset, the latest Tinynuke campaign sent against France evolved. The technique used to pack the first level DLL stored in the innosetup installer changed. At the time I write those lines, I’m still analysing the evolutions.