I did not solve this challenge during the CTF, so I put some content together to understand how it was solved.
YouTube Writeup
Visual workflow
Summary
This challenge demonstrates a classic prototype pollution vulnerability. By understanding how the merge function works we can exploit it to inject malicious properties into the global scope. The key take away is the emphasis on securely handling user input and being cautious about functions that dynamically merge or copy attributes as they can be vectors of attack.
Technical Workflow Summary
References
https://blog.abdulrah33m.com/prototype-pollution-in-python/ https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
Challenge Description
Website Enumeration
Initially viewing the website reveals very little. With the ability to interact with the register and login functions, we are forced to register an account as our attack surface is lacking otherwise.
We can see that some new menu options appear, dashboard and feedback. The dashboard area just shows existing blog posts. The feedback form however can be interacted with and thus represents our attack surface.
Taking a look at the feedback form doesn't tell us much beyond the fact that our submissions are stored forever which is suggestive to me that we don't have to worry about a race or timing condition.
Bupring the request we see that our feedback is sent as JSON data to the save_feedback endoint.
Source Code Analysis
routes.py helps us understand the flag is stored as an environment variable and can be retrieved via route with /get_flag if the users flag environment variable is set to true. The feedback form is actually storing the user data.
utils.py reveals that the merge function is responsible for copying attributes from the src to destination opbject. The merge function recursively merges dictionaries, updating the destination object with values from the source.
routes.py - 1/3: Reveals the flag is stored as an environment variable.
routes.py - 2/3: Reveals the flag can be access from the /get_flag endpoint, however the flag variable has to return true. The get_flag function relies on the flag global variable.
routes.py - 3/3: reveals that we want to grab user input via the feedback form and save it as an attribute, and suggesting language that attributes can be created on the fly through the merge function which is responsible for the saving and creation of the new attributes.
utils.py 1/1: reveals that the merge function is responsible for copying attributes from the src to destination object. The merge function recursively merges dictionaries, updating the destination object with values from the source.
Vulnerability Identification
The vulnerability here is prototype pollution. This occurs when an attacker manipulates the structure of an object by injecting properties that affect the entire prototype chain which effects the behavior of a program.
The prototype chain is a workflow that lets objects inherit properties from other objects. Every object can have only one prototype object from which it may inherit properties from. Each object prototype can have its own prototype, creating a chain of inheritance.
Prototype Analogy: The Hotel Registry
Imagine a hotel with a digital registry system where guests can enter their details when they check in. The hostel has a default template for each guests details, etc name, room number, special requests. The system automatically merges these details into the guests profile.
The merging process.
The hostel has a default template for guests.
Name: Guests
room: Not assigned
Special requests: unknown
When a guest checks in, they provide their details
Name: Bob
room: 5
Special requests: unknown
extra_towels: true
The system merges the default template with the guests input creating a profile for bob. This results due to lack of user input sanitization.
Malicious input can be padded that affects the hotels entire system.
Name: Bob
room: 5
Special requests: unknown
extra_towels: true __System_{ fire_alarm: false }
Merging the malicious input:
The system now merges the input like it does for all the guests, however system is called and an environment variable for fire_alarm is now false either for the backend user calling the process, or possible the entire hotel.
Building the Payload
Identifying the get_flag function:
There is a get_flag function we have observed above, that checks if the value of flag which is defined as an environment variable is set to "true", and returns the flag if so. The value of the flag is not directly controllable, but is controlled through a class pollution vulnerability.
Crafting a payload
Below is our default json data that is rendered from the feedback form, this is where we will build our payload. { "title":"aaa", "content":"aaaa", "rating":"5", "referred":"dsag" }
__class_
Purpose: Access the class of object "feedback_obj"
Use Case: Can be used to manipulate the class "Feedback"
__init_
Purpose: The initializer method for a class.
Use Case: Use to modify application behaviour during object creation.
__globals_
Purpose: Access global variable flag within the context.
Use Case: This lets us manipulate the global flag variable to true
The payload is constructed as follows:
Exploitation Phase
Here we burp the request and enter our payload, and we see the response from the server shows as true.
We then attempt a request to the /get_flag endpoint where we discover our flagf.
Comments