Skip to content

Commit

Permalink
add new first XSS level
Browse files Browse the repository at this point in the history
  • Loading branch information
zardus committed Sep 1, 2024
1 parent 0f59bb1 commit af42c75
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
8 changes: 5 additions & 3 deletions web-security/module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ challenges:
name: SQLi 4
- id: level-7
name: SQLi 5
- id: level-8
- id: xss-stored-html
name: XSS 1
- id: level-8
name: XSS 2
description: Exploit a cross site scripting vulnerability
- id: level-9
name: XSS 2
name: XSS 3
description: Exploit a cross site scripting vulnerability with more complicated context
- id: level-10
name: XSS 3
name: XSS 4
description: Exploit a cross site scripting vulnerability to cause a user action
- id: level-11
name: SSRF 1
Expand Down
33 changes: 33 additions & 0 deletions web-security/xss-stored-html/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Semantic gaps can occur (and lead to security issues) at the interface of any two technologies.
So far, we have seen them happen between:

- A web application and the file system, leading to path traversal.
- A web application and the command line shell, leading to command injection.
- A web application and the database, leading to SQL injection.

One part of the web application story that we have not yet looked at is the _web browser_.
We will remedy that oversight with this challenge.

A modern web browser is an extraordinarily complex piece of software.
It renders [HTML](https://en.wikipedia.org/wiki/HTML), executes [JavaScript](https://en.wikipedia.org/wiki/JavaScript), parses [CSS](https://en.wikipedia.org/wiki/CSS), lets you access pwn.college, and much much more.
Specifically important to our purposes is the HTML that you have seen being generated by every challenge in this module.
When the web application generated paths, we ended up with path traversals.
When the web application generated shell commands, we ended up with shell injections.
When the web application generated SQL queries, we ended up with SQL injections.
Do we really think HTML will fare any better?
Of course not.

The class of vulnerabilities in which injections occur into client-side web data (such as HTML) is called _Cross Site Scripting_, or XSS for short (to avoid the name collision with Cascading Style Sheets).
Unlike the previous injections, where the victim was the web server itself, the victims of XSS are _other users of the web application_.
In a typical XSS exploit, an attacker will cause their own code to be injected into (typically) the HTML produced by a web application and viewed by a victim user.
This will then allow the attacker to gain some control within the victim's browser, leading to a number of potential downstream shenanigans.

This challenge is a very first step in this direction.
As before, you will have the `/challenge/server` web server.
This challenge explores something called _Stored XSS_, which means that data that you store on the server (in this case, posts in a forum) will end up being shown to a victim user.
Thus, we need a victim to view these posts!
You will now have a `/challenge/victim` program that simulates a victim user visiting the web server.

Set up your attack and invoke `/challenge/victim` with the URL that will trigger the Stored XSS.
In this level, all you have to do is inject a textbox.
If our victim script sees three textboxes, we will give you the flag!
43 changes: 43 additions & 0 deletions web-security/xss-stored-html/server
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/opt/pwn.college/python

import tempfile
import sqlite3
import flask
import os

app = flask.Flask(__name__)

class TemporaryDB:
def __init__(self):
self.db_file = tempfile.NamedTemporaryFile("x", suffix=".db")

def execute(self, sql, parameters=()):
connection = sqlite3.connect(self.db_file.name)
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
result = cursor.execute(sql, parameters)
connection.commit()
return result

db = TemporaryDB()
# https://www.sqlite.org/lang_createtable.html
db.execute("""CREATE TABLE IF NOT EXISTS posts AS SELECT "First Post!" AS content""")

@app.route("/", methods=["POST"])
def challenge_post():
content = flask.request.form.get("content", "")
db.execute("INSERT INTO posts VALUES (?)", [content])
return flask.redirect(flask.request.path)

@app.route("/", methods=["GET"])
def challenge_get():
page = "<html><body>\nWelcome to pwnpost, the anonymous posting service. Post away!\n"
page += "<form method=post>Post:<input type=text name=content></form>\n"

for post in db.execute("SELECT content FROM posts").fetchall():
page += "<hr>" + post["content"] + "\n"

return page + "</body></html>"

app.secret_key = os.urandom(8)
app.run("challenge.localhost", 8080 if os.geteuid() else 80)
32 changes: 32 additions & 0 deletions web-security/xss-stored-html/victim
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/opt/pwn.college/python

import requests
import urllib
import sys
import re

url = sys.argv[1]
url_parsed = urllib.parse.urlparse(url)
try:
assert url_parsed.hostname == "challenge.localhost", "hostname should be 'challenge.localhost'"
assert url_parsed.port in {None, 80, 8080}, "port should be 80 or 8080"
except AssertionError as e:
print(f"Invalid URL: {e}")
sys.exit(2)

if len(sys.argv) <= 1:
print(f"Usage: {sys.argv[0]} URL")
sys.exit(1)

print("Visiting the URL...")
try:
num_inputs = len(re.findall(r"<input[^<]*>", requests.get(url, timeout=1).text))
if num_inputs <= 1:
print("You did not inject an <input> textbox...")
elif url_parsed.port == 8080:
print("You got it! Now do it for real (on port 80).")
else:
print("You got it! Here is your flag:")
print(open("/flag").read())
except requests.exceptions.ConnectionError:
print("Connection error... Is the service running?")

0 comments on commit af42c75

Please sign in to comment.