JavaScript can technically be written using only Unicode escape sequences or Unicode homoglyphs. This makes code valid to the JS engine but unreadable or misleading to humans. It is often used in obfuscation, XSS payloads, or supply chain attacks.
var \u0061 = 5; // same as: var a = 5
console.log(\u0061); // prints 5\u0061→"a"
console.log("\u0048\u0065\u006C\u006C\u006F"); // "Hello"Even keywords can be written using escapes:
\u0069\u0066 (true) { // "\u0069\u0066" → "if"
console.log("unicode if");
}\u0063\u006F\u006E\u0073\u006F\u006C\u0065\u002E\u006C\u006F\u0067(
"\u0048\u0065\u006C\u006C\u006F\u0020\u0057\u006F\u0072\u006C\u0064"
);Output:
Hello World
\u0066\u0075\u006E\u0063\u0074\u0069\u006F\u006E \u006D\u0079\u0046\u0075\u006E\u0063\u0074\u0069\u006F\u006E() {
\u0072\u0065\u0074\u0075\u0072\u006E "\u0048\u0065\u006C\u006C\u006F\u0020\u0057\u006F\u0072\u006C\u0064";
}
\u0063\u006F\u006E\u0073\u006F\u006C\u0065\u002E\u006C\u006F\u0067(\u006D\u0079\u0046\u0075\u006E\u0063\u0074\u0069\u006F\u006E());Output:
Hello World
Unicode homoglyphs are different code points that look identical. Example with Cyrillic vs Latin:
// Looks like "password"
var рassword = "secret";
console.log(рassword);р(U+0440, Cyrillic) vsp(U+0070, Latin)
// Safe function
function login() {
console.log("Safe login");
}
// Attacker’s homoglyph function
function logіn() {
console.log("Evil login");
}
logіn(); // Executes attacker’s version!і= Cyrillic small letter "і" (U+0456), not Latini.
// Normal (Unicode escapes)
\u0063\u006F\u006E\u0073\u006F\u006C\u0065.\u006C\u006F\u0067("Hello");
// Attacker version
\u0063\u006F\u006E\u0073\u006F\u006C\u0065.\u006C\u006F\u0067("Stolen data: " + document.cookie);Filters may fail if attackers swap in homoglyphs or escape sequences.
| Latin | Homoglyph | Unicode | Script |
|---|---|---|---|
| a | а | U+0430 | Cyrillic |
| e | е | U+0435 | Cyrillic |
| i | і | U+0456 | Cyrillic |
| o | о | U+043E | Cyrillic |
| p | р | U+0440 | Cyrillic |
| c | с | U+0441 | Cyrillic |
| y | у | U+0443 | Cyrillic |
| x | х | U+0445 | Cyrillic |
- Unicode Escapes (
\uXXXX) → Make code completely unreadable but valid. - Unicode Homoglyphs → Make code look normal but behave differently.
- Both together → Powerful obfuscation & exploitation trick, often seen in XSS or supply chain attacks.
This document covers different ways JavaScript code can be obfuscated using string escapes, identifiers, and numbers.
eval('\x61\x6c\x65\x72\x74(1)'); // alert(1)eval('\141\154\145\162\164(1)'); // alert(1)eval('\u0061\u006c\u0065\u0072\u0074(1)'); // alert(1)eval(String.fromCharCode(97,108,101,114,116,40,49,41)); // alert(1)eval(String.fromCharCode(0b1100001,0x6c,101,0o162,0x74,40,0b11,0x29)); // alert(1)JavaScript allows Unicode escapes inside identifiers:
var \u0061 = "Hello";
alert(\u0061); // alerts "Hello"\uXXXX) work inside identifiers. Hex (\x) and Octal are invalid here.
Numbers can be written in different bases:
alert(String.fromCharCode(0x61,0x6c,0x65,0x72,0x74)); // alertalert(String.fromCharCode(0o141,0o154,0o145,0o162,0o164)); // alertalert(String.fromCharCode(0b1100001,0b1101100,0b1100101,0b1110010,0b1110100)); // alertEncodings can be executed via:
eval("...");
setTimeout("...");
Function("...")();Examples:
setTimeout('\x61\x6c\x65\x72\x74(1)', 0);
new Function('\u0061\u006c\u0065\u0072\u0074(1)')();✅ Summary
- Strings: All encodings (hex, octal, unicode, decimal, binary) apply.
- Identifiers: Only Unicode escape sequences work.
- Numbers: Can obfuscate using base representations +
String.fromCharCode. - Execution:
eval,Function, andsetTimeoutallow hidden payloads.