diff --git a/web-security/module.yml b/web-security/module.yml index 02a81fd..745be7b 100644 --- a/web-security/module.yml +++ b/web-security/module.yml @@ -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 diff --git a/web-security/xss-stored-html/DESCRIPTION.md b/web-security/xss-stored-html/DESCRIPTION.md new file mode 100644 index 0000000..ef193fc --- /dev/null +++ b/web-security/xss-stored-html/DESCRIPTION.md @@ -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! diff --git a/web-security/xss-stored-html/server b/web-security/xss-stored-html/server new file mode 100755 index 0000000..866e260 --- /dev/null +++ b/web-security/xss-stored-html/server @@ -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 = "
\nWelcome to pwnpost, the anonymous posting service. Post away!\n" + page += "\n" + + for post in db.execute("SELECT content FROM posts").fetchall(): + page += "