Ramnit – in-depth analysis

Date of publication: 29/09/2017, Michał Praszmo

If we look on Ramnit’s history, it’s hard to exactly pin down which malware family it actually belongs to. One thing is certain, it’s not a new threat. It emerged in 2010, transferred by removable drives within infected executables and HTML files.

A year later, a more dangerous version was released. It contained a part of recently leaked Zeus source code, which allowed Ramnit to become a banking trojan.

These days, it has become much more sophisticated by utilizing a number of malicious activities including:

    • Performing Man-in-the-Browser attacks
    • Stealing FTP credentials and browser cookies
    • Using DGA (Domain Generation Algorithm) to find the C&C (Command and Control) server
    • Using privilege escalation
    • Adding AV exceptions
    • Uploading screenshots of sensitive information

    Despite Europol’s shut down of 300 C&C servers in 2015, it’s still going strong, recently being distributed by RIG EK via seamless gates.

    Executable’s analysis

    The main binary is packed like a matryoshka – a custom packing method first and then UPX.


    Despite being encrypted, extracting the binary from the packer is pretty straight-forward – all one needs to do is to set a breakpoint right after the binary decrypts the code and before it jumps into it. breakpoint1.png

    And if we now navigate to the newly unpacked code section we’ll find the binary right after the loader assembly:


    The unpacked binary (after UPX decompression) consists of 3 general functions:

    • ApplyExploit
    • CheckBypassed
    • start


    If the current user is not already an admin and the process is not running with admin privileges it tries to perform privilege escalation.

    Malware contains exploits for CVE-2013-3660 (patched in MS13-053) and CVE-2014-4113 (patched in MS14-058) vulnerabilities, however before it actually tries to run the payload, registry checks are performed to make sure that the host system is indeed vulnerable to said CVEs:

    If the exploits succeed or the program is already running with high privileges, a “TRUE” value is stored in a hardcoded random-looking registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\jfghdug_ooetvtgk, which is later used in the CheckBypassed function.


    This function checks if previously mentioned registry key is set. If not and process has admin privileges, updates it. Assuming the exploit has worked, Ramnit then adds registry keys to evade Windows’ security systems detection (see Obfuscation/Evasion):

    start routine

    The routine coordinates ApplyExploit and CheckBypassed – if they both run successfully it creates two svchost.exe processes and writes rmnsoft.dll and modules.dll into them respectively.

    Important detail: the binary executes CheckBypassed before ApplyExploit, so the binary has to be executed again in order to make any further progress. This trick outsmarts many single-run malware analysis systems, such as Cuckoo.


    Static config

    Ramnit encrypts its network communication using RC4 algorithm. Key for RC4 and botnet name are encrypted using xor with a hardcoded password.

    XOR encryption is pretty standard, the only catch is that it skips key’s first char and then reverses the key.

    XOR function calls:

    Ciphertext lengths are almost always too long and we have to rely on null termination:

    DGA config seems to be always declared at the beginning of the data section: datasection.png


    Program copies itself into C:\Users\User\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\.


    Ramnit generates a list of domains by using a LCG algorithm with a hardcoded seed:

    Generating a domain:


    DGA recreated in Python:


    Ramnit connects to C&C servers through port 443, but don’t let that fool you – it doesn’t use HTTPS, but its own protocol instead:

    Packet’s structure:

    Chunks’ structures:

    So if we’d like to send a packet containing some data, we would:

    • encrypt large (>4bytes) chunk data using RC4 with a key recovered from the XOR decryption
    • create packed chunks from data parts
    • concatenate all chunks together
    • wrap the output in packet layer

    Traffic example:


    Some of available commands:

    Command Byte Value Short Description
    COMMAND_OK 0x01 Server’s response that the command executed successfully
    GET_DNSCHANGER 0x11 Get DNS-changer payload
    GET_INJECTS 0x13 Get webinjects
    UPLOAD_COOKIES 0x15 Upload stolen cookies (zip format)
    GET_MODULE 0x21 Get a specific module
    GET_MODULE_LIST 0x23 Get a list of downloadable modules
    VERIFY_HOST 0x51 Check if the host is able to send a signed message
    REGISTER_BOT 0xe2 Register bot (send two MD5s)
    UPLOAD_INFO_GET_COMMANDS 0xe8 Upload detailed machine info

    Bot registration

    When a bot wants to register itself it sends two encrypted md5 hashes, the data structure of which is following:

    Python code:

    If C&C responds with a success packet (00ff0100000001), malware follows up with a empty 0x51 command. Signature from the response is verified using a hardcoded public RSA key. If there is a mismatch – the execution stops.


    The program can request a list of modules and then download each one individually:

    Antivirus Trusted Module v2.0

    Adds exceptions to a fixed list of anti-virus software (AVG Anti-Virus, BitDefender, Avast, ESET NOD32 Antivirus, Norton AntiVirus)

    Chrome reinstall module (x64-x86) v0.1

    Uninstalls Google Chrome

    and installs it again:

    Cookie Grabber v0.2 (no mask)

    Steals cookies from various hardcoded locations and sends a zip with results to the C&C through rmnsoft.dll.


    Used for performing Man-in-the-Browser attacks and hooking HTTP functions.


    Webinjects are a relatively new addition to Ramnit. They utilize a standard Zeus format:

    Obfuscation / Evasion

    Ramnit attempts to hide itself from Windows Defender by adding following registry values:

    ‘NOPs’ are inserted in random functions, which makes them difficult to find using e.g. Yara rule:


    New variant

    During writing of this article we’ve noticed a variation of Ramnit called clickbideu in an Italian spam campaign.

    Its loader is completely different, but the communication module (rmnsoft.dll) has remained somewhat unchanged with only some minor differences:

    DGA cycles between 3 hardcoded TLDs instead of just one:

    Python implementation:

    Also new version seems to be using different port – 8001, although we’ve also seen usage of port 442.

    Additionally, a different value (“fE4hNy1O”) is used for calculating the second md5.

    Additional links


    Yara rules:

    Samples analyzed:

    • Main PE

      • 92460d8ac1d1e9f155ef2ca6dd7abb417df8900a17e95157d4372a2c846e829f
    • rmnsoft.dll

      • be2044fe6f0220dde12c51677f2ef4c45d9dea669073bd052695584e573629e0
    • modules.fll

      • 96a10e07d092f6f429672ce2ca66528aae19de872bda39249135a82477d27a83
    • Module Antivirus Trusted Module v2.0 (AVG, Avast, Nod32, Norton, Bitdefender)

      • 975ed0f933d4a22ca631c5ab77c765cd46c48511d43326b066b4505c6dc911de
    • Module Cookie Grabber v0.2 (no mask)

      • bc977a0f455fc747a7868a7940aa98af10c91c4aae7598310de8b78132436bee
    • Module Hooker

      • a88151b3bf825e26ded28f94addeada095d2cd13791b2153a9594b26d9cfb85e


    Loader sha256:

    • d290225dde1b18bf68c4c42e06638a61fb336c91a2c4e6dd007bcbe7327fcbae
    • c2cae7d9ef91dfcc1ae8f542e0ac64ce66c526d5a4154241855020612d358ee8
    • 1f3fbca46a599b4f221ead7785606451365db45bbbc537ee0c4d019e8984d106
    • 9d723bb1dc375834ebb907271b83dffab44e98b82fa73da6267037f019e4bc83
    • f3567e2b5fc521987f0dd79aff6f3b1328db8e03fa825c3c030080a8b5819564
    • 7689465ba010537b0c29cf18d32a25962bd1605b717733f5953eb1b1eb0a68c9
    • f98ca50b7d07682ac359b97dd68eb924c4cbd825db72c1a132458e9bb765fa1e
    • 4b00b0ece480267af051e7907458381d8a9e8506c7da67b8a8e1d74d45773d68
    • 6ac47d82134385fa73386ff3cd7b2eb7008da2205b3f5af7b41fab45c63f9046
    • 6a1fc689d2ef32ee6288498f8a875c6dc880d7494f46c05d25d0e1f627984e8e
    • 522e935b91307b8c01e0ea8a724985f5b4e01227a761aeccb63b00f0d964f7e9
    • b3e67b5ee899c53f90c9da772592a4709372192542e1297bbce4929a8e1d5c69
    • 71d92cc6dc9273d162a969960b1021e5f18cf39b2c48043e5c5e49db5a58d955
    • da15c2a89334496910b6d966bf91fa25a1c9526c53796e06d166416abe7cf2f4
    • e4353bda9692581ea9743165dfd843238c23bb92e24b778983de80e90ac650a3

    DGA domains for analyzed configs:

Mole ransomware: analysis and decryptor

Date of publication: 30/05/2017, Jarosław Jedynak


Mole ransomware is almost month old ransomware (so it’s quite old from our point of view), that was distributed mainly through fake online Word docs. It’s a member of growing CryptoMix family, but encryption algorithm was completely changed (…again).

We became interested in this variant after victims contacted us asking for a decryptor. Remembering that all members of this family so far were plagued with serious crypto flaws, we decided to give it a try and reverse-engineered it thoroughly. It turned out to be a good idea – we were successful and managed to create working decryptor that you can download from:

In the rest of this article we will share detailed results of our research.

Campaign and Behaviour

Mole ransomware was distributed through malspam linking to fake Microsoft Word documents. Said documents prompted users to download and install a malicious plugin.

Because this variant is not new, it was analyzed by quite a lot of researchers before us. We don’t intend to copy their good work, so for anyone interested in the dynamic analysis we recommend looking at following links:

Instead, we’ll focus on a static analysis of the code and the encryption method.

Static analysis

As in many malware families, Mole won’t run in most Russian-speaking countries. Literally the first thing the binary does after being run is checking keyboard layout and charset – detecting Russian ones leads to immediate process termination. Otherwise, malware achieves persistence (by adding itself to the Autorun in the system’s registry), removes shadow copies (after Windows’ version check), and proceeds to the actual encryption:

After being started ransomware tries to bypass the UAC and displays fake dialog message:

access denied image

After that, UAC prompt is shown and the user probably clicks “Yes” believing that he/she agrees to “Display Color Calibration”. Instead, as usual, malware relaunches itself with admin privileges, and Shadow Volumes are deleted.

Of course ransomware doesn’t encrypt every file type. Interestingly, encrypted extensions are obfuscated – they were not hardcoded directly, but compared inside giant function, after transformation with following algorithm:

List of encrypted extensions:

And as usual, the most interesting thing in any ransomware is actual file encryption algorithm. In this case it can be summarized as follows (half-decompiled, half-handwritten pseudo-c++ code with non essential parts omitted):

Or in terse pseudocode:

This method is not perfect for a lot of reasons, but we’ll skip detailed cryptanalysis here.

General structure of encrypted file looks like this:

It’s very similar to Revenge ransomware, that is why we believe that Mole is next version of Revenge. On the other hand, RC4 is used here instead of more sophisticated (and stronger) AES. It doesn’t change much, as RC4 is still strong enough for most ransomware purposes, but we’re not sure why ransomware creators decided to take this step back.


Sha256 hashes of binaries:

Network communication:

Ransom note:

Analysis of Emotet v4

Date of publication: 24/05/2017, Paweł Srokosz


Emotet is a modular Trojan horse, which was firstly noticed in June 2014 by Trend Micro. This malware is related to other types like Geodo, Bugat or Dridex, which are attributed by researches to the same family.

Emotet was discovered as an advanced banker – it’s first campaign targeted clients of German and Austrian banks. Victims’ bank accounts were infiltrated by a web browser infection which intercept communication between webpage and bank servers. In such scenario, malware hooks specific routines to sniff network activity and steal information. This technique is typical for modern banking malware and is widely known as Man-in-the-Browser attack.

Next, modified release of Emotet banker (v2) has taken advantage of another technique – automation of stealing money from hijacked bank accounts using ATSs (Automated Transfer Systems, more informations on page 20 of CERT Polska Report 2013). This technology is also used in other bankers. Good examples are ISFB (Gozi) or Tinba.

At the beginning of April 2017, we observed wide malspam campaign in Poland, distributing fraudulent mails. E-mails were imitating delivery notifications from DHL logistics company and contained malicious link, which referred to brand-new, unknown variant of Emotet.

Malware distributed in this campaign differed from previously known versions. Behavior and communication methods were similar, but malware used different encryption and we noticed significant changes in its code. Thus we called this modification version 4.

Read more

SECURE 2017 – Call for Speakers

Date of publication: 23/05/2017, przemek

Call for Speakers for SECURE 2017 is now open. If you have an interesting topic and would like to share your ideas with a crowd of Polish and international IT security specialists, please consider submitting your proposal. You will find all applicable information below.

SECURE 2017 will be held on October 24-25 in Warsaw, Poland. This annual conference is dedicated entirely to IT security and addressed to administrators, security team members and practitioners in this field. SECURE’s unique feature is the organisers’ commitment to providing participants with reliable information about everything that is current and meaningful in IT security. A high professional level of the talks is ensured by CERT Polska during the paper selection process. Particular emphasis is on practical solutions, analysis of the current threats, latest trends in countering threats as well as important legal issues. Participants have an opportunity to gain the latest knowledge, improve their qualifications and exchange experience with experts.

Recent months proved that exploit kits are still among current threats, not only to individual users as part of opportunistic campaigns, but also targeting large enterprises and national infrastructure when used in water hole attacks. IoT devices have also become attackers’ tool of choice as they are often insufficiently protected. New ransomware families and popularization of ransomware-as-a-service put individuals as well as businesses, industrial systems and embedded devices. How to fight these threats, as well as all those that are still difficult to detect due to their well thought out and targeted nature? We will be looking for answers to these and many more questions during SECURE 2017.

If you want to share your experience in these topics, or if you are an expert in one of the areas below, this Call for Speakers is for you.

SECURE 2017 will be held on October 24-25 at Airport Hotel Okęcie in Warsaw, Poland.
The conference topics will be roughly grouped in the following tracks:

    • technical – practical aspects of implementation and integration of security solutions
    • organisational – new trends in attacks, threats and their mitigation
    • legal

Presentation topics

We are looking for speakers willing to deliver a talk covering one or more of the following subjects:

    • malware evolution and analysis, including viruses, worms and botnets
    • intrusion detection
    • innovatory honeypot and sandbox applications
    • Advanced Persistent Threat attacks
    • monitoring of network threats
    • security of smartphones and other mobile systems
    • security events visualisation
    • security of SCADA/ICS
    • security of IPv6
    • cloud security
    • early warning against network threats
    • incident handling
    • standards for security incident data exchange
    • DDoS attacks and their mitigation
    • efficiency of methods for mitigation of new attack vectors
    • open source security tools
    • protection of online identity
    • privacy, confidentiality and anonymity
    • steganography
    • Polish and European law in regards to computer and information security
    • law enforcement actions in regards to cybercrime mitigation
    • research projects in the area of computer and information security
    • securing the human

Important facts

    • proposals for presentations must be submitted only via EasyChair:
    • proposals should include at least a title, short abstract, name and bio of the speaker
    • any questions regarding the submission and selection process should be directed to [email protected]
    • time for presentation: 45 minutes, including Q&A
    • commercial presentations will not be accepted
    • all materials should be submitted in one of the following formats: OpenOffice, Microsoft Office, PDF
    • slides of presentations will be made available to all participants in an electronic version unless strictly prohibited by the speaker
    • authors of accepted proposals will receive full conference package (workshops not inclusive), but they are responsible for their travel and accommodation

Important dates

    • Proposals submission until: July 10, 2017 Extended: July 17, 2017
    • Acceptance notice by: August 7, 2017
    • Presentation submission by: October 9, 2017

Lightning talks

We encourage participants of SECURE to share their thoughts. One of the conference blocks will include lightning talks, allowing everyone to talk briefly about their projects, works, ideas or problems. Everything goes, as long as it touches IT security.

Important facts about lightning talks

    • maximum time for a talk is 5 minutes and total time for all talks will be limited
    • application for a lightning talk can be submitted at any time after you have registered for the conference or during the conference
    • the organisers reserve the right to accept or refuse any lightning talk application

We are joining the No More Ransom Project

Date of publication: 11/04/2017, piotrb

No More Ransom logo
From the beginning of April we are officially an Associate Partner of the No More Ransom Project. Its main goal is to fight ransomware by helping victims with free decryption of their files. It is coordinated, among others, by Europol, and it connects law enforcement agencies and private sector companies from around the world. Our main contribution is providing a decryption tool for Cryptomix, Cryptfile2 and Cryptoshield ransomware families, which we described some time ago.

The project already helped more than 10000 victims and now we can also contribute to this effort. We are proud to take part in this initiative.

Sage 2.0 analysis

Date of publication: 14/02/2017, Jarosław Jedynak



Sage is a new ransomware family, a variant of CryLocker. Currently it’s distributed by the same actors that are usually distributing Cerber, Locky and Spora.

In this case malspam is the infection vector. Emails from the campaign contain only malicious zip file without any text. Inside zip attachment there is malicious Word document with macro that downloads and installs ransomware.

After starting the ransomware, Windows UAC window is shown repeatedly until the user clicks yes.

At the end the encryption process is started and all files are encrypted:

Ransom message directs us to panel in the Tor network, but before we can log in we have to solve a captcha:

And finally we are greeted with “user-friendly” panel:

We can even chat with malware creators:

Interestingly, this ransomware doesn’t remove itself after encryption, but copies itself to %APPDATA%\Roaming directory and re-encrypts all files after every reboot (until the ransom is paid).

Technical analysis

After this short introduction, We’ll focus on the technical side (because Sage 2.0 is not completely a generic ransomware, few things are rather novel).

Main function of binary looks like this:

As we see, there is a lot of fingerprinting and checks, though most of them are quite standard. More interesting features include:

Debug switch

Probably something didn’t work on the first try, so there is a debug command line parameter to test that configuration data is set correctly:

And surely enough, this debug parameter does what it should:

Someone probably forgot to remove this from the final version, because this is clearly a debugging feature.

Locale Check

Sage 2.0 creators like some nations more than others:

This checks user keyboard layouts:

    • next == 0x23 -> Belarussian
    • next == 0x3F -> Kazakh
    • next == 0x19 -> Russian
    • next == 0x22 -> Ukrainian
    • next == 0x43 -> Uzbek
    • next == 0x85 -> Sakha

We’re a bit disappointed that Polish didn’t make it on the exception list (If Sage creators are reading this: our locale is 0x15).

Location fingerprinting

Sage is trying to get it’s host location by querying with current SSID and MAC:

Canary file

Before encryption Sage checks for existence of a special debug file:

Thanks to this, malware creators don’t have to worry about accidentally running the executable and encrypting their own files.

Finally, if the file is not found, encryption is initiated.

Extension whitelist

Of course, not every file is encrypted – only files with whitelisted extension are touched:


As usual, this is the most interesting thing in ransomware code. Sage 2.0 is especially unusual because it encrypts files with elliptic curve cryptography.

The curve used for encryption is y^2 = x^3 + 486662x^x + x over the prime field defined by 2^255 – 19, with base point x=9. These values are not arbitrary – this curve is also called Curve25519 and is the state of the art in modern cryptography. Not only it’s one of the fastest ECC curves, it’s also less vulnerable to weak RNG, designed with side-channel attacks in mind, avoids many potential implementation pitfalls, and (probably) not backdoored by any three-letter agency.

Curve25519 is used with hardcoded public key for shared secret generation. The exact code looks like this (with structures and function names by us):

This looks like properly implemented Elliptic Curve Diffie-Hellman (ECDH) protocol, but without private keys saved anywhere (they are useful only for decryption and malicious actors can create them anyway using their private key).

This may look complicated, but almost all those functions are just wrappers for ECC primitive – named CurveEncrypt by us. For example, computing matching public key is curve25519(secretKey, basePoint) – where basePoint is equal to 9 (one 9 and 31 zeroes).

Shared key computation is very similar, but instead of using constant base point we use public key:

Due to the design of Curve25519, converting between any sequence of random bytes and a secret key is very easy – it’s enough to mask few bits:

And, also because of this, secret key generation is completely trivial (it’s enough to generate 32 random bytes and convert them to the secret key):

That’s all for the key generation. What about file encryption? Files are encrypted with ChaCha (unconventional algorithm, again) and key is appended to output file – but after being encrypted with Curve25519:

AppendFileKeyInfo fucntion appends sharedKey and pubKey to the file:

ChaCha is not very popular algorithm among ransomware creators. It’s very closely related to Salsa20 which was used in Petya ransomware. We don’t know why AES is not good enough for Sage – probably it’s only trying to be different.

In other words, there are two sets of keys + one key pair for every encrypted file:

After ransomware finishes we know only my_public, sh_public, fl_shared, but we need chachakey to actually decrypt the file.

This encryption scheme is quite solid because it makes offline encryption possible – there is no need to bother connecting with C&C and negotiating encryption keys – the public key is hardcoded in binary and because of asymmetric cryptography decryption is impossible. Assuming that malware creators didn’t make any drastic implementation mistakes (and we have no reason to suspect that they did), recovery of encrypted files is impossible. Of course, it’s always possible that master encryption key will eventually be leaked or released.

Additional information

Yara rules:

Hashes (sha256):

    • sample 1, 362baeb80b854c201c4e7a1cfd3332fd58201e845f6aebe7def05ff0e00bf339
    • sample 2, 3b4e0460d4a5d876e7e64bb706f7fdbbc6934e2dea7fa06e34ce01de8b78934c
    • sample 3, ccd6a495dfb2c5e26cd65e34c9569615428801e01fd89ead8d5ce1e70c680850
    • sample 4, 8a0a191d055b4b4dd15c66bfb9df223b384abb75d4bb438594231788fb556bc2
    • sample 5, 0ecf3617c1d3313fdb41729c95215c4d2575b4b11666c1e9341f149d02405c05

Additional information:

Nymaim revisited

Date of publication: 30/01/2017, Jarosław Jedynak



Nymaim was discovered in 2013. At that time it was only a dropper used to distribute TorrentLocker. In February 2016 it became popular again after incorporating leaked ISFB code, dubbed Goznym. This incarnation of Nymaim was interesting for us because it gained banking capabilities and became a serious threat in Poland. Because of this, we researched it in depth and we were able to track Nymaim activities since then.

However a lot of things have changed during the last two months. Most notably, Avalanche fast-flux network (which was central to Nymaim operations) was taken down and that struck a serious blow to Nymaim activity. For two weeks everything went silent and even today Nymaim is a shadow of its former self. Although it’s still active in Germany (with new injects), we haven’t observed any serious recent activity in Poland.


This topic is really well researched by other teams, but it’s still interesting enough to be worth mentioning. Nymaim is heavily obfuscated with a custom obfuscator – to the point that analysis is almost impossible. For example typical code after obfuscation looks like this:

But with some effort we can make sense of it. There are a lot of obfuscation techniques used, so we’ll cover them one by one:

First of all, registers are usually not pushed directly onto the stack, but helper function “push_cpu_register” is used. For example push_cpu_register(0x53) is equivalent to pushing ebx and push_cpu_register(0x50) is equivalent to pushing eax. Constants are not always the same, but registers are always in the same order (standard x86 ordering).

. register constant
0 eax 0x50
1 ecx 0x51
2 ebx 0x52
3 edx 0x53
4 esp 0x54
5 ebp 0x55
6 esi 0x56
7 edi 0x57

Additionally, most constants in code gets obfuscated too – for example mov eax, 25 can be changed to:

The constant used in the example is 8CBFB5DA, but there’s nothing special about it – it’s a random dword value, generated just for the purpose of obfuscating this constant. The only thing that matters is the result of the operation (0x25 in this case).

Additionally there other similar obfuscating functions are used sometimes – for example sub_*_from_eax and add_*_to_eax.

Last but not least, the control flow is heavily obfuscated. There are a lot of control flow obfuscation methods used, but all boil down to simple transformation – call X and jmp X are transformed to at least two pushes. This obfuscation is in fact very similar to previous one – instead of jumping to 0x42424242, malware calls function detour with two parameters: 0x40404040 and 0x02020202. The detour adds it’s parameters and jumps to the result. In pseudoasm instead of:

we have:

There exists also a slight variation of this method – instead of pushing two constants, sometimes only one constant is pushed and machine code after a call opcode is used instead of a second constant (detour uses return address as a pointer to the second constant).

To sum up, previously pasted obfuscated code should be read like this:

With this in mind, we created our own deobfuscator. This was quite a long time ago and since then other solutions have shown up. Our deobfuscator probably isn’t the best, but is easily modifiable for our needs and it has some unique (as far as we know) features that we need, for example it imports recovery and decrypting encrypted strings stored in binary. Other deobfuscators include mynaim and ida-patchwork Nevertheless, with our deobfuscator we are able to untangle that messy code to something manageable:

When it comes to Nymaim obfuscation capabilities it’s not nearly over. For example external functions are not called directly, instead of it an elaborate wrapper is used:

This wrapper pushes hash of function name on the stack and jumps to the next dispatcher (even though call opcode is used, this code never returns here):

A second dispatcher pushes hash of a dll name on the stack and jumps to the helper function:

And finally real dispatcher is executed:

Additionally, real return address from API is obfuscated – return address is set to call ebx somewhere in the ntdll (real return address is somewhere in ebx by then, of course). Most tools are very confused by it. Let’s just say, it’s very frustrating when debugging and/or single stepping.

But wait, there’s more! As we have seen, short constants are obfuscated with simple mathematical operations, but what about longer constants, for example strings? Fear not, malware authors have a solution for that too. Almost every constant used in the program is stored in a special data section. When Nymaim needs to use one of that constants, it is using special encrypted_memcpy function. At heart it is not very complicated:

Inner workings of memcpy_and_decrypt are not that complicated either. Our reimplementation of the encryption algorithm in Python is only few lines long:

We only need to extract constants used for the encryption (they differ between executables) – they are hidden in these portions of code:

(These functions are not obfuscated, so extraction can be done with simple pattern matching).

But encryption of every constant was not good enough. Malware authors decided that they can do better than that – why don’t encrypt the code too? That’s not very often used, but few critical functions are stored encrypted and decrypted just before calling. Quite an unusual approach, that’s for sure. Ok, let’s leave obfuscation at that.

Static Configuration

After deobfuscation, the code is easier to analyze and we can get to interesting things. First of all, we’d like to extract static configuration from binaries, especially things like:

    • C&C addresses
    • DGA hashes
    • Encryption keys
    • Malware version
    • Other stuff needed for communication

How hard can that be? Turns out that harder than it looks – because this information is not just stored in the encrypted data section.

Fortunately, this time the encryption algorithm is rather simple.

We just need to point nymaim_config_crypt to the start of encrypted static config and everything will just work.

How do we know where static config starts? Well… We tried few clever approaches (matching code, etc), but they weren’t reliable enough for us. Finally, we solved this problem with a simplest possible solution – we just try every possible index in binary and try to decrypt from there. This may sound dumb (and it is), but with few trivial heuristics (static config won’t take 3 bytes of space, neither will it take 3 megabytes) this is quite fast – less than 1s on typical binary – and works every time.

Despite this, after decrypting static config we get a structure, which is is quite nice and easy to parse. It consists of multiple consecutive “chunks”, each with assigned type, length and data (for those familiar with file formats, this is something very similar to PNG, or wav, or any other RIFF).

Graphically this looks like this:

And chunks are laid consecutive in static config block:

So we can quickly traverse through all chunks of a static config with a simple five-liner:

Snippet from process_chunk (hash == chunk_type):

After initial parsing the static config looks like this:

static config example

(By the way, in this article chunk types are usually represented byte-order, i.e. big endian)

And in a more human readable form with most interesting chunks interpreted:

static config example

Infection timeline

There is more than one “kind” of Nymaims. As of now we distinguish between three kinds:

    • dropper – first Nymaim that gets executed on the system. This is the only type distributed directly to victims.
    • payload – module responsible for most of the “real work” – web injects for example
    • bot_peer – module responsible for P2P communication. It tries to become supernode in the botnet.

These are all one kind of malware and all of them share the same codebase, except few specialized functions. For example our static config extractor works on all of them, just like our deobfuscator and they all use the same network protocol.

Dropper role is simple. It performs few sanity checks – for example:

    • Makes sure that it’s not virtualized or incubated
    • Compares current date to “expiration time” from static config
    • Checks that DNS works as it should (by trying to resolve and

If something isn’t right, the dropper shuts down and the infection doesn’t happen.

The second check is especially annoying, because if you want to infect yourself Nymaim has to be really “fresh” – older executables won’t work. Even if you override check in the binary, this is also validated server-side and the payload won’t be downloaded.

If we want to connect to a Nymaim instance, we need to know the IP address of peer/C&C. Static config contains (among others) two interesting pieces of information:

    • DNS server (virtually always it’s and
    • C&C domain name (for example or

Nymaim is resolving that domain, but returned A records are not real C&C addresses – they are used in another algorithm to get a real IP address. We won’t reproduce that code here, but there is a great article from Talos on that topic. If someone is interested only in the DGA code, it can be found here:

When dropper obtains C&C address, it starts real communication. It downloads two important binaries and a lot more:

    • payload – banker module (responsible for web injects – passive member of botnet)
    • optional bot module (it is trying to open ports on a router and become an active part of a botnet. When it fails to do so, it removes itself from a system).
    • few additional malicious binaries (VNC, password stealers, etc – not very interesting for us).


Payload is very different from dropper when it comes to network communication:

    • No hardcoded domain
    • But has DGA
    • And P2P

The payload’s DGA algorithm is really simple – characters are generated one by one with simple pseudo-random function (variation of xorshift). Initial state of DGA depends only on seed (stored in static config) and the current date, so we can easily predict it for any given binary. Additionally, researchers from Talos have bruteforced valid seeds, simplifying the task of domain prediction even more.


First of all, few examples why we suspected from the start that there is something else besides DGA:

We have taken one of our binaries that hadn’t behaved like the payload, unpacked it, deobfuscated and reverse engineered it. But even without in-depth analysis, we’ve found a lot of hints that P2P may be happening. For example we can find strings typical for adding exception to Windows Firewall (and of course – that’s what malware did when executed on a real machine).

Another suspicious behavior is opening ports on a router with help of UPNP. Because of this, infected devices from around the world can connect to it directly.

And finally something even more outstanding. As we have seen, the malware presents itself as the Nginx in the “Server” header. Where does this header come from? Directly from the binary:

We implemented tracker for the botnet (more about that later) and with the data we obtained, we concluded that this probably is a single botnet, but with geolocated injects (for example Polish and US injects are very similar). Distribution of IPs we found is similar to what other researchers have determined (we have found more PL nodes and less US than others, but that’s probably because the botnet is geolocated and we were more focused on Poland).

49.9% (~7.5k) of found supernodes were in Poland, 30% (~4.5k) in Germany and 15.7% (~2.2k) in the US.

Network protocol

And now for something more technical. This is an example of a typical Nymaim request (P2P and C2 communication use the same protocol internally):

    • Host header is taken from the static config
    • Randomized POST variable name and path
    • POST variable value = encrypted request (base64 encoded)
    • User-Agent and rest of the headers are generated by WinHTTP (so headers are not very unique and it’s impossible to detect Nymaim network requests by using only them).

Typical response:

    • This isn’t really Nginx, just pretending.
    • Everything except the data section is hardcoded
    • Data = encrypted request

Encrypted messages have very specific format:

A lower nibble of the first byte is equal to a length of the salt and a lower nibble of the second byte is equal to the length of the padding. Everything between the salt and the padding is the encrypted message. To decrypt it, we need to concatenate the key with the salt – and use that password with the rc4 algorithm.

It can be easily decrypted using Python (but we had to reverse engineer that algorithm first):

After decrypting a message, we get something with a format very similar to the static config (i.e. a sequence of consecutive chunks):

Each chunk has its type, length and raw data:

We can process decrypted message with almost exactly the same code as code for static config:

And this is the basic code used for parsing the message. Each chunk type needs to be processed a bit differently. Interestingly, parsing message is recursive, because some chunk types can contain other lists of chunks, which in turn can contain other lists of chunks, etc. Unfortunately, important chunks have another layer of encryption and compression. At the end of an encrypted chunk we can find special RSA encrypted (or rather – signed) header. After decryption (unsigning) of the header, we can recover a md5 hash and length of the decrypted data and most important of all – a Serpent cipher key used to encrypt the data.

After the decryption we will stumble upon another packing method – decrypted data is compressed with APLIB32. This structure is very similar to the one used by ISFB – firstly we have magic ‘ARCH’, then length of compressed data, length of uncompressed data and crc32 – all of them are dwords (4 bytes).

Again, it’s nothing Python can’t deal with. We quickly hacked this function to recover real data hidden underneath:

With this function we finally managed to hit the jackpot. We decrypted all of the interesting artifacts passed over the wire, most importantly additional downloaded binaries, web filters and injects.


An example request, after dissection, may look like this:

As we can see, quite a lot of things is passed around here. There are a lot of fingerprinting everywhere and some information about current state.

Responses are often more elaborate, but for the sake of presentation, let’s dissect a simple one:

An infected machine gets to know its public IP address, IP addresses (and listening ports) of its peers and the active domain. Additionally it is usually ordered to sleep for some time (usually 90 seconds when some files are pending to be transmitted and 280 seconds when nothing special happens).

Here is the list of types of chunks that we can parse and understand:

chunk hash short description
ffd5e56e fingerprint 1
014e2be0 fingerprint 2 + timestamps
f77006f9 fingerprint 3
22451ed7 crcs of last received chunks of type be8ec514 and 0282aa05
b873dfe0 probably “enabled” flag (can be only 1 or 0)
0c526e8b nested chunk (decrypt with nymaim_config_crypt, unpack with aplib, recursively repeat parsing)
875c2fbf plain (non-encrypted) executable
08750ec5 nested chunk (decrypt with nymaim_config_crypt, unpack with aplib, recursively repeat parsing)
1f5e1840 injects (decrypt with serpent, unpack with aplib, parse ISFB binary format)
76daea91 dropper handshake (marker, without data)
be8ec514 list of peer IPs
138bee04 list of peer IPs
1a701ad9 encrypted binary (decrypt with serpent, unpack with aplib, save)
30f01ee5 encrypted binary (decrypt with serpent, unpack with aplib, save)
3bbc6128 encrypted binary (decrypt with serpent, unpack with aplib, save)
39bc61ae encrypted binary (decrypt with serpent, unpack with aplib, save)
261dc56c encrypted binary (decrypt with serpent, unpack with aplib, save)
a01fc56c encrypted binary (decrypt with serpent, unpack with aplib, save)
76fbf55a padding
cae9ea25 nested chunk (decrypt with nymaim_config_crypt, unpack with aplib, recursively repeat parsing)
0282aa05 nested chunk (decrypt with nymaim_config_crypt, unpack with aplib, recursively repeat parsing)
d2bf6f4a state informations
41f2e735 web filters
1ec0a948 web filters
18c0a95e web filters
3d717c2e web filters
8de8f7e6 datetime (purpose is unknown, it’s always few days ahead of current date)
3e5a221c list of additional binaries that was downloaded
5babb165 payload handshake (marker, without data)
b84216c7 public IP of infected machine
cb0e30c4 number of seconds to sleep
f31cc18f additional CRC32s of downloaded binaries
920f2f0c injects (decrypt with serpent, unpack with aplib, parse ISFB binary format)
930f2f0c injects (decrypt with serpent, unpack with aplib, parse ISFB binary format)

This may seem like a lot, but there are a lot of things we didn’t try to understand (we ignored most of dword-sized or always-zero chunks).

After extracting everything from communication we can finally look at injects. For example Polish ones:

(304 different injects, as of today)

Or US injects:

(393 different injects, as of today)


Yara rules:

Hashes (md5):

    • Payload 2016-10-20, 9d6cb537d65240bbe417815243e56461, version 90032
    • Dropper 2016-10-20, a395c8475ad51459aeaf01166e333179, version 80018
    • Payload 2016-10-05, 744d184bf8ea92270f77c6b2eea28896, version 90019
    • Payload 2016-10-04, 6b31500ddd7a55a8882ebac03d731a3e, version 90012
    • Dropper 2016-04-12, cb3d058a78196e5c80a8ec83a73c2a79, version 80017
    • Dropper 2016-04-09, 8a9ae9f4c96c2409137cc361fc5740e9, version 80016

Repository with our tools: nymaim-tools

Other research

Evil: A poor man’s ransomware in JavaScript

Date of publication: 18/01/2017, Jarosław Jedynak



Initially Evil was brought to our attention by an incident reported on 2017-01-08. By that time the Internet was completely silent on that threat and we had nothing to analyze.

We found first working sample day later, on 2017-01-09. In this article we will shortly summarize our analysis and conclusions. Since then, we had relatively high number of infections reported, so we predict that this family of ransomware may become a bigger threat in near future.

This malware follows recent trend, and doesn’t have any decryption panel (like CryptoMix) – instead of this, an email address is provided. Sure, why complicate things if simple solutions work good enough.

Read more

Technical analysis of CryptoMix/CryptFile2 ransomware

Date of publication: 04/01/2017, Jarosław Jedynak

skull and crossbones malware


CryptoMix is another ransomware family that is trying to earn money by encrypting victims files and coercing them into paying the ransom.
Until recently it was more known as CryptFile2, but for reasons unknown to us it was rebranded and now it’s called CryptoMix.
It was observed in the wild being served by the Rig-V exploit kit.

This malware stands out from among others, but not necessarily in a good way.


First unusual thing about this family is very large amount of money requested – 5 bitcoins is an insane amount of money (especially considering that CryptoMix is really primitive under the hood, but we’ll get to it). We don’t know how many victims have paid, but probably few were desperate enough.

Additionally we have stumbled upon following comment discouraging anyone from paying the ransom:

we were infected and they asked for 10 bitcoins, after some negotiations the price was lowered to 6 bitcoins. they provided 1 decrypted file to prove concept. we paid 6 bitcoins and they asked for another .6 as the c&c server will not provide the key due to late payment. after promptly paying another .6 bitcoins (about $4800 in total) there has been no communication from them! its been 2 weeks and nothing.

We can’t verify if this is true, but it sounds plausible – if someone is desperate enough to pay 6 bitcoins for his files, he probably can be coerced into paying even more. As usual, we discourage anyone from supporting the criminals by paying the ransom.

Payment portal

Additionally CryptoMix doesn’t have any payment portal in the Tor network. Or any payment portal, for that matter – victim have to write an email and literally wait some time before malware operators kindly send the decryption keys
(assuming that they will do it, instead of bargaining for even more money).

For example, ransom message can look like this (most recent variant):

Or like this (older variant):

We don’t think that this strategy was well thought out. First of all, using emails for communication with victims is bothersome and need constant attention.
Automated portal would be much more reliable and secure for both sides. Additionally, emails are prone to being deleted/locked, effectively cutting malware authors from their “clients”.


Content of exchanged emails is very unusual too. Actors claim to be a charity organization (!) that is going to sponsor presents and medical help for children. For example:

That’s really original, but unfortunately also obviously false.

Technical analysis

Leaving aside strange quirks of ransomware “interface”, let’s get more technical. In its heart, CryptoMix is just a bare bones encryptor – it doesn’t have any fancy features, it doesn’t have a web portal, it doesn’t change user wallpaper, the only thing it does is encrypting every file on the victim’s disk and on the mounted network drives.

CryptoMix is protected by a very primitive packer – the real binary is stored in resources, and xored with a hardcoded key. For some reason, Cuckoo has problems with automatic unpacking of cryptomixer, so we had to write our own unpacker. Using pefile and Yara is very easy:

After decryption ransomware checks whether it’s being debugged – but no antiVM techniques are employed, so everything works as it should under VirtualBox.

Before file encryption starts, the ransomware checks internet connectivity (using InternetOpenUrl function). If everything is ok, an encryption key is generated on victim’s PC and sent to the C&C server.
Otherwise, depending on malware version, either a hardcoded encryption key is used or malware is spinning in an infinite loop until the internet connection is restored.

The main function can be expressed as follows:

After encryption key is generated/selected, it is stored in windows registry. Registry key used for malware specific data varies depending on version, but for example SoftWare\Microsoft\Windows\Shell\Nodes Slots, SoftWare\Microsoft\Windows\Shell\FlashPlayerPluginK or Software\Adobe Reader LicensionSoftWare\AdobeLicensionSoftWare can be used (malware probably tries to hide its presence by impersonating another software).

The list of supported extensions constains more than 1250 entries:

That’s quite a lot of extensions, but nothing special (for comparsion: CryptXXX supports 933 extensions, CrypMIC 901). Most unusual thing here is inclusion of another ransomware extensions (for example .zepto, .locky, .crypt, .locked, .cryptolocker, .cryptowall, etc).


Let’s get back to ransom message for a while:

Malware claims that our files are “encrypted with 2048bit RSA KEY”. Well, it’s not entirely true. Yes, 2048bit RSA key is generated with windows Crypto API – but after RSA key is selected, it is hashed with SHA256 to create a real encryption key and every file on disk is encrypted with that key. Encryption algorithm used is AES 256 in CBC mode without initialization vector.

Encryption routine can be summarized with this (simplified) code:

This function is called for every file, so hashing rsaKey and deriving AES key every time doesn’t make much sense. But there is bigger problem with it – there is no need for such things as “public” and “private” keys, because this encryption routine is entirely symmetric – RSA serves here just as (unnecesarily slow) random number generator.

So yes, in a way RSA is “used for encryption”, but files are not encrypted with RSA and encryption is entirely symmetric.

UserID given by CryptoMix is not random – it is generated from username and serial number for first disk.

This doesn’t seem like a good idea, because UserIDs absolutely have to be unique, and neither username nor volume serial number is designed to be unique – so userID collisions are possible and very plausible (after taking low entropy of userID and birthday paradox into account).
Why is this a problem? Because when UserID collision happens, malware creators have no way of distinguishing two users apart – so they don’t know which encryption key belongs to which user, and can’t send the right one. It’s also possible that in case of collision old key will be overwritten in database and lost.

Finally, CryptoMix achieves persistence by copying itself to user documents and writing to HKEY_CURRENT_USER\SoftWare\Microsoft\Windows\CurrentVersion\Run registry key.

As a final measure, all shadow copies are removed (if user doesn’t have admin account, UAC window is shown before):

Cryptomix Decryptor

Due to a cryptographic flaw in encryption, we are able to decrypt CryptoMix (and CryptFile2), but only sometimes and only if files were encrypted with a vulnerable version.
If your files were encrypted by CryptoMix and you don’t want to pay a ransom, you can contact us at [email protected] and we’ll see what we can do.
Please attach a single encrypted file without changing it’s filename after encryption (for example[[email protected]]id[7e5973f5e0ce337d].lesli).


Cryptomix packer (old and new):

Cryptomix payload (after unpacking):


c2f30cd537c79b6bcd292e6824ea874e sample0

befc0e43b38fe467ddc3ddd73150a9fc sample0 decrypted

8c413e31f39a54abf78c3585444051f7 sample1

0d1206246bf15c521474cee42f13fc09 sample1 decrypted

b778bda5b97228c6e362c9c4ae004a19 sample2

042a38a32cd20e3e190bb15b085b430a sample2 decrypted

Tofsee – modular spambot

Date of publication: 16/09/2016, Adam Krasuski

Tofsee, also known as Gheg, is another botnet analyzed by CERT Polska. Its main job is to send spam, but it is able to do other tasks as well. It is possible thanks to the modular design of this malware – it consists of the main binary (the one user downloads and infects with), which later downloads several additional modules from the C2 server – they modify code by overwriting some of the called functions with their own. An example of some actions these modules perform is spreading by posting click-bait messages on Facebook and VKontakte (Russian social network).

Read more