Are YOU todayโs unlucky contestant in Cache! It! To! Win! It??????? Find out below!
cache-it-to-win-it.chall.lac.tf
Note: do NOT perform any sort of denial-of-service attack against the web server or databases, directly or indirectly.
By taking a look at the attached files, I understood that the Flask application has a mechanism to assign unique UUIDs to users and allows them to check their progress to earn a reward (a โflagโ). However, some vulnerabilities allow attackers to manipulate the UUIDs and bypass the caching system and the database to speed up the process. The normalize_uuid function mishandles the UUIDs, introducing inconsistencies, especially with the use of invisible characters (like zero-width characters). Manipulated UUIDs can pass the normalization and create different variants for the same key:
I noticed that simply adding a URL-encoded space character %20 was enough to have a new valid UUID:
Crafted UUID
As we can see, this generates a valid UUID, and since itโs a different key, the cache check fails, allowing us to win. I continued adding %20, but I noticed that a maximum of 13 wins can be achieved with this technique, so I started looking for other ways to exploit this vulnerability. In fact, as we can see from the following lines of code:
The check is done up to a maximum of 64 characters, which is why I was being blocked after 13 wins with this method. So, starting to look for other variants, I tried using zero-width characters, like \u200, and by doing so, I could construct enough variants of the UUID to reach a total of 100 wins. In addition to UUID normalization, thereโs another issue that allowed me to exploit this vulnerability, which is that the caching system doesnโt account for changes made in the database, causing inconsistencies. The attacker can manipulate the cache to avoid proper updates and exploit the latency between the two systems.
1
2
3
4
5
@cache.cached(timeout=604800,make_cache_key=make_cache_key)defcheck():user_uuid=request.args.get("uuid")run_query("UPDATE users SET value = value + 1 WHERE id = %s;",(user_uuid,))res=run_query("SELECT * FROM users WHERE id = %s;",(user_uuid,))
The attacker manipulates the cache key to achieve a โcache hitโ with outdated data, thus avoiding the proper increment of the counter in the database. This allows the attacker to accumulate wins without respecting the imposed limits. The attacker sends a large number of requests while manipulating the cache to bypass the limits (e.g., using zero-width characters to avoid cache hits). This is possible because the victory counter is incremented without any temporal or logical checks.
๐ฌ Vulnerability Analysis
Potential Vulnerabilities
Web Cache Poisoning
๐ฏ Solution Path
Exploitation Steps
Initial setup
Now that we understand the type of vulnerability and how to exploit it, we can move on to the exploitation phase.
Exploitation
Obviously, creating 100 UUID variants manually seemed like a bit of a hassle, so I wrote a Python exploit to automate the process.
Flag capture
Manual Flag
๐ ๏ธ Exploitation Process
Approach
The exploit generates 99 valid UUID variants and sends them to claim the win. Once it reaches 0, it checks the response for the flag and extracts it using a regex, printing it.