# *SOME WEB THINGs THAT I LEARNED (part 8)* #### Recently i got some new things that i learned from some challenges that i want to share. ## 1. Proton Memo - It's a challenge about Python Class Pollution on DreamHack. Let's begin. ### i. Challenge: #### a) Source code: - **```app.py```**: - First, it defines a function ```get_memo_with_auth_or_abort```: - The function takes in and ```id``` and a ```password```. It then pass the ```id``` into a method called from a class ```Memo```. ![image](https://hackmd.io/_uploads/rkN_F6WER.png) - The function uses the id that being passed in, then search for it in the ```collections``` dictionary. It then checks if that ```id``` exists as a key in the dict. - It then create an object with class ```Memo``` and add it to the ```collections``` dict.![image](https://hackmd.io/_uploads/rkDDjTbVR.png) - Then it defines 4 routes. Let's go through them quickly: - *```/```*: Return the id and title of the memo for each memo in the ```collections``` dict.![image](https://hackmd.io/_uploads/BJN_ppZVR.png) - *```/new```*: Add a new object with the class Memo to the dict. ![image](https://hackmd.io/_uploads/HkX9p6WEA.png) - ```/edit/<uuid:memo_id>```: By sending a POST req, you can edit the values of the the choosen memo object, using the ***```set_attr```*** method. We will come back to this method later. - ```/view/<uuid:memo_id>```: Take in a password that is set when create a new memo, then return its content. ![image](https://hackmd.io/_uploads/H1uXJR-V0.png) - **```models.py```**: This is where the challenge defines the classes. Let's go through them quickly. - **Password**: - ```__init__```: hashes the password with SHA256. - ```check_password```: check the self.data return by __init__ with the password being pass in after being hash to see if they are the same. ![image](https://hackmd.io/_uploads/BJd-W0W40.png) - **Title**: - ```__init__```: Takes in the data is the title, and the edit_time which uses the time() method. - ```get_raw_data```: Returns the title without the whitespace. - ```get_title```: Returns the title in the format ```Title: <title>```. - ```get_title_with_edit_time```: Returns the title and the time it was last edited. - **Content**: The same as **```Title```**. - **Memo**: - First it creates the ```collections``` dict. - ```__init__```: - ```self.id```: Generates a unique ID for the memo using uuid4(). - ```self.title```: Initializes the title attribute with a new Title object. - ```self.content```: Initializes the content attribute with a new Content object. - ```self.password```: Initializes the password attribute with a new Password object. - Then it calls the methods from other classes, and defines two methods: - ```get_memo_by_id```: Return the memo if found in the ```collections``` dict. - ```add_memo_to_collection```: add the memo object into the ```collections``` using the id. - **```utils.py```**: Let's go into this function. - The functions takes in three arguments: obj, prop and value. - It first split the prop chain by the **```.```**, which then takes the first property from the current chain after splitting.![image](https://hackmd.io/_uploads/SkDv4MGNC.png) - It then check if the there is 1 element of prop_chain, then it will check: - If the obj is a dict, then it will set the value being passed in to the current prop of the obj dict. - If not, then it will use ```setattr``` to set the attribute cur_prop to the value.![image](https://hackmd.io/_uploads/HkwIOMMV0.png) - If the len of prop_chain isn't 1, then it will jump to the else part. - First, it checks of the obj is a dict, if so, then it will check: - If the current prop exist as a key in the dict, then it sets the variable ```next_obj = obj[cur_prop]```.![image](https://hackmd.io/_uploads/H1PRuMMER.png) - If not, then it create an empty dictionary as ```next_obj``` and set it to ```obj[cur_prop]``` - If not a dict, then it will: - Check if the current prop has the attribute being passed in. If so, then it get the next_obj using the ```getattr``` method to move to the next property. - If not, then it will create an empty dict and set it to the current prop. - Then it will call to ```set_attr``` passing in the next_obj. ![image](https://hackmd.io/_uploads/H1KyaQGNR.png) #### b) The exploit: - Since I want the rest of this blog is to try to explain what i learned. So I'm going to exploit first, before explaining. - First, let's create a new note. Then use the edit function of it. Catching it with burp we can modified the request, something like this. ``` selected_option=__init__.__globals__.Memo.collections.<secret_note_id>.password&edit_data=<injected_password>&password=<password_of_our_public_note ``` - This is how we gen the injected password:![image](https://hackmd.io/_uploads/HyEF9M44R.png) - Then we just need to input ```1``` in the password field of the secret note, and we will receive the flag. ![image](https://hackmd.io/_uploads/S1qvjM44R.png) - Let's dive in to see why this works #### c) The vulnerability: - This challenge is about Python Class Pollution. Let's go through it together. (i'll try to explain how i understand it) ##### c.1) Brief intro: - In Javscript, we have something called ```prototype```, which Javascript objects inherit features from one another, like methods and properties. You can read about it here: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes - In Python, we dont have ```prototype```, but we do have **special attributes**. You can read about it here: https://docs.python.org/3/reference/datamodel.html ##### c.2) Dive in: - Let's begin with an example: ![image](https://hackmd.io/_uploads/HyFG2VGE0.png) - We create an empty ```User``` class. Then create ```user``` instance of the ```User class```. We then add an attribute called ```name``` to it. - Let's do something else. Let's use a special attribute to see what class the ```user``` is.![image](https://hackmd.io/_uploads/B1R71SfN0.png) - Ahh, it returns the ```User```. But, what if we were able to change it?![image](https://hackmd.io/_uploads/BJPFGHGNC.png) - So it changes the class name from ```User``` to what we set. - *```__qualname__```* or qualified name returns us the full path of the class or the function from where its being called from. For example:![image](https://hackmd.io/_uploads/rJgrdpfVA.png) - *```__class__```* is a references to the class of an object, in this case is ```User``` class. **```__class__.__qualname```** will point to the full path of the ```User``` class object, in this case will return ```User```. By *polluting* it by setting another value, calling ```user1``` now, it will return the class with the value that we change. - Let's look at a popular function, its a recursive merge that helps set the attributes to an object, or merge two or more objects, ... ```python def merge(src, dst): # Recursive merge function for k, v in src.items(): if hasattr(dst, '__getitem__'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v) ``` - The function uses ```getattr```and ```setattr``` to recursively traverse and set attributes to an object. With an untrusted input, the attacker can manipulate the attribute of an object or item.![image](https://hackmd.io/_uploads/SJXty0fEA.png) - After printing ```user1```, we can see now it got the attributes after calling the ```merge``` function. The ```user1-attributes``` is a dict, going through the ```merge``` function will set the key and value from the attributes dictionary and set it to the object. - This function is pretty normal, but without any restrictions, it can be abuse, let's see what will happened if we pass in a **special attribute**.![image](https://hackmd.io/_uploads/HJodWAMNA.png) - Let's see how it works using debugging to better undeerstand. Jump in!! ![image](https://hackmd.io/_uploads/SJBC7AMEA.png) - ```src``` is the injected attributes and ```dst``` is the object.![image](https://hackmd.io/_uploads/rJxUN0fV0.png) - ```k``` is the *__class__* while ```v``` is the *{"__qualname": "LMAO"}*, which is k's value. - It then check if ```dst``` got an attribute called ```__getitem__```, then check if ```dst``` has the key ```k``` and it's corresponding value is a dictionary, recursively merge the nested dictionaries.Otherwise, directly set the key-value pair in ```dst```.![image](https://hackmd.io/_uploads/HyCj00zNR.png) - If not, then chec if ```dst``` has the attribute ```k``` and ```v``` is a dict. If so, then it recursively merge the nested dictionary. If not, set the attribute ```k``` to the value ```v``` using setattr.![image](https://hackmd.io/_uploads/r1Y20CzV0.png) - It jumps into the merge function since the conditions is met.![image](https://hackmd.io/_uploads/HkHHykXNC.png) - Continue stepping into it, now we move into the ```__qualname__``` dict.![image](https://hackmd.io/_uploads/B1yylyQEA.png) - Jumps to checking ```hasattr```.![image](https://hackmd.io/_uploads/BJWHeJm4A.png) - ```v``` can't be a dict, so that means it will jump to the final else, which sets the value ```LMAO``` to the attribute ```__qualname```. - After running, you can see it returns ```user1``` as ```LMAO```.![image](https://hackmd.io/_uploads/S175YfXN0.png). - Let's continue going with this. In Python, a class can be inherit from another class. A child class will inherits the property and methods from the parents class. - In Python, the ```__base__``` property of the class contains a list of all the base classes that the given class inherits. - **Friend** class inherits **User**. ![image](https://hackmd.io/_uploads/SJ-5WQXEA.png) - By injecting the ```__qualname__``` of the ```__base__``` property, we are directly changing the value of the base class of **Friend**, or in this case **User** class. Changing it from ```User``` to ```LMAO```.![image](https://hackmd.io/_uploads/ryoRzQmV0.png) ##### c.3) Dive in more?: - So until now we are only polluting the classes only. But in fact we are not limit only to classes. By using ```__globals__```, we are ablt to reach the globals variable of a code, or a global class as well. Let's go for an example: ![image](https://hackmd.io/_uploads/rJ415PXV0.png) ![image](https://hackmd.io/_uploads/SyxSdwmEA.png) - As you can see, ```some_variable``` is a global variable that can be access anywhere in the code. We first called ```__class__``` attribute, which then traverse into ```__init__```, finally ```__blobals__```, which then we will be able to reach the global variable. But why is that? - **```__class__```**: is an attribute on the object that refers to the class from which the object was created. - **```__init__```**: is used to initialize objects of a class. It contains a collection of statements, instructions, attributes, ... In this case, this calls to the ```__init__``` inside the ```User``` class. - **```__globals__```**: As Python document writes:![image](https://hackmd.io/_uploads/SyYWCPQVR.png) - So ```__globals__``` is a dictionary that holds the access to the globals scope of a function, which we can access defined variables, modules, etc. We can access ```__globals__``` from any defined method of the instance we controlled. In this example, the ```User``` class have a ```__init__``` method, which from there we can access into ```__globals__```. - Since ```some_variable``` is a global variable, which can be access from anywhere in the code.![image](https://hackmd.io/_uploads/HkqvlumVC.png) - As we can see, we can use ```some_variable``` to set the attribute value for ```user```. So using the ```__globals__``` attributes, we can access a global variable that the ```User``` class can access, in this cased, ```some_variable```. - And classes is also the same too. Since you can set other class in a class, it means that other class is global and can be access by one class.![image](https://hackmd.io/_uploads/ryctwtQ4R.png) - We can also use this, to even change the variable from an imported module. Let's look at an example. - This is a test module:![image](https://hackmd.io/_uploads/HyBPlN44R.png) - We can then import it into our main code.![image](https://hackmd.io/_uploads/rJA9xNNNC.png) - As you can see, we can access and print out the variables of the test module.![image](https://hackmd.io/_uploads/SkH4b44VC.png) - Now let's use the ```__globals__``` **special attribute** to polute it.![image](https://hackmd.io/_uploads/HyDKdEV4C.png) - As you can see, we have changed every variable and even ```__qualname__``` attribute. - Or maybe using the ```modules``` attribute of __sys__, which comes in the form of a dictionary, containing all the imported module when the code is loaded (only when sys is imported). ![image](https://hackmd.io/_uploads/ryMZp2E4C.png) - But what will happened if __sys__ is not imported. In Python, we have an object called **loader**.![image](https://hackmd.io/_uploads/By_I2pNNC.png) - Let's have a look at it. - **loader** is an instance of class ```importlib.abc.Loader```. It is responsible for module loading. ![image](https://hackmd.io/_uploads/r18AZCVN0.png)![image](https://hackmd.io/_uploads/ryHeQ0V4A.png) - **__loader__**: is an attribute being set to the loader object when loading a module.![image](https://hackmd.io/_uploads/rylGDOSVA.png) - **__spec__**: built-in attribute was introduced in Python 3.4. It contains information about the module loading process, which is defined by the ```ModuleSpec``` class.![image](https://hackmd.io/_uploads/S15nKdrVR.png) ![image](https://hackmd.io/_uploads/SkxdtdBVC.png)![image](https://hackmd.io/_uploads/Hy3u3OB4C.png)![image](https://hackmd.io/_uploads/HkvGTdrNC.png). ### ii. Exploitation explaination: - Let's debug and see why our payload works. ![image](https://hackmd.io/_uploads/H1hjphH4C.png) - Divide the property chain. ![image](https://hackmd.io/_uploads/rJkm0nSV0.png) - Getting the next obj. ![image](https://hackmd.io/_uploads/SyesRnrER.png) - Continue, we will reach ```__globals__```. Since ```Memo``` is an imported class, we can access it from ```__globals__```.![image](https://hackmd.io/_uploads/rJztJTrV0.png) - Now we are inside the ```Memo``` class variable. We can see it contains the collections dict.![image](https://hackmd.io/_uploads/Sy3LeTrV0.png) - Reach the ```collections``` dict. ![image](https://hackmd.io/_uploads/SkwOMpBN0.png) - Traverse into the id property, we reach its object. ![image](https://hackmd.io/_uploads/B1O0zTSV0.png) - Set the ```password.data``` to the injected data.![image](https://hackmd.io/_uploads/Hyy1Vpr4A.png)![image](https://hackmd.io/_uploads/r1--VTSEC.png). - So why don't we just pollute the global ```secret``` object. Well since after created, ```secret``` object is then add to the dict. So whether we change the global object or not, it doesn't affect the value of the secret, which is being saved inside the ```collections``` dict.![image](https://hackmd.io/_uploads/B1SsvpSN0.png) ### What i learned - This is probably some surface of the vulnerability called ```Python Class Pollution```. There are still more but i want to document a bit about what i learned. - Understand some **special attributes** and how it works. Learn some more about Python class and objects. ## 2. JerryTok - It's a challenge about Twig SSTi with PHP ```disable_functions``` and ```open base_dir``` on HacktheBox. Let's begin. ### i. Challenge: #### a) Source code: ## Thank you for reading >.< Gud bye ![image](https://hackmd.io/_uploads/rk-UgZatp.png)