We, SOter14, spotted 1st place in Middle East and North Africa region, and 13th worldwide out of more than 900 participating teams and got invited to attend the finals, onsite in ABU DHABI - Emirates. We managed to solve 27 out of 30 task, and we’ve got solutions for 2 out of the 3 remaining tasks (1 crypto, 1 pwn) working locally but had issues with remote server, hard luck next time!
Word wide web (406 solves)
Given a webpage with lot of <a> tags, only one tag contains href attribute with valid path to the next page. Recursively, every new page visited contains the same thing but with new path to the next page.
There is a unique feature of converting markdown to pdf, at first when I fetched external ressources and got the result in pdf, I tried to look with ns lookup for what cloud the challenge is hosted on. Once I discovered that it’s AWS, one Idea instantly came to my mind : accessing aws meta-data through 169.254.169.254
import json from application.database import User, Image, db, migrate_db from application.util import admin_only, generate import sys, os from subprocess import PIPE, run
from flask import Blueprint, jsonify, redirect, render_template, request, current_app, send_file from flask_login import current_user, login_required, login_user, logout_user from werkzeug.utils import secure_filename
import logging import logging.config
logger = logging.getLogger(__name__)
web = Blueprint('web', __name__) api = Blueprint('api', __name__)
""" Front-end of the site is currently in development but we've put placeholders for now... """
#One of the volunteers keeps messing with the logger config. Doing this as a temporary fix so I can fix remotely... #If you're the one doing it and reading this, please stop. @api.route('/log_config', methods=['POST']) @login_required deflog_config(): ifnot request.is_json: return response('Missing required parameters!'), 401
data = request.get_json() file_name = data.get('filename', '')
#WORK IN PROGRESS """@web.route('/admin') @login_required @admin_only def admin_panel(): pass """
# One of the guys cant get his VPN working on his laptop and complains he needs to do some checks remotely... @api.route('/run_command', methods=['POST']) @login_required @admin_only defremote_exec(): ifnot request.is_json: return response('Missing required parameters!')
data = request.get_json() command = data.get('command','') result = run(command, stdout=PIPE, stderr=PIPE, shell=True)
return response(result.stdout.decode())
Running the app locally
Getting also my local app exposed publicly, helped me switch smoothly between machines.
Notes
I used postman for sending requests
/api/register for signing up a new user
/api/login to authenticate the newly created user
The presence of /log_config endpoint looks very juicy, as there is an upload feature (+ we can get the path of the uploaded file through /gallery and read it through /download_image endpoints), *** it seems that the whole task is about changing an existing config file. ***
app.post("/waitlist", (req, res) => { res.send("Sorry, the waitlist is currently closed."); });
// Emergency alert email notification system for all residents app.post("/admin/alert", (req, res) => { if (req.body.msg) { const proc = spawn("mail", ["-s", "ALERT", "all_residents@localhost"], {timeout}); proc.stdin.write(req.body.msg); proc.stdin.end(); setTimeout(() => { kill(proc.pid); }, timeout); }
res.end(); });
app.listen(port);
Server.js
And for varnish config, it’s clear that it’s ignoring every request, to /admin and without the required token. In our case we don’t care with the condition statements because our goal is to make the proxy forward the request to the server no matter if the condition is verified or not. Also, note that /admin/alert in the code above spawns an execution of os command, which means there is a potentiel of arbitrary os injection to retrive the flag.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
vcl 4.1;
backend default { .host = "127.0.0.1:8082"; }
sub vcl_recv { if (req.url ~ "/admin" && !(req.http.Authorization ~ "^Basic TOKEN$")) { return (synth(403, "Access Denied")); } }
sub vcl_synth { if (resp.status == 403) { set resp.body = resp.reason; return (deliver); } }
varnish.vcl
Eye-catching part
While reading config files, I noticed the use of to versions of HTTP (http1.1 and http2) .. To be honest I was highly focusing on every detail related to request sending and how the server receives / interprets it.
Also, when trying a simple POST request to /waitlist that it’s allowed, it won’t work until you add the -k option (for insecure connection) + –http1.1 to force http1.1 use.
The trick behind this chall is stated here , I really appreciate his approach.
Solution
One last thing to mention is that there is great option in mail command that you can find interesting regarding the arbitrary os command injection, while reading its manual.
When dealing with burp suite repeater, try to disable auto updating for content-length
LOST IN AMAZON (19 solves)
Only 19 out of 900 teams succeeded in breaking down This task. Luckily great part of the Solution was my first approach for solving MY LITTLE WEBSITE task, mentioned above.
One single static page, there is no source code for the task and no interesting client side stuff which means that we should aim for fuzzing directories and files. Hopefully, the organizers released a small version of the mostly known wordlist rockyou.txt : that’s a confirms that fuzzing is the starting point of the challenge.
/secret revealed a jwt that we’ll need later
Fuzzing recursively with depth of 2 seems to reveal smthg, because we can’t do nothing at this level.
/developer/heaven
We need to add a cookie to authenticate
NICE!! ez ssrf which means ez win!
Unlike my first attempt in MyLittleWebsite. This time we don’t have security Token, odds are we have access to S3 bucket with that role.
Adding the Secret and Id keys to the config file ~/.aws/credentials
Final Solution
Conclusion
I want to thank the organizers / authors for their effort. kudos to Ophelious and T0m7r00z for pushing this to the limit. All my teammates are heros! PROUD TUNISIANS. SOter14 ftw <3