JavaScript Attacks

How to bypass JavaScript-based protection

JavaScript is one of the most popular languages used in websites. Due to this popularity, you can find a wide variety of JavaScript usage examples. Of these, the most meaningless are:

  • JavaScript access control
  • JavaScript protection of part or all of the content
  • JavaScript data verification without server-side validation

Creating a site, it is necessary to proceed from the fact that any data received from users is unreliable and JavaScript cannot guarantee anything.

To demonstrate the vulnerability of JavaScript, we will bypass the protection in Damn Vulnerable Web Application (DVWA). To install DVWA on your computer, see the articles:

1. Any part of the web page and JavaScript can be arbitrarily changed by the user

So, let’s set the low security level in DVWA (in the DVWA Security tab) and go to the “JavaScript Attacks” page and see the following there:

As a task, we need to pass the word “success” through the form on the site. Let’s try: we enter “success” and Sumbit. We get the error “Invalid token.”:

Open the source code of the page. If you have problems with this on the target site in a real situation, then see “How to see locked HTML code, how to bypass social content lockers and other website info gathering countermeasures”.

Between the <script></script> tags there is the following:

I can only understand the following lines:

function generate_token() {
    var phrase = document.getElementById("phrase").value;
    document.getElementById("token").value = md5(rot13(phrase));
}
 
generate_token();

As well look at the form being used to send the word “success”:

<form name="low_js" method="post">
    <input type="hidden" name="token" value="" id="token" />
    <label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase" />
    <input type="submit" id="send" name="send" value="Submit" />
</form><script>

In the generate_token() function in the first line “phrase” variable is assigned. It got the value that has the field with the “phrase” id in the form. Then, in the second line of the considered function, the value of the phrase variable is processed by two functions and their value is assigned to the element with “token” id.

Outside the function, there is a call code for this function:

generate_token();

This call is not tied to any event (for example, form submission) or condition. This means that when the page is open in our browser, the generate_token() function has already been executed, that is, the token value for the “ChangeMe” string has already been calculated and assigned to the input field with the “token” id. For this reason, when we change the value of the “phrase” input field, it does not coincide with the token, which is what we get the message about.

It turns out that the only way to cope with the task is to change the value of the phrase field from “ChangeMe” to “success” before opening it in a web browser. How can I do that?

This can be done in Burp Suite, which, among other things, can change the contents of any part (headers and body of requests and responses) of HTTP on the fly.

But I will show you a completely “childish” method, which I used from the first years of the appearance of my computer, for this method no tools are needed at all.

The essence is elementary: we save the page to our computer, open it with an editor (any text or HTML code editor), make the necessary changes, open this page in the browser and submit it!

We save (the name I chose is shorter, otherwise there may be problems due to special characters):

Open the *.html file and find the form:

As you can see, it has changed, namely, the value is assigned to the token field, apparently, this was done by the browser when saving:

<form name="low_js" method="post">
    <input type="hidden" name="token" value="8b479aefbd90795395b3e7089ae0dc09" id="token">
    <label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase">
    <input type="submit" id="send" name="send" value="Submit">
</form>

So, firstly, change “ChangeMe” to “success”.

Secondly, this form has no “action” attribute. The attribute “action” specifies the page where the data is submitted. If the attribute is absent (this is allowed), then the data is sent to the same address where the page with the form is located. This worked fine when the page had the http://localhost/dvwa/vulnerabilities/javascript/ address. But when I open my saved file, it will have address something like file:///home/mial/Загрузки/1.html and the browser will try to send data from the form to the address file:///home/mial/Загрузки/1.html. But we need to send data to the server. Therefore, we add the “action” attribute to the form and specify the address where the data should go (in our case, the source address of the form): action="http://localhost/dvwa/vulnerabilities/javascript/"

Sometimes the page may already have “action” set, but the address is indicated as a relative path – in this case, again, submitting the form will not work as we need. To fix, just specify the absolute path to the page on the target site.

Thirdly, as I mentioned, when saving the page, the browser entered the token value – we remove it (although, logically, this is not necessary, since the new value will be calculated and assigned after the page is opened).

So, I got the following form:

<form name="low_js" method="post" action="http://localhost/dvwa/vulnerabilities/javascript/">
    <input type="hidden" name="token" value="" id="token">
    <label for="phrase">Phrase</label> <input type="text" name="phrase" value="success" id="phrase">
    <input type="submit" id="send" name="send" value="Submit">
</form>

I save it and open the file in the browser:

I click the “Submit” button:

Pay attention to the page address – we are back on the server. At the same time, we received the message “Well done!” - that is, the task is completed. In such a simple way, we circumvented JavaScript protection and we didn’t even have to understand complex token calculation algorithms – it was enough to make a small change at the reference point.

2. Changing JavaScript variables during debugging

In the web browser, open the Developer Tools (F12 key), go to the “Sources” tab, select the desired file (in our case (index)) and find the line in the code where we want to interrupt code execution:

Click on the line number to set the breakpoint:

Note that in this line we can set up to four breakpoints, since four operations are performed. I put a breakpoint in the very right part – on the first action:

The “token” variable value is already set, so we reload the page so that all scripts are executed again.

We see that the execution froze at the breakpoint:

Let's take a look at this information:

Change the “phrase” variable value in this window and press the “Resume script execution” button (you can press F8):

The remaining scripts will be executed on the page. You can remove the breakpoint and even close the Developer Tools.

What we have? The “token” variable value is calculated for the string “success”, now we just need to enter this line in the form field:

And send to the server:

3. Debugging JavaScript in external files

In DVWA, raise the security level to medium.

By the way, even before delving into the features of the page, please note that the “childish” method with saving the page and changing the form still works!

But let's start debugging, since not all cases are so simple: for example, the submit form may be completely absent in the source code and created on the fly using JavaScript.

Let's look at the source code of the page, JavaScript was taken out in a separate file:

<script src="../../vulnerabilities/javascript/source/medium.js"></script>

The contents of this file are:

function do_something(e){for(var t="",n=e.length-1;n>=0;n--)t+=e[n];return t}setTimeout(function(){do_elsesomething("XX")},300);function do_elsesomething(e){document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")}

To make JavaScript code readable, see the “How to deobfuscate JavaScript code” article. I will use the JStillery tool.

Download the file of interest:

wget http://localhost/dvwa/vulnerabilities/javascript/source/medium.js

Run deobfuscation:

./jstillery_cli.js medium.js

Result:

function do_something(e)
    /*Scope Closed:true*/
    {
        for (var t = '', n = e.length - 1; n >= 0; n--)
            t += e[n];
        return t;
    }
setTimeout(function ()
    /* Called:undefined | Scope Closed:false| writes:false*/
    {
        do_elsesomething('XX');
    }, 300);
function do_elsesomething(e)
    /*Scope Closed:false | writes:true*/
    {
        document.getElementById('token').value = do_something(e + document.getElementById('phrase').value + 'XX');
    }

If you look closely at the source line, you can see that the code is not obfuscated, but minified. In any case, it has now become much clearer.

Of the three functions, only the latter is actually used, and the rest, apparently, are added to confuse us.

We start debugging the code to replace the value of the variable to get the token we need.

On the web browser, go back to the Developer Tools (F12), find the medium.js file. To bring minified JavaScript in an understandable form, press the { } button:

The analysis of the code suggests that you should pay attention to the function:

function do_elsesomething(e) {
    document.getElementById("token").value = do_something(e + document.getElementById("phrase").value + "XX")
}

We put a breakpoint there. Since several operations are performed on one line at once, we select the last position for the breakpoint:

Reload the page.

We see that the variable e is assigned the value “XX”. Press F9 to go to the next step.

As you can see, I was wrong to say that the first two functions were added only to confuse us – the call to the first function is being prepared and “XXChangeMeXX” will be passed to it as the parameter e. Apparently, this is the beginning of the calculation of the token.

I set the parameter e to “success”, but got an error about the wrong token. By analogy with the transmitted value (“XXChangeMeXX”), assign the string “XXsuccessXX” to e.

Then press F8 to continue executing the JavaScript code without debugging or interrupting.

Enter the word “success” on the page:

And send:

As you can see, everything went well.

4. Debugging obfuscated JavaScript code

In DVWA, raise the security level to High.

The JavaScript code is again moved to the high.js file, look at it:

The code is obfuscated, I tried to use JStillery, but mainly nothing has changed – the code has remained completely unreadable.

We return to the article “How to deobfuscate JavaScript code” and try various tools. The best result was shown by “JavaScript online deobfuscator deobfuscatejavascript.com”, the address of this service: http://deobfuscatejavascript.com/

After deobfuscation, we got a large piece of JavaScript code.

At the end of the code there are the following functions:

function do_something(e) {
    for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
    return t
}
function token_part_3(t, y = "ZZ") {
    document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
    document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
    document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
    token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);

Pay attention to the line:

document.getElementById("token").value = do_something(document.getElementById("phrase").value)

It reads the value of the “phrase” field. I have a suspicion that the functions in huge code before do nothing and is placed just to confuse us. But in any case, whether we want to debug all the code or just the specified fragment, the main question is how exactly to insert our deobfuscated code into the http://localhost/dvwa/vulnerabilities/javascript/source/high.js file? If you do not answer this question, then debugging of obfuscated code may become impossible, since the previous meaningless code can perform millions of operations before control passes to a truly functional fragment.

There is a way out of this situation – moreover, the function of changing files on the fly and saving these changes even after reloading the page is directly in the Browser Developer Tools. See the article “How to make changes in browser Developer Tools persist after page reload” for details.

Insert the deobfuscated code, set the breakpoint:

Add this page to “Save for overrides” as described in the article at the link above and reload the page.

Perhaps I will not analyze this example to the end – it will be your homework.

Conclusion

In practice, there are very difficult to analyze JavaScript usage examples that combine the dynamic building of DOM, the dynamic assignment of event listeners, obfuscation of the code, the use of cumbersome frameworks, etc. But in general, you need to proceed from the fact that the protection is on the user side, including JavaScript are unreliable a priori.

JavaScript can be used, for example, to verify data in a form, but only for the convenience of the user, to inform him that some required field is missing or the information is entered in the wrong format. But on the server side, everything must be double-checked as if there were no checks on the user side. We must proceed from the fact that anything can come from the user side.

Last Updated on

Recommended for you:

Leave a Reply

Your email address will not be published.