Lord777
Professional
- Messages
- 2,577
- Reaction score
- 1,563
- Points
- 113
Bypass JavaScript-based protections
JavaScript is one of the most popular languages used in the work of sites. Due to this popularity, you can find a wide variety of examples of the use of JavaScript. Of these, the most meaningless are:
- JavaScript access control
- protecting part or all of the content with JavaScript
- data verification using JavaScript methods without rechecking on the server side
To demonstrate the JavaScript vulnerability, we will bypass the protection in Damn Vulnerable Web Application (DVWA). To install DVWA on your computer, see the articles:
- Installing OWASP Mutillidae II and Damn Vulnerable Web Application (DVWA) on Kali Linux
- Windows Website Hacking Workout
1. Any part of the web page and JavaScript can be arbitrarily changed by the user
So, set the security level in DVWA to low (done 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. Trying it: enter "success" and send it. 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 "Bypassing the ban on displaying HTML source code, bypassing social blockers and other countermeasures to collect information about the site." In any case, it is strongly recommended that you study "Static Analysis of Website Source Code in a Browser" in advance.
Between the <script> </script> tags there is the following:
I can only understand the following lines:
Code:
function generate_token () {
var phrase = document.getElementById ("phrase"). value;
document.getElementById ("token"). value = md5 (rot13 (phrase));
}
generate_token ();
Let's also look at the form through which we must send the word "success":
Code:
< 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, the variable phrase is assigned the same value as the field with the phrase identifier (id) in the form. Then, in the second line of the considered function, the value of the variable phrase is processed by two functions and their value is assigned to the token element in the form.
Outside the function, there is a code for calling this function:
Code:
generate_token ();
This call is not tied to any event (for example, form submission) or condition. This means that when the page is opened in our browser, the generate_token () function has already been executed, that is, the token value for the "ChangeMe" line has already been calculated and assigned to the input field with the token identifier . For this reason, when we change the value of the phrase input field , it does not match the token about which we receive the message.
It turns out that the only way to do the job 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 content of any part (headers and bodies of requests and responses) of HTTP on the fly.
But I will show you a completely "childish" method that I have been using since the first years of my computer's appearance, 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 for HTML code), make the necessary edits, open this page in a browser and send it!
Save (I chose a shorter name, 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:
Code:
< 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 first, change "ChangeMe" to "success".
Second, this form lacks the action attribute. The action attribute specifies the page where the data is sent. 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 http: // localhost / dvwa / vulnerabilities / javascript / . But when I open my saved file, it will have something like file: ///home/mial/Downloads/1.html as an address and the browser will try to send data from the form to file: /// home / mial / Downloads / 1.html, but we need to send to the server. Therefore, we add the action attribute to the form.and as its value we specify the address where the data will be sent (in our case, the original address of the form): action = "http: // localhost / dvwa / vulnerabilities / javascript /"
Sometimes the page may already have action set, but the address is specified as a relative path - in this case, again, submitting the form will not work as we need. To fix it, just provide the absolute path to the page on the target site.
Thirdly, as I mentioned, when saving the page, the browser entered the token value - let's remove it (although, logically, this is not necessary, since the new value will be calculated and assigned after opening the page).
So, I ended up with the following form:
Code:
< 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 press the "Submit" button:
Pay attention to the page address - we are on the server again. At the same time, we received the message "Well done!" - that is, the task is completed. In this simple way, we bypassed JavaScript protection and we didn't even have to understand the complex algorithms for calculating the marker - it was enough to make a small change in the reference point.
2. Changing JavaScript variables while debugging
In a web browser, open the Developer Tools (F12 key), go to the "Sources" tab , select the file you need (in our case, this is (index)) and find the line in the code where we want to interrupt:
Click on the line number to set the breakpoint:
Note that on this line we can set up to four interrupts, since four operations are performed. I put a breakpoint on the very right side - on the first action:
The token value has already been set, so let's reload the page so that all scripts are executed again.
We see that the execution froze at the breakpoint:
Let's take a closer look at this information:
Let's change the phrase value in this window and click 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 value is calculated for the string "success", now we just need to enter this string 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 specifics of how the page works, please note that the "child" method of saving the page and changing the form still works!
But let's get down to debugging, since not all cases are so simple: for example, a submit form may not be present at all in the source code and can be created on the fly using JavaScript.
Let's look at the source code of the page, JavaScript was moved to a separate file:
Code:
< script src = "../../ vulnerabilities / javascript / source / medium.js"> </ script >
The content of this file is as follows:
Code:
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 article "Deobfuscate JavaScript Code". I will use the JStillery tool.
Download the file of interest:
Code:
wget http: //localhost/dvwa/vulnerabilities/javascript/source/medium.js
We start deobfuscation:
Code:
./jstillery_cli.js medium.js
Received:
Code:
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 compressed. In any case, it is now much clearer.
Of the three functions, only the last is actually used, and the rest, apparently, were added to confuse us.
We start debugging the code to replace the value of the variable to get the token we need.
In the web browser, go back to the Developer Tools (F12), find the medium.js file. Press the {} button to make the compressed JavaScript readable :
Analysis of the code suggests that you should pay attention to the function:
Code:
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 that the first two functions were added only to confuse us - the call of the first function is being prepared and "XXChangeMeXX" will be passed to it as a parameter e . Apparently, this is the beginning of the token calculation.
I set the e parameter to "success", but I got an error about an invalid token. By analogy with the transmitted value ("XXChangeMeXX"), assign e to the string "XXsuccessXX".
Then press F8 to continue executing JavaScript code without debugging or interruptions.
Enter the word "success" on the page:
And we send:
As you can see, everything went well.
4. Debugging obfuscated JavaScript code
In DVWA, raise the security level to High.
The JavaScript code has been moved to the high.js file again, let's take a look at it:
The code is obfuscated, I tried using JStillery, but nothing fundamentally changed - the code remained completely unreadable.
Let's go back to the article "Deobfuscate JavaScript Code" and try different tools. The best result was shown by the JavaScript online deobfuscator deobfuscatejavascript.com, the address of this service is: http://deobfuscatejavascript.com/
After deobfuscation, we got a large piece of JavaScript code.
At the end of the code, there are the following functions:
Code:
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:
Code:
document.getElementById ("token"). value = do_something (document.getElementById ("phrase"). value)
It reads the value of the phrase field . I have a suspicion that the huge code before these functions does not do anything and is posted just to confuse us. But in any case, do we want to debug all the code or only the specified fragment, the main question is how exactly to insert our deobfuscated code into the file http: //localhost/dvwa/vulnerabilities/javascript/source/high.js? If you do not answer this question, then debugging 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 right in the Browser Developer Tools. For details, see How to keep changes in Browser Developer Tools persist across page reloads .
Insert the deobfuscated code, set a 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 homework.
Conclusion
In practice, there are JavaScript use cases that are very difficult to analyze, combining dynamic construction of the DOM structure of the page, dynamic assignment of events, code obfuscation, the use of bulky frameworks, etc. using JavaScript are a priori unreliable.
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 information is entered in the wrong format. But on the server side, everything must be rechecked as if there were no checks on the user side. We need to assume that anything can come from the user side.