If you are involved in the blockchain or cryptocurrency space, you might own wallets containing valuable assets. It is important to protect your keys with robust security protocols (hardware wallet or air gapped computer).
Even if you are careful, you may still fall victim to an attack. Here is a demonstration of how such an exploit works.
After a conversation on LinkedIn, an individual (the attacker) sent me this message:

The assignment looked fishy. I requested more information, but they refused to give any until I completed their task. At this point, I had no interest in working on this task but I knew I was going to find something cool in this repository.
After cloning the repository in a virtual machine, I started looking at every single file carefully. My first assumption was that this project had been compromised by a malicious package (in the package.json or package-lock.json files). That would be an easy way to trigger an exploit while hiding the payload code. Here is the process:
Publish a malware package on npm
Add the malware package as a dependency to this project
Trigger the malware when running the project
Unfortunately, I did not find any suspicious package. However, I did find an interesting hint.

The package.json has a dependency to the fs (filesystem) package, which is strange since fs is a built-in node module: there is no need to install it as a dependency. This was probably a mistake. But it got me thinking, the filesystem is the place to look for private keys or passwords.
A simple search for fs in the project revealed what I was looking for.
It seems there is a require(“fs”) somewhere in this file…

I did not see it at first, but the first line is just that long. If we scroll a little bit more…

Finally! This appears to be the exploit payload. This could have been innocuous minified code, but only malicious actors would conceal their code this way. Let’s analyze what it does.

As expected, the exploit code is obfuscated but we can still analyze it. Without executing it. To deobfuscate JS code, here are some simple steps we can take:
Pretty print: use a tool such as prettier to clean up the code. A pretty printer will format the code to reintroduce spaces and indentation where needed, to increase readability.
Find the main obfuscating primitives: in this case, strings are encoded with base64. It is a quite common method to hide variable names or IP adresses.
Write a script to replace strings and rename variables: input_obfuscated → script → output_deobfuscated. This allows to perform changes efficiently on the whole payload. The less manual work, the better.
Do manual edits in the obfuscated source, if necessary: For example, to rename variables manually, or to change the formatting. In that case, their obfuscator tends to put multiple statements on the same line (separated by commas). I did manual edits to make the code more readable.
After some time working on the code, it was easy to understand how the exploit works. It is triggered when the user runs the application (a Node web server) and their goal is to steal your private keys, wallets, passwords and more. Here are the original files for reference: Obfuscated payload, Unobfuscated payload
Luckily, the obfuscation here was rather simple. They could have employed stronger techniques such as Control-flow flattening or introducing dead code.
And some code snippets from the exploit.




This stage of the malware relies on Node to access the victim’s filesystem. Its success depends on whether or not the host gave enough permissions to Node to access restricted directories such as the home directory, and system files. Running this script with elevated privileges probably means game over.
At this point, I had seen enough to be convinced this was an exploit attempt. However there was more: this payload had a second stage that downloaded and ran Python based malware with more elaborate features to steal user credentials.
The second stage downloads a Python program (and even downloads Python if not available on the victim machine!). Below is the full code of the second stage:

Again, this is an obfuscated payload but its structure is much simpler. The base64 encoded exploit code is decoded, and then executed. Here’s the decoded version:

Reference: 2nd stage payload
This is a client for downloading two Python based malwares. They are quite long, and analyzing them is out of the scope of this research but here are some features of the malware:
Keylogger
Clipboard logger
Shell into the target machine: a wildcard to control the target and possibly send even stronger malware
Stealing passwords/credit cards


Working in the blockchain space requires extra caution to safeguard your assets.
This is especially true if you own or earn crypto. Do not run unverified Git repositories. If you must, use a virtual machine.
However, manually inspecting every file we download and run is impractical.
The community has to think about more automatic ways to detect exploits. Or make sure Node is sandboxed properly. That is also why I prefer Deno’s security model, but it is not always practical to use either.
By looking for a fingerprint of the payload on Github, we can see that multiple repositories are infected. Probably forks of a similar exploit attempt.
If you think you have been exploited by this or need more details, you can reach me @moru on Farcaster.
Hack safely!
