Skip to content

Commit

Permalink
new csrf 3
Browse files Browse the repository at this point in the history
  • Loading branch information
zardus committed Sep 2, 2024
1 parent 13e78cf commit 7a15f0f
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 2 deletions.
3 changes: 3 additions & 0 deletions web-security/csrf-reflected-alert/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Let's start putting a few things together...
A CSRF can lead to many things, including other injections!
Use the CSRF in this level to trigger a XSS and invoke an `alert("PWNED")` somewhere in `http://challenge.localhost`!
129 changes: 129 additions & 0 deletions web-security/csrf-reflected-alert/server
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/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

flag = open("/flag").read().strip() if os.geteuid() == 0 else "pwn.college{fake_flag}"

db = TemporaryDB()
# https://www.sqlite.org/lang_createtable.html
db.execute("""CREATE TABLE posts AS SELECT ? AS content, "admin" AS author, FALSE AS published""", [flag])
db.execute("""CREATE TABLE users AS SELECT "admin" AS username, ? as password""", [flag[-10:]])
# https://www.sqlite.org/lang_insert.html
db.execute("""INSERT INTO users SELECT "guest" as username, "password" as password""")
db.execute("""INSERT INTO users SELECT "hacker" as username, "1337" as password""")

@app.route("/login", methods=["POST"])
def challenge_login():
username = flask.request.form.get("username")
password = flask.request.form.get("password")
if not username:
flask.abort(400, "Missing `username` form parameter")
if not password:
flask.abort(400, "Missing `password` form parameter")

# https://www.sqlite.org/lang_select.html
user = db.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password)).fetchone()
if not user:
flask.abort(403, "Invalid username or password")

response = flask.redirect("/")
response.set_cookie('auth', username+"|"+password)
return response

@app.route("/draft", methods=["POST"])
def challenge_draft():
username, password = flask.request.cookies.get("auth", "|").split("|")
user = db.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password)).fetchone()
if not user:
flask.abort(403, "Invalid username or password")

if username == "admin":
flask.abort(400, "pwnpost no longer supports admin posting due to rampant flag disclosure")

content = flask.request.form.get("content", "")
# https://www.sqlite.org/lang_insert.html
db.execute(
"INSERT INTO posts (content, author, published) VALUES (?, ?, ?)",
(content, username, bool(flask.request.form.get("publish")))
)
return flask.redirect("/")

@app.route("/publish", methods=["POST"])
def challenge_publish():
username, password = flask.request.cookies.get("auth", "|").split("|")
user = db.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password)).fetchone()
if not user:
flask.abort(403, "Invalid username or password")

if username == "admin":
flask.abort(400, "pwnpost no longer supports admin posting due to rampant flag disclosure")

# https://www.sqlite.org/lang_update.html
db.execute("UPDATE posts SET published = TRUE WHERE author = ?", [username])
return flask.redirect("/")

@app.route("/ephemeral", methods=["GET"])
def challenge_ephemeral():
return f"""
<html><body>
<h1>You have received an ephemeral message!</h1>
The message: {flask.request.args.get("msg", "(none)")}
<hr><form>Craft an ephemeral message:<input type=text name=msg action=/ephemeral><input type=submit value=Submit></form>
</body></html>
"""

@app.route("/", methods=["GET"])
def challenge_get():
page = "<html><body>\nWelcome to pwnpost, now with users!<hr>\n"
username, password = flask.request.cookies.get("auth", "|").split("|")
user = db.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password)).fetchone()
if user:
page += """
<form action=draft method=post>
Post:<textarea name=content>Write something!</textarea>
<input type=checkbox name=publish>Publish
<input type=submit value=Save>
</form><br>
<form action=publish method=post><input type=submit value="Publish All Drafts"></form><hr>
"""

for post in db.execute("SELECT * FROM posts").fetchall():
page += f"""<h2>Author: {post["author"]}</h2>"""
if post["author"] == username:
page += "<b>YOUR DRAFT POST:</b> " + post["content"] + "<hr>\n"
elif username == "admin":
page += "<b>NON-ADMIN POST HIDDEN FOR SAFETY</b>"
elif post["published"]:
page += post["content"] + "<hr>\n"
else:
page += f"""(Draft post, showing first 12 characters):<br>{post["content"][:12]}<hr>"""
else:
page += """
<form action=login method=post>
Username:<input type=text name=username>
Password:<input type=text name=password>
<input type=submit name=submit value=Login>
</form><hr>
"""

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

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

import psutil
import urllib
import atexit
import time
import sys
import os

from selenium import webdriver
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException, UnexpectedAlertPresentException

os.setuid(os.geteuid())
os.environ.clear()
os.environ["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

options = FirefoxOptions()
options.add_argument("--headless")
service = FirefoxService(log_path="/dev/null")

browser = webdriver.Firefox(service=service, options=options)
atexit.register(browser.quit)

open_ports = { s.laddr.port for s in psutil.net_connections(kind="inet") if s.status == 'LISTEN' }
if 80 in open_ports:
print("Port 80 is open. Will connect to the service there.")
challenge_url = "http://challenge.localhost:80/"
flag = open("/flag").read().strip()
elif 8080 in open_ports:
print("Port 8080 is open. Will connect to the service there. 8080 is the debug port, so you will earn a fake flag.")
challenge_url = "http://challenge.localhost:8080/"
flag = "pwn.college{fake_flag}"
else:
print("Services don't seem to be running?")
sys.exit(1)

print(f"Visiting {challenge_url}")
browser.get(challenge_url)

print("Logging in as admin...")
browser.find_element(By.NAME, "username").send_keys("admin")
browser.find_element(By.NAME, "password").send_keys(flag[-10:])
browser.find_element(By.NAME, "submit").submit()
time.sleep(3)
print("Logged in!")

print("Visiting the attacker's website (http://hacker.localhost:1337/)...")
browser.get("http://hacker.localhost:1337/")
try:
WebDriverWait(browser, 1).until(EC.alert_is_present())
except TimeoutException:
print("Failure: JavaScript alert did not trigger...")
sys.exit(3)
else:
browser.switch_to.alert.accept()
if browser.current_url.replace(":80", "").startswith(challenge_url.replace(":80", "")):
print("Alert triggered! Your reward:")
print(flag)
else:
print("It looks like the alert did not come from {challenge_url}/ephemeral?")
6 changes: 4 additions & 2 deletions web-security/module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ challenges:
name: CSRF 1
- id: level-12
name: CSRF 2
- id: level-13
- id: csrf-reflected-alert
name: CSRF 3
- id: level-13
name: CSRF 4
description: Exploit a cross site scripting vulnerability to exfiltrate user session data
- id: level-14
name: CSRF 4
name: CSRF 5
description: Exploit a cross site scripting vulnerability to exfiltrate user data
- id: level-15
name: Web Pwn
Expand Down

0 comments on commit 7a15f0f

Please sign in to comment.