StackZero
  • Homepage
  • Cryptography and Privacy
  • Ethical Hacking
  • Reverse Engineering
  • Contacts
  • About Me
No Result
View All Result
StackZero
No Result
View All Result

The terrifying world of Cross-Site Scripting (XSS) (Part 2)

June 14, 2022
in Ethical Hacking
0 0
The terrifying world of Cross-Site Scripting (XSS) (Part 2)
0
SHARES
591
VIEWS
Share on FacebookShare on Twitter

I already discussed the vulnerability in the XSS Intro in Part 1 of the article. Anyway, just for a quick refresh, XSS is a vulnerability that enables attackers to inject malicious code into web pages viewed by other users.
There are 3 types of XSS:

  • Reflected XSS
  • Stored XSS
  • DOM-Based XSS

What I’m going to do in the 2nd part of the article is just build a simple vulnerable application and then try to exploit them. Even if it will contain just what is necessary to understand, I’ll try to implement all types of vulnerabilities.

Let’s try together to reach the goal and better understand XSS.

Before starting, just a list of articles about XSS for quick navigation:

  • The terrifying world of Cross-Site Scripting (XSS) (Part 1)
  • The terrifying world of Cross-Site Scripting (XSS) (Part 2)
  • XSS in practice: how to exploit XSS in web applications
  • Reflected XSS DVWA – An Exploit With Real World Consequences
  • How to exploit a stored XSS vulnerability on DVWA
  • How to exploit DOM XSS on DVWA

Table of Contents

Toggle
  • Requirements
  • Reflected Cross-Site Scripting
  • Stored Cross-Site Scripting
  • DOM-Based Cross-Site Scripting
  • Some quick mitigation
  • The complete code
  • Conclusion

Requirements

I think that to make everything more readable, Python will be the right choice, we’ll write the whole application with Python/Flask and just the last part with Javascript.

What we just need is:

  • Python 3
  • Flask

To install flask we have to open a new terminal and type:

pip3 install flask

If something goes wrong you can look here for the official guide.

After that all requirements are filled, we can build our application structure:

from flask import Flask, request
from pathlib import Path

app = Flask("__name__")
         
# Code Here

app.run(host='0.0.0.0', port=5000)

This is an empty application, at this point, we just need to define the endpoints.

Reflected Cross-Site Scripting

This is the simplest to understand, we are going to implement the vulnerability just by taking the unsanitized variable from the query string and putting it inside the page from the server.

@app.route("/reflected-xss")
def reflected_xss():
    name = request.args.get('name')
    page = f"""
            <html>
                <head> </head>
                <body>
                    <h1>Hello {name}! </h1>
                </body>
            </html>
    """
    return page

As we can see, the server puts into the HTML whatever we pass into the variable “name”.

If we want to exploit, let’s run the server with:

python3 main.py

Now we want to run the following malicious script as a Proof of Concept:

<script> alert("You have been hacked"); </script>

In order to do that, let’s open a browser and connect to this address:

http://127.0.0.1:5000/reflected-xss?name=<script> alert(“You have been hacked!”); </script>

And it will show a popup containing the string “You have been hacked!”
Obviously, it’s harmless, but It lets us understand how a hacker can run arbitrary code inside our browser.

Easy? Now I want to go a step further.

Stored Cross-Site Scripting

In this case, the server has to store the script, the idea it’s just to create a simple persistence mechanism.

So the practical result is the implementation of a very basic guestbook, but in order to avoid useless complications, we’ll keep all messages in a simple text file.
It works well for our purpose and can let us focus just on the main topic.

@app.route("/stored-xss", methods=['POST', 'GET'])
def stored_xss():

    db_file = "comments.txt"

    file = Path(db_file)
    file.touch(exist_ok=True)

    if request.method == 'POST':
        comment = request.form["comment"]
        
        with open(db_file, "a") as f:
            f.write(comment+"\n")
        
    comments = ""
    with open(db_file, "r+") as f:
        for line in f.readlines():
            comments+=f"<div>{line}</div>"

    page = f"""
        <html>
        <head></head>
        <body>
            <form action="#" method="POST">
                <input type="text" id="comment" name="comment"><br><br>
                <input type="submit" value="Submit">
            </form>
            {comments}
        </body>
        </html> 
    """
    return page

The code is self-explanatory anyway, just for clarity I want to list its flow.

These are the actions that this code performs:

  • Defines a route called stored_xss allowing access with either the POST or GET methods.
  • Creates a database file called “comments.txt ” if it does not already exist.
  • If the request method is POST, it adds the comment from the form to the database file.
  • Finally, it reads the database file and displays the comments on the page.

As we did before now it’s time to test the exploit so, let’s open our browser to the address:

http://localhost:5000/stored-xss

In this case, the input comes in a POST method so we have to put the exploit into the “name” form and then click on the submit button.

Then, after submitting, the server stores the given script inside our database and the popup will appear every time we open the page.

It’s obvious how dangerous it can be this type of cross-site scripting, it can hit all the application’s users every time they open the vulnerable page.

DOM-Based Cross-Site Scripting

This is the vulnerability that involves client-side processing. So what we want to do is process the Url and write it into the DOM.
Apparently, it’s very similar to the first example, but looking deeper, the missing sanitization, this time, occurs in the client while in the previous example, vulnerable input is reflected by the server.

But take a look at the code:

@app.route("/dom-based-xss")
def dom_based_xss():
    page = """
        <html>
        <head></head>
        <body>
            <h1>Hello 
                <script>
                var name_start = document.location.href.indexOf("name=");
                var name_end = document.location.href.substring(name_start).indexOf("&");
                if(name_start == -1)
                    var name= "Anonymous";
                else if(name_end != -1)
                    var name = document.location.href.substring(name_start+5, name_start+name_end);
                else
                    var name = document.location.href.substring(name_start+5);
                document.write(decodeURIComponent(name));  
                </script>
            
            </h1>
   
        </body>
        </html>
    """

    return page

It’s pretty obvious that there is no server-side processing, but it simply returns an HTML/Javascript page.

The code inside the “script” tag is retrieving the value of the “name” parameter from the URL and writing it to the document.

In particular, it gets the index of “name=” and from the variable separator “&”.

For example, take a look at the case where we connect to this address (localhost is an alias for 127.0.0.1):

http://localhost:5000/dom-based-xss?name=stackzero&othervariable=othervalue

The script inside the page will parse the URL and put into the name variable the value “stackzero” which is the substring between indexOf(“name=”)+5 (5 is the length of the string “name=”) and indexOf(“&”).

The string is not sanitized even in this case, so in order to exploit the vulnerability, we can just put the malicious script inside the URL as we did in the reflected XSS case.

http://localhost:5000/dom-based-xss?name=<script> alert(“You have been hacked!”); </script>

Some quick mitigation

As we have seen in the XSS Intro article, the way to prevent the attack is to sanitize the input, let’s do how we can do it in our application.

The first two cases are pretty much the same, we need to escape the output into the server, and we can easily. use the “escape” module from Flask.

These are the new imports:

from flask import Flask, request, escape
from pathlib import Path

And now the Reflected and the Stored XSS are sanitized:

@app.route("/reflected-xss-sanitized")
def reflected_xss_sanitized():
    name = request.args.get('name')
    page = f"""
            <html>
                <head> </head>
                <body>
                    <h1>Hello {escape(name)}! </h1>
                </body>
            </html>
    """
    return page

@app.route("/stored-xss-sanitized", methods=['POST', 'GET'])
def stored_xss_sanitized():

    db_file = "comments.txt"

    file = Path(db_file)
    file.touch(exist_ok=True)

    if request.method == 'POST':
        comment = request.form["comment"]
        
        with open(db_file, "a") as f:
            f.write(comment+"\n")
        
    comments = ""
    with open(db_file, "r+") as f:
        for line in f.readlines():
            comments+=f"<div>{line}</div>"

    page = f"""
        <html>
        <head></head>
        <body>
            <form action="#" method="POST">
                <input size="100" type="text" id="comment" name="comment"><br><br>
                <input type="submit" value="Submit">
            </form>
            {escape(comments)}
        </body>
        </html> 
    """
    return page

In the case of a DOM-Based Cross-Site Scripting attack, the error is in the decoding of the URI Component,

We can fix it just by Encoding the component with the encodeURIComponent function.

@app.route("/dom-based-xss-sanitized")
def dom_based_xss_sanitized():
    page = """
        <html>
        <head></head>
        <body>
            <h1>Hello 
                <script>
                var name_start = document.location.href.indexOf("name=");
                var name_end = document.location.href.substring(name_start).indexOf("&");
                if(name_start == -1)
                    var name= "Anonymous";
                else if(name_end != -1)
                    var name = document.location.href.substring(name_start+5, name_start+name_end);
                else
                    var name = document.location.href.substring(name_start+5);
                document.write(encodeURIComponent(name));  
                </script>
            
            </h1>
   
        </body>
        </html>
    """

    return page

The complete code

Now let’s see the full application in order to have everything at your fingertips for better understanding, or if you are a bit lazy you can just copy-paste and test the exploits:

from flask import Flask, request, escape
from pathlib import Path

app = Flask("__name__")

@app.route("/reflected-xss")
def reflected_xss():
    name = request.args.get('name')
    page = f"""
            <html>
                <head> </head>
                <body>
                    <h1>Hello {name}! </h1>
                </body>
            </html>
    """
    return page

@app.route("/reflected-xss-sanitized")
def reflected_xss_sanitized():
    name = request.args.get('name')
    page = f"""
            <html>
                <head> </head>
                <body>
                    <h1>Hello {escape(name)}! </h1>
                </body>
            </html>
    """
    return page

@app.route("/stored-xss", methods=['POST', 'GET'])
def stored_xss():

    db_file = "comments.txt"

    file = Path(db_file)
    file.touch(exist_ok=True)

    if request.method == 'POST':
        comment = request.form["comment"]
        
        with open(db_file, "a") as f:
            f.write(comment+"\n")
        
    comments = ""
    with open(db_file, "r+") as f:
        for line in f.readlines():
            comments+=f"<div>{line}</div>"

    page = f"""
        <html>
        <head></head>
        <body>
            <form action="#" method="POST">
                <input size="100" type="text" id="comment" name="comment"><br><br>
                <input type="submit" value="Submit">
            </form>
            {comments}
        </body>
        </html> 
    """
    return page

@app.route("/stored-xss-sanitized", methods=['POST', 'GET'])
def stored_xss_sanitized():

    db_file = "comments.txt"

    file = Path(db_file)
    file.touch(exist_ok=True)

    if request.method == 'POST':
        comment = request.form["comment"]
        
        with open(db_file, "a") as f:
            f.write(comment+"\n")
        
    comments = ""
    with open(db_file, "r+") as f:
        for line in f.readlines():
            comments+=f"<div>{line}</div>"

    page = f"""
        <html>
        <head></head>
        <body>
            <form action="#" method="POST">
                <input size="100" type="text" id="comment" name="comment"><br><br>
                <input type="submit" value="Submit">
            </form>
            {escape(comments)}
        </body>
        </html> 
    """
    return page

@app.route("/dom-based-xss")
def dom_based_xss():
    page = """
        <html>
        <head></head>
        <body>
            <h1>Hello 
                <script>
                var name_start = document.location.href.indexOf("name=");
                var name_end = document.location.href.substring(name_start).indexOf("&");
                if(name_start == -1)
                    var name= "Anonymous";
                else if(name_end != -1)
                    var name = document.location.href.substring(name_start+5, name_start+name_end);
                else
                    var name = document.location.href.substring(name_start+5);
                document.write(decodeURIComponent(name));  
                </script>
            
            </h1>
   
        </body>
        </html>
    """

    return page

@app.route("/dom-based-xss-sanitized")
def dom_based_xss_sanitized():
    page = """
        <html>
        <head></head>
        <body>
            <h1>Hello 
                <script>
                var name_start = document.location.href.indexOf("name=");
                var name_end = document.location.href.substring(name_start).indexOf("&");
                if(name_end != -1)
                    var name = document.location.href.substring(name_start+5, name_start+name_end);
                else
                    var name = document.location.href.substring(name_start+5);
                document.write(encodeURIComponent(name));  
                </script>
            
            </h1>
   
        </body>
        </html>
    """

    return page


app.run(host='0.0.0.0', port=5000)

Conclusion

In this article, we have analyzed in practice the vulnerability and seen some easy mitigations, anyway, it’s just a proof of concept for better understanding, far away from a real-world case.
Just for completeness, XSS is a very dangerous and diffuse vulnerability (even if it does not appear in the OWASP top 10 2021) so we must take care of the input when we build our web applications.


Our tour into the XSS world it’s not over, the next step will be a walkthrough of a more realistic application containing the same kind of vulnerabilities.

I hope you enjoyed the article.

How to create network scanner tool in a few lines of code!
Trending
How to create network scanner tool in a few lines of code!

Tags: dom-based xssjavascriptpractical xssreflected xssstored xssvulnerable applicationxss
Previous Post

The terrifying world of Cross-Site Scripting (XSS) (Part 1)

Next Post

XSS in practice: how to exploit the Google XSS game

Next Post
XSS in practice: how to exploit the Google XSS game

XSS in practice: how to exploit the Google XSS game

You might also like

Cryptographic functions

Cryptographic Hash Functions in Python: Secure Your Data Easily

November 3, 2024
Malware Obfuscation Techniques: All That You Need To Know

Malware Obfuscation Techniques: All That You Need To Know

March 25, 2024
How To Do Process Enumeration: An Alternative Way

How To Do Process Enumeration: An Alternative Way

March 4, 2024
How To Do DLL Injection: An In-Depth Cybersecurity Example

How To Do DLL Injection: An In-Depth Cybersecurity Example

February 8, 2024
Process Injection By Example: The Complete Guide

Process Injection By Example: The Complete Guide

January 24, 2024
How To Build Your Own: Python String Analysis for Malware Insights

How To Build Your Own: Python String Analysis for Malware Insights

November 10, 2023

StackZero

StackZero is a specialized technical blog dedicated to the realm of cybersecurity. It primarily provides insightful articles and comprehensive tutorials designed to educate readers on developing security tools. The blog encompasses a broad spectrum of subjects, starting from the foundational principles of cryptography and extending to more sophisticated areas such as exploitation and reverse engineering. This makes StackZero an invaluable resource for both beginners and professionals in the field of cybersecurity.
The blog covers a wide range of topics, from the basics of cryptography to the more advanced topics of exploitation and reverse engineering.

Tags

application security blind sqli blind sql injection bruteforce c cesar cipher command injection cryptography ctf cybersecurity debugging dom-based xss dvwa ethical-hacking ethical hacking exploitation file inclusion gdb hacking injection javascript malware malware analysis malware evasion network-security pentesting lab picoctf pico ctf python reflected xss reverse engineering sql sqli sql injection static analysis stored xss substitution substitution cipher vulnerable application web application security web exploitation web security windows windows api xss
  • About Me
  • Contacts
  • HomePage
  • Opt-out preferences
  • Privacy Policy
  • Terms and Conditions

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
Manage Cookie Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
Manage options Manage services Manage {vendor_count} vendors Read more about these purposes
View preferences
{title} {title} {title}
No Result
View All Result
  • Homepage
  • Cryptography and Privacy
  • Ethical Hacking
  • Reverse Engineering
  • Contacts
  • About Me