What is Lynx Ransomware?

Lynx ransomware is a “fork” of another popular ransomware called “INC”, its source code was supposedly sold on an onion site at some point in the last year. This “fork” is largely based on it. Lynx uses some strong encryption (AES-128), good secret handling (Curve22519), and an efficient IO pipeline for fast encryption of files.However I believe certain features of this ransomware have been falsified (double extortion), as well as what I believe is the likely infection chain of this ransomware. This is all based on a couple of samples that I have reversed, and at least for the double extortion feature, it is not there, and has no imports that would enable this directly.
In recent months it has gotten quite “popular”, and the group has supposedly ransomed several UK/USA/Aus companies.

Surface Level Analysis

Metadata from the original PeEXE:

It is a 32 bit binary:


SHA-256: 0315dbb793f855f154aa8d227151f1098bd9b580a4f85064648b85bac1321663

Compilation time: 2024-12-21 11:37:45 UTC

Processes Graph:

(any.run)

Initial Triage

Upon starting to reverse this malware, I soon realized that it was not packed or obfuscated in any way. This is pretty uncommon for most intermediate and up malware, however I think there is a reason for this that I will detail later.

Reversing Lynx Ransomware

I will structure the following analysis starting with what is basically the entry, “InitializeRansomwareOperation”, then reversing everything that it calls.

Command Prefixes

One of the first things that I noticed about this ransomware is that it appears to take in command prefixes.


This is an oddity. When using the –help command it gives you a nice manpage.

ArgumentDescription
–file <filePath>Encrypt only specified file(s)
–dir <dirPath>Encrypt only specified directory
–helpPrint this message
–verboseEnable verbosity
–stop-processesTry to stop processes via RestartManager
–encrypt-networkEncrypt network shares
–load-drivesLoad hidden drives
–hide-cmdHide console window
–no-backgroundDon’t change background image
–no-printDon’t print note on printers
–killKill processes/services
–safe-modeEnter safe-mode

This is pretty odd for ransomware or malware in general, as no victim would know to look for these when trying to use an ordinary .exe. This suggests to me that it is a post exploitation ransomware, but I’ll advance on this point later.

Depending on what prefixes are used they store a value in local_14 which accumulates flag states etc.

For an example of how the command prefixes work, if –kill is used a specific byte is stored into local_14.

It then enumerates these processes (using Process32FirstW/Process32NextW) and, for each process, uses an internal pointer to an array of string pointers. This array holds the target substrings (such as “veeam”, “backup”, “exchange”, etc.) to check against each process name. If a process name contains one of these substrings, that process is terminated.

Decoding The Ransomnote

After setting up sufficient buffer sizes and allocating memory the ransomware decrypts its base64 note(s).

After this it calls a function I’ve named “ReplaceIDInRansomNote”, which takes the %id% string from the decoded note and replaces it with a hardcoded victim ID.

This takes it from this:

To this:

Encryption of Files

After decrypting the ransomware note, it likely calculates the amount of threads or cores available for before spawning them in.

The ransomware uses the winAPI CreateIoCompletionPort to asynchronously ensure that the files are encrypted within multiple threads of a function I’ve named “ProcessEncryptionTasks”.

From there “ProcessEncryptionTasks” deals with IO and block level encryption, it has many switch and case statements that do different things in stages, for instance ReadFile:

Then it encrypts the files with case 1:

This appears to use a combination of AES-128 as well as potentially XOR to aid in additional obfuscation.

Case 2 then writes the file.

Case 3 appears to check if the file has a “LYNX” signature, if not it encrypts the file.

Cases 4-5 then cleans the metadata up.

File Handling and Path Building

After this other functions get called from the main “InitializeRansomwareOperation”.

It attempts to use the winAPI SHEmptyRecycleBinA to empty the recycle bin.

The ransomware then calls a function I’ve named “drivePathBuilder”

This function basically finds all drives on the system, even if they are hidden, and attempts to mount them.

It does this with these global variables and local_480, which given there are 26 of them and there are 26 letters in the alphabet, these are likely letters that it uses to enumerate all possible drives.

After this it attempts to mount drives found.

Delete Shadow Copy(s) (VSS)

Then another function gets called, “scanDrivesandDeleteShadowCopies”.

It attempts to find drives, check the drive type, open them, then delete shadow copies, all before encrypting them.

It calls a handler function (“EncryptionExclusionsThreadProc”) that calls “createRansomnoteExcludeFiles”.

Ransomnote Propagation, File Exclusions and File Encryption Processing

The function I have renamed “createRansomnoteExcludeFiles” is quite self descriptive, but it basically copies the decrypted ransomnote from a global variable and saves it to “README.txt” in every directory if not already written. It also deals with file exclusions and which files to encrypt.

First, if silent mode is disabled it populates the decoded ransomnote in every directory.

It then enumerates files and directories while skipping several hardcoded file types and directories..

It ends up skipping these file types/attributes:

  • .exe
  • .msi
  • .dll
  • .lynx
  • LYNX
  • README.txt

As well as sys files:

  • symlinksF
  • FILE_ATTRIBUTE_SYSTEM

It also skips these directories:

  • windows
  • program files
  • program files (x86)
  • $RECYCLE.BIN
  • appdata

Additionally it appears that it might specifically look for these DB engines:

Processing File Encryption

Then for files that pass the “filter” it waits until fewer than 100 encryption threads are running before creating file processing encryption thread(s) for the file(s).

This is where the files start to be processed for encryption. “FileEncryptionWorker” calls “SetupFileEncryption”.

This is the function that deals with the AES key expansion as well as deriving the session keys and secrets, using Curve25519.

“SetupFileEncryption” first uses the winAPI “GetFileAttributesW”, which gets the files attributes :P. It then removes the read only flag from all files processed by it with “SetFileAttributesW”. This makes sure that the files can be modified. It then uses the API “CreateFileW” to read the file.

If it cant read the file, it then optionally calls “TerminateResourcesUsingProcesses(RM)”, if –stop-processes is enabled, it then calls “TakeFileOwnershipAndSetPermissions”.

“TerminateResourcesUsingProcesses(RM)” uses the RestartManager APIs to shutdown processes unless they are critical.

“TakeFileOwnershipAndSetPermissions” attempts to allow the ransomware to take ownership of files, as well as changes the current user to be the file owner.

From there it checks to see if the file is empty with “GetFileSizeEx” before continuing.

Lynx then creates buffers for dealing with AES keys before decoding the attackers hardcoded base64 public key.

It logs almost everything it does, it makes reversing a bit easier:


From there it calls “derive_session_keys”, which acquires cryptographic context, before generating 32 random bytes for the AES session key.

“derive_session_key” then derives a shared secret using Curve25519, and in the context of being called by “SetupFileEncryption” it uses the attackers public key to compute the shared secret.

It then hashes the derived keys with the hashing algo SHA-256.

Back in “SetupFileEncryption” it uses “aes_key_expansion” to expand the AES session key into round keys for encryption.

It then appends “.” + “LYNX” to the original filename to mark it as encrypted.

From there it then ensures that the file’s time are left unaffected.

It then deals with the encryption task queue before exiting.

Post Encryption

Lynx then changes the users desktop background with “changeBackgroundimage”.

It takes the decoded ransomnote, saves it to temp as “background-image.jpg” and creates a desktop background with the text against a black background.

It then sets the wallpaper registry to point to the newly generated “background-image.jpg”.

Print Ransomnote

From there the ransomware attempts to print the ransom note to all printers before exiting. It uses the function “printRansomNote” to do this.

It is quite simple, it enumerates printers on the local network, and prints copies of the decoded ransomnote to all of them, but it excludes printers such as “Microsoft Print to PDF” and “Microsoft XPS Document Writer”.

Conclusion and Hypothesis

This ransomware is pretty advanced at what I think it is used for. Because it lacks any obfuscation, or packing techniques as well as having CLI prefixes, and lots of logging, in my mind it is very clear that this family is intended to be used as post-exploitation ransomware. It is possible to attempt to position this ransomware for user execution, but given the cmd.exe window that it shows by default, it is unlikely that it is intended to be used this way. The user is likely to instantly close the cmd.exe window, as most applications with differing levels of legitimacy usually never open it, so it would be a big red flag. Even more so that when closing the window in the first few seconds it completely kills the ransomware.

Additionally with it not using any obfuscation, packing, or other obfuscation and exploit techniques, most AV/EDR engines detect this ransomware at runtime, making it unfeasible to execute the ransomware unless you have already have access to the machine and have compromised its security.

“Double Extortion” Claim

Secondly from reports from authorities such as Paloalto Networks’ Unit 42 and sandboxes cataloging the ransomware, it is most likely falsely reported as double extortion ransomware, as none of the libraries used or files dropped have any internet capabilities. I have looked at 2-3 samples, even one Paloalto listed, and none of them have any sign of exfiltration capabilities.

Paloalto Networks – https://unit42.paloaltonetworks.com/inc-ransomware-rebrand-to-lynx/
— MIRROR — Lynx Ransomware: A Rebranding of INC Ransomware

It is also possible that it is delivered in the ways that they reference, but I personally doubt it is the main way it is intended to be used or delivered, and that it is far more likely it is post exploitation intended.

It is however a pretty flawless analysis other than this.

Indicators of Compromise (IoCs)

SHA-2560315dbb793f855f154aa8d227151f1098bd9b580a4f85064648b85bac1321663
URL(s)https[:]//lynxblog[.]net
https[:]//lynxblogco7r37jt7p5wrmfxzqze7ghxw6rihzkqc455qluacwotciyd[.]onion
https[:]//lynxblogijy4jfoblgix2klxmkbgee4leoeuge7qt4fpfkj4zbi2sjyd[.]onion
https[:]//lynxblogmx3rbiwg3rpj4nds25hjsnrwkpxt5gaznetfikz4gz2csyad[.]onion
https[:]//lynxblogoxllth4b46cfwlop5pfj4s7dyv37yuy7qn2ftan6gd72hsad[.]onion
https[:]//lynxblogtwatfsrwj3oatpejwxk5bngqcd5f7s26iskagfu7ouaomjad[.]onion
https[:]//lynxblogxstgzsarfyk2pvhdv45igghb4zmthnzmsipzeoduruz3xwqd[.]onion
https[:]//lynxblogxutufossaeawlij3j3uikaloll5ko6grzhkwdclrjngrfoid[.]onion
Attacker’s Base64 Encoded Public KeyGq70ERycjoW7SmLxAcnbWs+sCiFoaw+RMQ2VG3EvFho=
Hardcoded Victim ID67af691361c2d9c619f08a76
Base64 Encoded Ransom Note(s)WW91ciBkYXRhIGlzIHN0b2xlbiBhbmQgZW5jcnlwdGVkLg0KRG93bmxvYWQgVE9SIEJyb3dzZXIgdG8gY29udGFjdCB3aXRoIHVzLg0KDQpJRA0KIH4gJWlkJQ0KDQpDaGF0IHNpdGU6DQogfiBUT1IgTmV0d29yazogaHR0cDovL2x5bnhjaGF0bHk0emx1ZG1obWk3NWpyd2h5Y25vcXZreGI0cHJvaHhteXpmNGV1ZjVnanhyb2FkLm9uaW9uL2xvZ2luDQogfiBUT1IgTWlycm9yICMxOiBodHRwOi8vbHlueGNoYXRmdzRyZ3NjbHA0NTY3aTRsbGtxanIya2x0YXVtd3dvYnhkaWszcWEyb29ycmtuYWQub25pb24vbG9naW4NCiB+IFRPUiBNaXJyb3IgIzI6IGh0dHA6Ly9seW54Y2hhdG9obXBwdjZhdTY3bGxvYzJ2czZjaHk3bnlhN2RzdTJoaHM1NW1janhwMmpvZ2xhZC5vbmlvbi9sb2dpbg0KIH4gVE9SIE1pcnJvciAjMzogaHR0cDovL2x5bnhjaGF0YnlrcTJ2eWN2eXJ0anFiM3l1ajR6ZTJ3dmR1YnpyMnU2YjYzMnRyd3ZkYnNnbXlkLm9uaW9uL2xvZ2luDQogfiBUT1IgTWlycm9yICM0OiBodHRwOi8vbHlueGNoYXRkZTRzcHY1eDZ4bHd4ZjQ3amRvN3d0d3dnaWtkb2Vyb3hhbXBodTNlN3h4NWRvcWQub25pb24vbG9naW4NCiB+IFRPUiBNaXJyb3IgIzU6IGh0dHA6Ly9seW54Y2hhdGR5M3RnY3VpanNxb2Zoc3NvcGNlcGlyamZxMmY0cHZiNXFkNHVuNGRocXl4c3dxZC5vbmlvbi9sb2dpbg0KIH4gVE9SIE1pcnJvciAjNjogaHR0cDovL2x5bnhjaGF0ZHlrcG9lbGZmcWx2Y2J0cnk2bzdneGszcnMyYWlhZ2g3ZGR6NXlmdHRkNnF1eHFkLm9uaW9uL2xvZ2luDQogfiBNaXJyb3IgIzc6IGh0dHA6Ly9seW54Y2hhdC5uZXQvbG9naW4NCg0KT3VyIGJsb2c6DQogfiBUT1IgTmV0d29yazogaHR0cDovL2x5bnhibG9neHN0Z3pzYXJmeWsycHZoZHY0NWlnZ2hiNHptdGhuem1zaXB6ZW9kdXJ1ejN4d3FkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjMTogaHR0cDovL2x5bnhibG9nY283cjM3anQ3cDV3cm1meHpxemU3Z2h4dzZyaWh6a3FjNDU1cWx1YWN3b3RjaXlkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjMjogaHR0cDovL2x5bnhibG9naWp5NGpmb2JsZ2l4MmtseG1rYmdlZTRsZW9ldWdlN3F0NGZwZmtqNHpiaTJzanlkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjMzogaHR0cDovL2x5bnhibG9nbXgzcmJpd2czcnBqNG5kczI1aGpzbnJ3a3B4dDVnYXpuZXRmaWt6NGd6MmNzeWFkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjNDogaHR0cDovL2x5bnhibG9nb3hsbHRoNGI0NmNmd2xvcDVwZmo0czdkeXYzN3l1eTdxbjJmdGFuNmdkNzJoc2FkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjNTogaHR0cDovL2x5bnhibG9ndHdhdGZzcndqM29hdHBland4azVibmdxY2Q1ZjdzMjZpc2thZ2Z1N291YW9tamFkLm9uaW9uLw0KIH4gVE9SIE1pcnJvciAjNjogaHR0cDovL2x5bnhibG9neHV0dWZvc3NhZWF3bGlqM2ozdWlrYWxvbGw1a282Z3J6aGt3ZGNscmpuZ3Jmb2lkLm9uaW9uLw0KIH4gTWlycm9yICM3OiBodHRwOi8vbHlueGJsb2cubmV0Lw0KIA==

— TTA
For questions, collaboration, or to report vulnerabilities, feel free to reach out:

[email protected]

By xande