diff --git a/web-security/level-8/.config b/web-security/level-8/.config deleted file mode 100644 index 45a4fb75..00000000 --- a/web-security/level-8/.config +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/web-security/level-8/DESCRIPTION.md b/web-security/level-8/DESCRIPTION.md new file mode 100644 index 00000000..cbfbe3aa --- /dev/null +++ b/web-security/level-8/DESCRIPTION.md @@ -0,0 +1,10 @@ +In the previous examples, your injection content was first stored in the database (as posts), and was triggered when the web server retrieved it from the database and sent it to the victim's browser. +Because the data has to be stored first and retrieved later, this is called a _Stored_ XSS. +However, the magic of HTTP GET requests and their URL parameters opens the door to another type of XSS: _Reflected_ XSS. + +Reflected XSS happens when a URL parameter is rendered into a generated HTML page in a way that, again, allows the attacker to insert HTML/JavaScript/etc. +To carry out such an attack, an attacker typically needs to trick the victim into visiting a very specifically-crafted URL with the right URL parameters. +This is unlike a Stored XSS, where an attacker might be able to simply make a post in a vulnerable forum and wait for victims to stumble onto it. + +Anyways, this level is a Reflected XSS vulnerability. +Fool the `/challenge/victim` into making a JavaScript `alert("PWNED")`, and you'll get the flag! diff --git a/web-security/level-8/run b/web-security/level-8/run deleted file mode 120000 index 84ba55b9..00000000 --- a/web-security/level-8/run +++ /dev/null @@ -1 +0,0 @@ -../run \ No newline at end of file diff --git a/web-security/level-8/server b/web-security/level-8/server new file mode 100755 index 00000000..94aaf1de --- /dev/null +++ b/web-security/level-8/server @@ -0,0 +1,19 @@ +#!/opt/pwn.college/python + +import flask +import os + +app = flask.Flask(__name__) + +@app.route("/", methods=["GET"]) +def challenge_get(): + return f""" + +

pwnmsg ephemeral message service

+ The message: {flask.request.args.get("msg", "(none)")} +
Craft a message:
+ + """ + +app.secret_key = os.urandom(8) +app.run("challenge.localhost", 8080 if os.geteuid() else 80) diff --git a/web-security/level-8/victim b/web-security/level-8/victim new file mode 100755 index 00000000..a676fe48 --- /dev/null +++ b/web-security/level-8/victim @@ -0,0 +1,68 @@ +#!/opt/pwn.college/python + +import contextlib +import urllib +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 + +os.environ["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +@contextlib.contextmanager +def run_browser(): + options = FirefoxOptions() + options.add_argument("--headless") + + # workaround for ubuntu + if os.path.exists("/snap/bin/geckodriver"): + service = FirefoxService(executable_path="/snap/bin/geckodriver", log_path="/dev/null") + else: + service = FirefoxService(log_path="/dev/null") + driver = webdriver.Firefox(service=service, options=options) + + try: + yield driver + finally: + driver.quit() + +if len(sys.argv) <= 1: + print(f"Usage: {sys.argv[0]} URL") + sys.exit(1) + +url = sys.argv[1] +url_arg_parsed = urllib.parse.urlparse(url) +try: + assert url_arg_parsed.hostname == "challenge.localhost", "hostname should be 'challenge.localhost'" + assert url_arg_parsed.port in {None, 80, 8080}, "port should be 80 or 8080" +except AssertionError as e: + print(f"Invalid URL: {e}") + sys.exit(2) + +print("Visiting the URL!") +with run_browser() as browser: + try: + browser.get(url) + print("URL loaded...") + WebDriverWait(browser, 1).until(EC.alert_is_present()) + except TimeoutException: + print("Failure: JavaScript alert did not trigger...") + sys.exit(3) + except WebDriverException as e: + if "can%E2%80%99t%20establish%20a%20connection" in str(e): + print("Connection error! Is the service running?") + else: + print(f"Failure: {e}...") + sys.exit(4) + else: + if url_arg_parsed.port is None or url_arg_parsed.port == 80: + print("Alert triggered! Your flag:") + print(open("/flag").read()) + else: + print("Alert triggered! Now do it on the real service (port 80)!") diff --git a/web-security/module.yml b/web-security/module.yml index 4744c285..f9b9cc7a 100644 --- a/web-security/module.yml +++ b/web-security/module.yml @@ -36,7 +36,6 @@ challenges: name: XSS 2 - id: level-8 name: XSS 3 - description: Exploit a cross site scripting vulnerability - id: level-9 name: XSS 4 description: Exploit a cross site scripting vulnerability with more complicated context