Skip to main content

cancri-sp picoCTF 2023 Solution

A hard, long-unsolved web exploitation challenge built around a Server-Side Request Forgery in a large custom application.

Published: April 26, 2023Updated: August 25, 2026

Description

A 500-point web challenge that went unsolved during picoCTF 2023, with no public solution since. The live instance ships an unusually large source archive (around 1 GB), which points at a multi-service / multi-tier application rather than a single small app. The page below is an honest, clearly-labeled methodology for the most likely vulnerability class (an SSRF-to-internal-service chain), not a verified step-by-step solve.

Pull the live instance's source.tar.gz (it is large, around 1 GB; budget disk and bandwidth) and map the services it bundles before touching the app.

Open the challenge URL and use Burp Suite to intercept and replay requests.

bash
# Navigate to http://<HOST>:<PORT_FROM_INSTANCE>
bash
# Download and unpack the (large) source bundle, then enumerate the internal services it defines.

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
Status: unsolved, no public solution. This challenge was not solved during the competition, so the steps below are inferred from the challenge name and the large multi-service source bundle, not confirmed against a working solve. Treat the concrete endpoints and payloads as illustrative placeholders. The most defensible single guess for the bug class is an SSRF pivot into an internal service (with a SAML/SSO service-provider bypass as a runner up, given the "sp" in the name). The SSRF for CTF post covers the full methodology: confirming the sink, sweeping internal ranges, and chaining with a secondary bug to exfiltrate.
  1. Step 1Identify the SSRF vector
    Observation
    The challenge ships a roughly 1 GB multi-service source archive. Somewhere in there the app takes user-supplied URLs or resource identifiers to fetch remote content, which is the classic SSRF entry point in a multi-tier application.
    Find an endpoint that fetches a URL or resource specified by user input. This is the SSRF entry point.
    Learn more

    Server-Side Request Forgery (SSRF) occurs when a server makes an HTTP (or other protocol) request to a URL controlled by the attacker. Attackers use SSRF to access internal services that are not exposed to the public internet: internal APIs, metadata services (e.g., AWS IMDSv1 at 169.254.169.254), and services bound to localhost.

    Look for parameters with names like url, endpoint, src, href, redirect, or fetch. Also look for features that fetch remote content: link previews, webhook validators, PDF generators, and image importers are classic SSRF surfaces.

    Test with a URL you control (e.g., a requestbin or webhook.site URL) to confirm the server is making outbound requests with your input.

  2. Step 2Pivot to internal services via SSRF
    Observation
    With SSRF confirmed, the server makes outbound requests using your input. Probe loopback and private address ranges for unauthenticated internal services that the public internet cannot see.
    Use the SSRF vector to probe internal addresses (127.0.0.1, 10.0.0.0/8) and find a running internal service that exposes sensitive data.
    bash
    # Try internal endpoints:
    bash
    curl 'http://<HOST>/fetch?url=http://127.0.0.1/'
    bash
    curl 'http://<HOST>/fetch?url=http://127.0.0.1:8080/'
    bash
    curl 'http://<HOST>/fetch?url=http://169.254.169.254/latest/meta-data/'
    What didn't work first

    Tried: Sending the SSRF payload directly in the browser address bar instead of through curl or Burp Suite

    The browser issues the request from your machine, not the server, so you reach only the public addresses you already had. SSRF works when the backend fetches the URL, so send the parameter to the vulnerable endpoint and let the server make the connection.

    Tried: Using http://localhost/ instead of http://127.0.0.1/ when the server blocks the string 'localhost'

    Many SSRF filters block the hostname 'localhost' but not its numeric equivalents. If the raw IP is also filtered, try decimal (2130706433), hex (0x7f000001), or IPv6 ([::1]). Stopping at localhost means missing the bypass entirely when a denylist is in place.

    Learn more

    Once SSRF is confirmed, enumerate internal services by trying common ports (80, 8080, 8443, 3000, 5000, 6379 for Redis, 27017 for MongoDB, 9200 for Elasticsearch). The server's response body, status code, and response time reveal whether each port is open.

    Many internal services have no authentication because they are assumed to be unreachable from outside. An internal admin panel, debug endpoint, or metadata service may immediately disclose sensitive data when accessed through SSRF.

    SSRF filter bypasses include: using alternative IP representations (0x7f000001, 2130706433, or [::1] for 127.0.0.1), protocol switches (file://, dict://, gopher://), URL redirectors, and DNS rebinding.

  3. Step 3Chain with injection to read the flag
    Observation
    The internal service reachable through the SSRF takes parameters that look like file paths or query inputs. Chain the pivot with path traversal or SQL injection to turn network access into direct disclosure.
    If the internal service is vulnerable to injection (SQLi, command injection, path traversal), chain the SSRF with injection to read the flag file.
    bash
    # Example: SSRF to internal API + path traversal:
    bash
    curl 'http://<HOST>/fetch?url=http://127.0.0.1:8080/read?file=../../flag'
    bash
    # Example: SSRF to internal API + SQLi:
    bash
    curl 'http://<HOST>/fetch?url=http://127.0.0.1:8080/users?id=1+UNION+SELECT+flag+FROM+flags--'

    Expected output

    picoCTF{...}
    What didn't work first

    Tried: Attempting path traversal directly against the external-facing host before confirming an internal service handles file reads

    The external app almost certainly sanitizes or forbids traversal. It only succeeds against the internal service behind the SSRF, which has no such validation. Pivot through the SSRF channel first and aim at the internal service's file-read or API endpoint.

    Tried: Injecting SQL directly into the external app's parameters instead of tunneling the SQLi through the SSRF payload

    The external app is the SSRF vector, not the injection target; the internal service on loopback is what exposes the vulnerable query. Inject into the outer app's parameters and you hit a code path with no SQL in it. URL-encode the payload inside the url parameter so the internal service receives and evaluates it.

    Learn more

    Chained vulnerabilities - using one bug to access a second, more impactful bug - are common in real-world attacks. SSRF is particularly useful as a pivot because internal services often have weaker input validation than internet-facing ones.

    In this challenge, the SSRF lets you reach an internal service, and that service has a secondary vulnerability (injection, traversal, or a sensitive endpoint) that gives you the flag. Enumerate the internal service's routes and parameters carefully.

    SSRF was included in the OWASP Top 10 in 2021 (A10:2021). High-profile real-world SSRF incidents include the Capital One breach (2019), which used SSRF against the AWS instance metadata service to steal IAM credentials.

  4. Step 4Exfiltrate the flag
    Observation
    The internal service relays its response back through the SSRF fetch endpoint. Once the injection chain lands, the flag appears in that relayed response.
    Read the flag from the internal response and submit it.
    Learn more

    The flag will appear in the server's response once the chain is successful. If the internal service returns the flag directly in its HTTP response, your SSRF fetch endpoint will relay it back to you. If the internal service only writes the flag to a file, use a secondary path traversal or file-read endpoint to fetch it.

    When exfiltration through the same SSRF channel is blocked (e.g., response size limits or content filtering), consider out-of-band channels: cause the internal service to make a DNS lookup or HTTP request to a server you control, encoding the flag in the subdomain or URL path.

Interactive tools
  • URL Encoder / DecoderEncode and decode URL-encoded (percent-encoded) strings. Useful for web exploitation challenges involving query parameters, form data, and HTTP headers.

Flag

Reveal flag

picoCTF{...}

Challenge unsolved during the competition; this page is a methodology template for SSRF + injection chains, not a verified step-by-step solution. Concrete endpoints and payloads will vary.

Key takeaway

Server-Side Request Forgery exploits a server's willingness to fetch attacker-controlled URLs, turning it into a proxy onto internal services the attacker cannot address directly. Those services often skip authentication entirely, assuming network isolation protects them, so one SSRF vector grants unauthenticated reach into admin panels, cloud metadata endpoints, or databases. Chaining it with a second bug on the internal service, traversal, injection, an exposed debug route, is how a network pivot becomes data exfiltration, as the 2019 Capital One breach demonstrated.

Related reading

Useful tools for Web Exploitation

Where to go next