Intro
Near the end of 2024, TrustFoundry introduced a game called “spot the bug” to its weekly internal stand-up meetings. The idea of the game is to present a relatively small snippet of code (that can ideally fit legibly within a single screenshot) that contains at least one vulnerability. Folks take some time to review the code and try to spot the bug, and then discuss their solutions (or lack thereof) on the weekly stand-up.
I would be remiss not to mention that this was directly inspired by the “Spot the Vuln” game on the Dayzerosec podcast. We even began by working through some of the challenges they created. If you haven’t seen their challenges before, be sure to check them out! Here’s a starting point: https://dayzerosec.com/blog/2023/07/07/def-con-spot-the-vuln-shirts.html I’ve also seen plenty of other occurrences of the same game out there, so if you enjoy this, you can find more with a little searching.
For us, the goal of playing this game is partially to promote some fun discussion and collaboration, but also to build up strong bug pattern recognition skills. Some ancillary fun of the game for me has been highlighting that seemingly different vulnerabilities can be caused by roughly the same code patterns. With enough time, hopefully we’ll all continue to really mentally ingrain these patterns, making us more likely to identify similar flaws during code reviews.
Before long, we moved to creating custom challenges for the game, which meant either custom code/pseudocode or pulling real-world vulnerable code that illustrated a particularly interesting flaw. We’d like to begin sharing some challenges we’ve used internally. For our team, this game is meant to be played for a short period of time once per week, so the challenges are not designed to be anywhere remotely near the level of difficulty of a true Capture the Flag (CTF) challenge. You’re encouraged to take your time and finalize an answer before looking at the solution. You might want to write your answer down to keep yourself honest.
Challenge
Spot the bug in the following pseudocode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# receive and parse a SAML assertion during authentication def validate_saml_assertion(assertion): assertion_details = extract_details_from_assertion(assertion) username = assertion_details.username tenant = assertion_details.tenant timestamp = assertion_details.timestamp if !assertion_details.signature: raise ValueError("Signature not valid") if !timestamp_within_last_5_minutes(timestamp): raise ValueError("Assertion too old") if check_user_and_tenant(username,tenant): complete_auth_flow() else: raise ValueError("Invalid username / tenant combo") |
Solution (spoilers below this point):
Because these challenges are intended to be bite-size, there’s obviously not much room for adding context. However, from what little we have, this looks like part of a Single-Sign On (SSO) handler that’ll parse a SAML assertion received after a user has authenticated with an identity provider. The function validate_saml_assertion() is probably responsible for ensuring that the SAML assertion hasn’t been modified by an attacker (or expired or otherwise been improperly formed).
We don’t see the implementations of functions called within validate_saml_assertion() , which tells us that the specific details of those functions won’t be important (which is another luxury afforded by the structure of the game that we wouldn’t receive in the real world). The function begins by extracting details from the SAML assertion, which we can assume is parsing out all the relevant fields. The username, tenant and timestamp are all saved from the parsed assertion details. Then there are a few checks that are intended to make sure the signature is valid, the timestamp is recent enough, and that the provided username and tenant are valid (and the username belongs to the tenant, presumably).
The bug is on lines 7-8. The signature isn’t actually being checked for validity here, despite what the error message says — the check only ensures that a signature exists, not that the signature is valid. No validation occurs at any point. An attacker could forge a SAML assertion by modifying it, and so long as the signature field remained present, this check would pass, even though the signature would no longer be valid for the assertion.
Conclusion:
Hopefully you enjoyed this first challenge. If you’ve been trying to find a way to increase some technical discussion and collaboration on your own team, implementing something similar to this may be worthwhile; it’s been popular internally and we plan to continue it. We plan to publish more challenges in the future.