# JS SAFE 2.0 [121]
> You stumbled upon someone's "JS Safe" on the web. It's a simple HTML file that> can store secrets in the browser's localStorage. This means that you won't be> able to extract any secret from it (the secrets are on the computer of the> owner), but it looks like it was hand-crafted to work only with the password> of the owner...
This challenge gives us a single HTML file which shows an input box representinga key and an ominous spinning cube (presumably the safe it opens).
![Safe Screenshot](https://i.imgur.com/Rcu8zqK.png)
To get the flag, we need to reverse engineer the code to figure out whatpassphrase will unlock the safe. When we try to enter some arbitrary text, thepage will give us a big red `Access Denied` error.
![Safe Screenshot Locked](https://i.imgur.com/PoS15tK.png)
## Initial Analyses
Before we can properly work on solving the challenge, we need to do a bit ofintel gathering. How is the locking code implemented? What protections has thedeveloper put in place to stop us from simply reading the password?
First off it runs in a browser so it's probably JavaScript, but CSS 3 is TuringComplete so you never know. Lets dive into the code...
At the top of the page we get a nice comment telling us pretty much everythingwe need to know. The safe is implemented in JavaScript and the developer hasdone their best to prevent it from being debugged. Anti debugging techniques arequite common among proprietary JavaScript libraries, because in this situationyou are essentially selling your source code, and want to prevent one of yourconsumers from simply looking at your code, learning how you solved a certainproblem, and then implementing it themselves and selling their version for alower price.
```html<title>JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology</title>
```
Then we get all the CSS styling and animations for the rotating cube, and at thevery bottom we get the JavaScript for unlocking the safe. Some of the code isminified, so let's use the Atom beautifier to make it more readable.
```html<script>    function x(х) {        ord = Function.prototype.call.bind(''.charCodeAt);        chr = String.fromCharCode;        str = String;
        function h(s) {            for (i = 0; i != s.length; i++) {                /* --snip-- */            }            return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF)        }
        function c(a, b, c) {            for (i = 0; i != a.length; i++) c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length])));            return c        }        for (a = 0; a != 1000; a++) debugger;        x = h(str(x));        source = /Ӈ#7ùª9¨M¤À.áÔ¥6¦¨¹.ÿÓÂ.Ö£JºÓ¹WþÊmãÖÚG¤¢dÈ9&òªћ#³­1᧨/;        source.toString = function() {            return c(source, x)        };        try {            console.log('debug', source);            with(source) return eval('eval(c(source,x))')        } catch (e) {}    }</script><script>    function open_safe() {        keyhole.disabled = true;        password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value);        if (!password || !x(password[1])) return document.body.className = 'denied';        document.body.className = 'granted';        /* --snip-- */    }
    function save() {        /* --snip-- */    }</script>```
The code doesn't look too bad, it's pretty short, there are only a few functions,and the developer was nice enough to use `ord`, `chr`, and `str` to make iteasier for us to understand what's going on.
When we enter some input into the safe it gets checked against a regex to makesure it's in the form `CTF{some ascii here}`, and then the part between thecurly braces gets passed to the `x()` function. If the return value of `x()`evaluates to `true`, then we know we have the right password.
Inside of `x()`, there are a few helper functions. First we have `h()` whichtransforms our input into a 4 character string. The exactly implementationdoesn't really matter, we just treat it as a hash function which generates 4character hashes. We also have `c()` which appears to be doing a repeating keyxor.
There is also an interesting variable called `source` which is assigned to aregex literal containing a bunch of unicode characters. This is passed to thexor function in a few places, so it looks like there are some goodies hiddeninside which the developer is trying to hide from us.
**Summary**
Functions:*   open_safe - Opens the safe*   save - Saves data to localStorage if the safe is open*   x - Takes a password and tells us if it's correct*   h - Generates a 4 character hash*   c - Computes a repeating key xor
Other objects of interest:*   source - Some bytes probably containing the key, but "encrypted" by `c()`
## *Anti* Anti Debug
One of the ways we could try to solve this challenge is to add a bunch of`console.log()` printouts throughout the code to gain some insight intointermediate values. For example, if we can print out the the xor key usedto decrypt the bytes of `source` or better yet, print the result of that xor,we would be a lot closer to getting the flag.
However, if we try this, the first thing we'll notice is that when the developertools are open, JavaScript execution will pause whenever it reaches a `debugger`statement. And unfortunately the code will do this 1000 times during `x()`.
```JavaScriptfor (a = 0; a != 1000; a++) debugger;```
Well that's easy enough to deal with. We can just delete (or comment out) theloop and refresh the page. Now we can try to print out the value of the xor keyafter it is calculated.
```JavaScriptx = h(str(x));console.log("xor key is:", x)```
We see some unicode characters printed out, but after a few seconds ourcomputer fans start to spin up and we get an alert that the tab is notresponding. We also notice that the debug output which was present in theoriginal, never happens.
![Script Hanging](https://i.imgur.com/9DRBmqt.png)
If we look closer at the code we will notice that this is happening because ofthe modified `toString` function which is applied to `source`. It appears thatwhen `source` is printed, it should decrypt itself first, however, the xorfunction (`c()`) is expecting the input to have a `length` attribute, which isnot defined for regex literals. As a result the for loop's condition(`i != a.length`) is always `true` and it gets stuck in an infinite loop.
When we investigate what attributes actually exists for regex literals,we see that they have an attribute called `source` which is a stringrepresentation of the regex.
![Regex Literal](https://i.imgur.com/4ermQ75.png)
So we can fix this little issue by using `source.source` instead. And now whenwe run the page again we see the decrypted value printed out as we would expect.
```JavaScriptsource.toString = function() {    return c(source.source, x)};```![Printed Source](https://i.imgur.com/gGWxbMf.png)
Hmmm... That still looks like garbage. Somehow we must not be using the correctxor key. Wait a second, the xor key that is being printed out now is differentthan it was before! We used the same test input, so what changed? Is the inputbeing modified somewhere else that we haven't noticed? Lets do a search for alloccurrences of `x` in the file.
![Searching For X](https://i.imgur.com/OiwrZo5.png)
And now we notice something very strange. The function appears to take anargument which is also named `x`, however it isn't highlighted when we searchedfor 'x' in the file.
When we copy and paste the parameter `х` into DuckDuckGo it brings up theWikipedia page for the Cyrillic letter Kha[https://en.wikipedia.org/wiki/Kha_(Cyrillic)](https://en.wikipedia.org/wiki/Kha_(Cyrillic)).Wow! The parameter is actually just a unicode character which looks exactly thesame as the ascii character 'x' but is a completely different symbol inJavaScript. The xor key is not being generated from our input, but instead isthe result of hashing the string representation of the whole function!
That's why it changed when we modified the `toString` function. We also realizenow, that beautifying the code in the first place would break the functionalityof the safe. The good news is that all we have to do is calculate the hash ofthe original string representation of the minified function.
We can grab a fresh copy of the file, open up the browser console on it, andcall the hash function manually.
Now, there are a few catches here. The first is that we need to paste in `h()`because it's only defined within the scope of `x()`. The second is much moreprofound and also a giant headache to figure out. Do you remember thedebugger loop from earlier?
```JavaScriptfor (a = 0; a != 1000; a++) debugger;```
Notice that the variable it's using isn't `i` like the rest of the loops.It's actually `a`, which appears again inside the hash function. So in order toget the correct output, we need to set `a` to 1000 at the start of `h()`.
![Calling H](https://i.imgur.com/V8dCnpA.png)
Now that we have the key, we can finally figure out what secrets are hidden inthe mess of the unicode regex. We can paste in the xor function and `source`regex and manually invoke the decryption.
![Decrypting Source](https://i.imgur.com/xjCAiEq.png)
Great! We are almost there. It looks like `source` actually contains more validJavaScript code which checks that the unicode `х` parameter from before isequal to the flag. Unfortunately the flag isn't in plaintext. Once again we havesome encrypted unicode string which we need to decrypt first. Luckily it's justa multibyte xor, and we even know the key length.
```JavaScriptх==c('¢×&\u0081Ê´cÊ¯¬$¶³´}ÍÈ´T\u0097©Ð8Í³Í|Ô\u009c÷aÈÐÝ&\u009b¨þJ',h(х))//᧢```
Because we know that the key length is 4, we know that every 4th character hasbeen xored with the same character from the key. For example if the key were`abcd` and the text were `01234567`, then `0` and `4` would get xored with `a`,`1` and `5` would get xored with `b` and so on. Therefore if we split theciphertext into 4 parts, where we know that each part has been xored with thesame character, we can solve each part separately by trying to xor it with everypossible character, and evaluating whether the result looks like validplaintext. The caveat is that we need to know something about the originalplaintext, in order to evaluate if our candidates look valid. In our case wehave the regex from `open_safe()` which we can test our candidates against.
First we create a JavaScript file which we will run using NodeJS. We copy in theciphertext and create our 4 single byte xor strings.
```JavaScriptcipher = '¢×&\u0081Ê´cÊ¯¬$¶³´}ÍÈ´T\u0097©Ð8Í³Í|Ô\u009c÷aÈÐÝ&\u009b¨þJ'transposed = ['', '', '', '']
for (let i = 0; i < cipher.length; i++) {    transposed[i % transposed.length] += cipher[i]}console.log(transposed)```
Output:`[ '¢Ê¯³È©³Ð¨', '×´¬´´ÐÍ÷Ýþ', '&c$}T8|a&J', 'Ê¶ÍÍÔÈ ]`
Then we solve each of the strings as a single byte xor against the regex from`open_safe()` and print any possible candidates.
```JavaScriptfor (let i = 0; i < transposed.length; i++) {    for (let j = 0; j < 1000; j++) {        xored = c(transposed[i], chr(j));        if (/^[0-9a-zA-Z_@!?-]+$/.test(xored)) {            console.log(i, xored)        }    }}```
Output:```0 '_7RN5TNa-U'1 'B!9!!EXbHk'1 'N-5--ITnDg'2 '3v1hA-it3_'3 'x3O4n4-1b'```
This looks very promising! Only the second string had more than one possiblesolution, so now we just need to pick one (or try both), and put the pieces backtogether. Since we know that the flag will probably be English text written inleetspeak, we guess that the second candidate is the correct one.
```JavaScriptchunks = ['_7RN5TNa-U', 'N-5--ITnDg', '3v1hA-it3_', 'x3O4n4-1b']flag = ''for (let i = 0; i < cipher.length; i++) {    flag += chunks[i % chunks.length][Math.floor(i / chunks.length)]}console.log("CTF{" + flag + "}")```
Output:`CTF{_N3x7-v3R51ON-h45-AnTI-4NTi-ant1-D3bUg_}`