Deobfuscating JavaScript, Part 1
Written on September 12th, 2023 by MouseIntroduction
Code obfuscation is the process of deliberately writing code in a way that makes it difficult to understand and read, to ensure that it cannot be reverse-engineered or modified. Some basic techniques include making the code structure more complex with redundant code, and making code less human-readable by renaming variables and functions to random character strings. There are multiple reasons for doing this - both innocent and malicious. Code obfuscation can be used to protect a company’s intellectual property, but is also used by malware developers to evade detection. For more on the general principles of code obfuscation, see my previous article on it.
JavaScript is a scripting language used to implement dynamic and interactive elements to web pages, and it is widespread across the web. JavaScript can either be embedded into an HTML file:
Or externally linked to an HTML file:
To see some JavaScript obfuscation in practice, there are many available online tools. This tool can be used to efficiently obfuscate JavaScript, and allows the user to fiddle with various obfuscation settings. As an example, consider the basic JavaScript code below:
Running this in the Chrome Developer Console, we see it works as intended:
Running this through the above JavaScript obfuscation tool, we get the following mess:
Running this on the console, we get the exact same output:
To most people, even experienced programmers, this code is incomprehensible. After looking at it carefully we see the encoded text, but only after a jumble of garbage code.
Recently, in March 2023, the Emotet malware was reverse engineered by Trustwave and was found to contain highly-obfuscated code. It contained many random-seeming functions and variable names. When the malware analysts read this code, they found a recognizable macro name, unobfuscated -
AutoOpen
. This is a reference to the event of opening a document file - so this function was the entry point. The article listed goes further in depth on the obfuscation methods used in this malware.
This article will focus on ways of deobfuscating JavaScript code. Code deobfuscation is an important skill in code analysis and reverse engineering. Security researchers will often come across malware that uses obfuscated JavaScript to deliver a malicious payload - the ability to understand and reverse this code is of paramount importance to understanding the malware.
JavaScript Obfuscation
In order to deobfuscate JavaScript, we need to know some common methods used for obfuscating it in the first place.
Code minification is a way of reducing the readability of code by compressing it all into a single line. This includes the removal of whitespace, comments, and other unnecessary characters. Many websites use this technique, so much so that many browser developer tools include a “beautify” or “pretty print” feature, which expands minified JavaScript into its full form. This is a simple online tool for JavaScript minification.
The Browser Developer Tools beautify feature can often be found as the { }
button. Furthermore, Prettier and Beautifier can be used to expand minified JavaScript.
JavaScript packing is another obfuscation technique. Packers are tools which take in a binary, and transform it using compression, encryption, and anti-debugging tricks to create a smaller file, whilst retaining the original functionality. Packing can be used benignly to help pages load faster and keep web pages more efficiently, but can also be used to obfuscate code for malicious purposes. The same ideas can be used to obfuscate JavaScript. Typically, JavaScript packing has two stages:
- Transforming the code into a new reduced form, hardcoded into the output
- Introducing some helper code which will unpack and evaluate the original code
eval(unescape())
The simplest way to pack JavaScript is by using the built-in eval(unescape())
function. The original code is URL-encoded, and used as an argument to eval(unescape())
. If we search PublicWWW (a search engine for HTML, CSS and JS snippets on the web) for eval(unescape())
we quickly find an example in the wild.
The second result shows a positive result on the OSHA (the USA’s Occupational Safety and Health Administration) website.
From line 110, we see the eval(unescape())
method in use. If we URL-decode the first few lines of this code:
eval%28function%28p%2Ca%2Cc%2Ck%2Ce%2Cr%29%7Be%3D
function%28c%29%7Breturn%28c%3Ca%3F%27%27%3Ae%28parseInt%28c/a%29%29%29
+%28%28c%3Dc%25a%29%3E35%3FString.fromCharCode%28c+29%29%3Ac.
we get:
We see that this was just one layer of packing - another packer program has been used on this software. The inner packer has also used the eval()
function. The variables p
, a
, c
, k
, e
and r
are being used as pointers to other functions or variables in the code, but these undescriptive names hide their true purpose. These pointers are used by the interpreter to rebuild the original code. The line eval(function(p,a,c,k,e,r))
is typical of packed JavaScript code.
Note that the
eval()
function should never be used in JavaScript, as it is a known security risk. Ifeval()
is used, there is likely some obfuscation going on.
Research by Or Katz of Akami in 2021 showed that 26% of malicious websites use some form of JavaScript packing. His full talk can be found here. For more on JavaScript packing, see this blog post by rl1987.
One tool to deobfuscate packed JavaScript is JSNice. This website will attempt to make JavaScript ‘nicer’, for instance renaming elusive variables.
However, automated online tools can only take you so far - effectively obfuscated code will need more powerful reverse engineering techniques. Attackers will often use additional techniques such as string substitutions, encodings and junk variables and functions to make code difficult to understand.
Conclusion
In this article, a brief overview has been given on the world of JavaScript obfuscation, a technique to make code intentially complex and challenging to decipher. Methods such as code minification and packing have been explored. The potential dangers of using eval()
have been demonstrated, and methods of using eval()
to obfuscate JavaScript have been described. It’s worth noting that a significant portion of malicious websites utilize JavaScript packing.
In the next article, a real-life example of JavaScript deobfuscation will be explored, using the Locky ransomware. This example will provide valuable insights into the application of deobfuscation techniques in the realm of malware analysis.
List of External Links
Online JavaScript Obfuscation Tool
Emotet Reverse Engineering JavaScript Minification
PublicWWW Results for eval(unescape())
JavaScript eval() Best Practices
Prevalence of JavaScript Packing