hashimXSS
Cross-Site-Scripting practicing game

Introduction
Hello everyone, I’m excited to share my first story with you. I hope you enjoy it! hashimXSS serves as a Cross-Site-Scripting (XSS) practicing game, comprising five challenges that explore the three types of XSS attacks. Each level presents a varying level of difficulty, offering an engaging journey through the world of web security.
Easy..Peasy! ( Level 1 — Reflected XSS )

Let’s test some input, such as [ abc”>< ]. Notice how our input is entirely printed on the screen and also visible in the page’s source code.

Upon testing a payload such as [ <img src=x onerror=alert(1)> ], the site has now detected our XSS payload.

So, let’s break down our payload part by part. We’ve discovered that the [ alert() ] function is being filtered in some way. To bypass this, we can encode it into HTML entities, as the function’s place now resides within an HTML tag. Thus, our payload becomes [ <img src=x onerror=alert(1)> ]. And voilà! It’s working.

TryReflectMe ( Level 2 — Reflected XSS )

In this challenge, our input will be in the GET parameter within the URL. Let’s try [ abc”>< ]. It appears that our input is considered safe for the site. However, upon inspecting the source code page, we find that it’s printed into the value attribute.

Now, let’s test a payload such as [ ‘onclick=alert(1);x=’ ]. Ah, it seems like it has been detected! Let’s analyze which part of this payload has been filtered. It appears that the challenge can detect the function [ alert() ]. So, let’s try something new, like using the function [ eval() ] in JavaScript. By writing [ eval(“aler”%2b”t(1)”) ], we achieve the same outcome as [ alert(1) ], but our program can’t detect it. (Note: ‘%2b’ represents ‘+’, as it is URL encoded). Thus, our payload becomes [ ‘ onclick=eval(“aler”%2b”t(1)”); x=’ ]. And boom, it worked!

Leave me a comment..Plz ( Level 3 — Stored XSS)

As we observe, there’s a simple comment form in front of us. Let’s test some inputs like (name: test”><, comment: comm”><) and hit the send button. Alright, it’s evident that everything we wrote is printed out on the screen. Let’s delve deeper into our source code page. Hmm, evidently, our name has been encoded to HTML special characters, and our comment is the same. Okay, I see that we can utilize the comment box.

Now, let’s try entering [ confirm(64) ] into our comment box and see what happens. Alright, it appears that the program detects the function and encodes it using BASE64. Hmm, how can we bypass that?

I have some good ideas, but I always prefer the simplest one. Let’s try encoding our function [ confirm(64) ] to its HTML equivalent. So our output now is ( confirm(64) ).

Alright, that sounds like a plan. Let’s try our payload. Remember, every HTML encoded text is only interpreted correctly within HTML tags! Our payload could be [ <img src=x onerror=confirm(64)> ]. Excellent, it worked as expected.

DOM..DOM..DOM ( Level 4 — DOM Based XSS )

A page of posts? Ha$h_1s_5o_Co0l? Hmm, I see. Now, I sense that the page is taking user input after the hash sign ( # ). Let’s try inputting 5, for example.

Alright, first, the site scrolled down to the post with the ID 5. After reloading the page, we can observe that our text (5) is reflected on our screen. Let’s take a closer look inside the page’s source code. Hmm, by inspecting the script file (DOM.js), we might find some valuable information.

The summary of that script and the `isThere()` function is as follows: The function takes two arguments (pattern, input), searching for a pattern in the input and returns an array of matches. If the length of this array is greater than 0 (meaning there are matches), it indicates that the input follows that pattern (which is essentially a regular expression used to filter some blocked words). If the input doesn’t follow the pattern, the function will reflect the input onto the screen through `document.write()`.
Now that we know the program blocks words like “alert”, “src”, etc., let’s brainstorm ideas for the payload. First, let’s try the `<script>` tag.
Okay, it’s not detected. Let’s try this payload: `<script>alert(“Ha$h_1s_5o_Co0l”)</script>`. Alright, `alert()` is detected. Let’s attempt using `eval()`.

Great! It seems like [ eval() ] is not detected. Now, we can try this payload: [ test<script>eval(‘al’%2b’ert(“Ha$h_1s_5o_Co0l”)’)</script> ]. And voila, it worked!

Mission: Impossible ( Level 5 — Should be SECURED )

In this challenge, I’ve made numerous attempts to ensure that all our efforts would be rendered useless. So, let’s try typing our previous payload. As expected, it’s incredibly useless! 😄

Let’s take a closer look at the PHP code.

First things first, I’ve implemented the Content Security Policy (CSP) header to ensure that scripts are executed only by a trusted domain http://localhost/
. However, you’ll notice that I’ve added 'unsafe-inline'
to the header. This was necessary because I encountered a problem when attempting to execute a script using the echo()
function.

Another attempt to enhance the challenge’s difficulty is by implementing the X-XSS-Protection
header. Additionally, I've incorporated client-side user input validation by limiting the maximum length of user input.


Here, I encoded special characters using htmlspecialchars()
before reflecting them on the screen. Then, I employed a function called preg_match_all()
to filter certain words and block them using if statements.

Last updated