Obfuscation Is Not Encryption
A contractor hid a secret in obfuscated JavaScript. Peel the encoding layers, or make the program hand the secret over, to recover it.
Statement
The story
A freelancer left vault.js behind: the "access control" of an internal demo page. The only accepted password is stored inside the script, and the owner insists it is safe because the source code is obfuscated. It is an hour of work for you to prove otherwise.
Your task
Open vault.js. It compares the variable attempt with a secret and prints ACCESS GRANTED or ACCESS DENIED. The only attempt that is accepted is the flag, which has the shape IMB{...}.
You can solve it two ways, and both are legitimate reverse-engineering:
- Static analysis: read the code and undo, by hand or with your own script, what it does to build the secret.
- Dynamic analysis: run it in Code Lab (JavaScript) and get the program to reveal the value it compares against.
The program never reads input and needs no network. Brute-forcing attempt is pointless: the comparison is exact.
Things worth knowing
- The escape
"\x68\x69"is just the texthi, andobj["\x70\x75\x73\x68"](x)isobj.push(x). Hex escapes hide names from a casual reader, not from the interpreter. atob("aGk=")decodes base64 and returnshi.- XOR undoes itself:
(a ^ k) ^ k === a.
Submit the recovered flag.
Flag format: IMB{...}
Files
Public puzzle material. Download it or read it right here.
The obfuscated script. Runs as is in Code Lab (JavaScript).
// vault.js: client-side "access control" for an internal demo page.
// The owner says the secret is safe because the source is obfuscated.
const _0x3c1e = ['\x43\x42\x73\x67\x47\x78\x6b\x36\x47', '\x63\x6b\x56\x30\x49\x4e\x47\x54\x51', '\x41\x45\x4b\x56\x68\x6f\x54\x4a\x78', '\x47\x47\x6a\x45\x6d\x50\x41\x3d\x3d'];
(function (_0x2b, _0x4d) {
const _0x1f = function (_0x5a) {
while (--_0x5a) {
_0x2b['\x70\x75\x73\x68'](_0x2b['\x73\x68\x69\x66\x74']());
}
};
_0x1f(++_0x4d);
})(_0x3c1e, 0x92);
const _0x2e9a = function (_0x1a) {
return _0x3c1e[_0x1a];
};
const _0x6d = [0x2, 0x0, 0x3, 0x1];
const _0x9b = [0x75, 0x6e, 0x6d, 0x61, 0x73, 0x6b];
function _0x71() {
let _0xa = '';
for (let _0xi = 0x0; _0xi < _0x6d['\x6c\x65\x6e\x67\x74\x68']; _0xi++) {
_0xa += _0x2e9a(_0x6d[_0xi]);
}
const _0xb = atob(_0xa);
let _0xc = '';
for (let _0xi = 0x0; _0xi < _0xb['\x6c\x65\x6e\x67\x74\x68']; _0xi++) {
_0xc += String['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](_0xb['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](_0xi) ^ _0x9b[_0xi % _0x9b['\x6c\x65\x6e\x67\x74\x68']]);
}
return _0xc['\x73\x70\x6c\x69\x74']('')['\x72\x65\x76\x65\x72\x73\x65']()['\x6a\x6f\x69\x6e']('');
}
function _0x90(_0xin) {
return _0xin === _0x71();
}
// Try your luck: change the guess below.
const attempt = 'hunter2';
console.log(_0x90(attempt) ? 'ACCESS GRANTED' : 'ACCESS DENIED');
Hints
Stuck? Hints are optional, and each one lowers the points you earn for solving this challenge.
- Hint 1Costs 5 points
- Hint 2Costs 5 points
Reveal the previous hint first
- Hint 3Costs 8 points
Reveal the previous hint first
Lessons that teach this
Stuck on the ideas rather than the puzzle? These lessons cover what you need.