Inheritance of Objects-Using SSTI To Its Advantage- Updated for 2022

Purpose

Inheritance is the capability of one class to derive the properties/attributes from another class. Inheritance increases reusability of a code so that you don't have to write the same code over and over again. As explained in a previous project, SSTI allows a web server to be injected with false code to show directories and other important items.

You will need the following to complete this assignment:

  • Debian Linux on GCP that you created in a previous project
  • Expectations

    ****Need to add here.****

    Please be sure to create a Google Doc that contains screenshots with captions indicating what you are accomplishing. This will be used to help show that you completed the project and can be used as a reference later on for you. This will also be your submission for this project. The number of screenshots is to be determined but typically it should match the page count of the project.

    Directions

    This is a continuation of the project on SSTI. Complete that project first before attempting this one.

    Inheritance is the capability of one class (as an example let's say sub class) to derive or inherit the properties or attributes from some other class (base class). Alternatively, you can think of the child and parent relationship here as well. As mentioned in the purpose of the project, inheritance increases the reusability of code by not having to write the same thing over and over again. Additionally, this allows programmers to add more features to a class or modify existing behavior of a certain object.

    Let's start with a simple example of inheritance. In your Debian Linux, type in the following:

    sudo nano -l simple_class.py
    

    If you noticed, I included a dash and lowercase L. This will print the code with line numbers/count to make it easier to discuss and read. Copy the following code:

    class Student:  # base class or parent class
        def __init__(self, name):  # constructor
            self.name = name
    
        def get_name(self):
            return self.name
    
    
    class Alumni(Student):  # derived class or subclass
        def is_alumni(self):
            return True
    
    
    student = Student("ProfT Student")  # object creation/instantiation
    print(student.get_name())
    
    alumni = Alumni("Alumni of ProfT")
    print(alumni.get_name())
    print(alumni.is_alumni())
    

    To provide some explainations of the code above, you can see that the get_name method is defined in Student class but not the Alumni class. However, if you look at line 9 you can see that the base class (Student) and is passing along its attributes to the subclass (Alumni). This is why at lines 18 and 19, you can see that alumni is able to use the get_name method that is called from the Student class. Always remember that a subclass can call on a base class for it's attributes, but NOT the other way around.

    What happens when you execute the python script? What results do you get? Take a screenshot showing off the Python script running. After you do that, modify the script to print at the end to state student.is_alumni() What results did you get? Explain what happened with the result and provide screenshots to support your answer.

    Now let's move into multilevel inheritance. When a class is derived from a class which is also derived from another class (when a sub class has more than one base class), this is referred to as multilevel inheritance. The level of inheritance can be extended to an infinite amount of levels depending on the level of the relation. Let's try this with an example.

    sudo nano -l multi_class_inheritance.py
    

    Here is the code for the example:

    class A:
        def method_from_class_a(self):
            print("Hi from class A")
    
    
    class B(A):
        def method_from_class_b(self):
            print("Hi from class B")
    
    
    class C(B):
        def method_from_class_C(self):
            print("Hi from class C")
    

    As you can see, class A only has the method of method_from_class_a attribute. However, class B(A) has the methods of methods_from_class_a as well as method_from_class_b because class B extends A. Class C has all three by way of the shared inheritance, thus method_from_class_a and method_from_class_b and method_from_class_C methods.

    Let's take this and build on it. As mentioned before, the level of inheritance can be extended to several levels depending on the level of relation. Let's try an example where a sub class has two base classes (or a child has two parents).

    sudo nano -l twoparent_onechild_inheritance.py
    

    The code is below:

    class A:
        def speak(self):
            print("Class A speaking")
    
    
    class B:
        def scream(self):
            print("Class B screaming")
    
    
    class C(A,B):
    	pass
    
    
    c = C()
    c.speak()
    c.scream()
    

    What happens when you execute the python script? What results do you get? Take a screenshot showing off the Python script running. After you do that, modify the script to print Your First name for Class A, and your last name for Class B. Take another screenshot showing your name in each class. What results did you get? Explain what happened with the result and provide screenshots to support your answer.

    Now let's introduce a concept called Method Resolution Order (MRO). What were happen if we have two parents classes with one child class, but they both define the same method? Let's find out. Try the out this new code:

    sudo nano -l mro.py
    

    The code is below:

    class A:
        def speak(self):
            print("Class A speaking")
    
    
    class B:
        def speak(self):
            print("Class B speaking")
    
    
    class C(A,B):
    	pass
    
    
    c = C()
    c.speak()
    

    Let's run our code by issuing python3 mro.py.

    What happened? As you can see, we declared two different methods of speak between our parent classes of A and B. By it's nature C extends to both A and B. So class C should get both speak methods. But how does it know which one to use? Confusing isn't it? Let's try something.

    What happens when you execute the python script? What results do you get? Take a screenshot showing off the Python script running. After you do that, modify the script to Class C (B,A) and re-run your script. What results did you get? Explain what happened with the result and provide screenshots to support your answer.

    Lastly, if we change the previous code of class C to the following but leave the rest as before:

    class A:
        def speak(self):
            print("Class A speaking")
    
    
    class B:
        def speak(self):
            print("Class B speaking")
    
    
    class C(A,B):
    	def speak(self):
            print("Class C speaking")
    
    
    c = C()
    c.speak()
    

    Run your script to see the output below.

    Hmm, interesting. It would appear that the speak method from class C will over ride the previous speak methods from A and B. And of course, c.speak() is printing "class C speaking". Now that you understand some of the basics of MRO, let's see what happens with the following code:

    sudo nano -l mro_quiz.py
    

    The code is below:

    class A:
        def mro_test(self):
            return 'A'
    
    
    class B(A):
        pass
    
    
    class C:
        def mro_test(self):
            return 'C'
    
    
    class D(B, C):
        pass
    
    
    print(D().mro_test())
    

    What happens when you execute the python script? What results do you get? Take a screenshot showing off the Python script running. Explain what happened with the result and provide screenshots to support your answer.

    Now lets get back to what we working on with SSTI in the previous project and see how inheritance can help us. On your main terminal prompt enter the following (Note that each command has TWO underscores:

    python3
    s = 'GREETINGS'
    dir(s)
    s.__class__
    s.lower
    s.lower()
    

    To recap the commands from above, you are creating a string object called s and using the dir command to look at available attributes for your object. The third step while in the interpreter mode allows you to examine the _class_ attribute of the object, which is a string, review the lower method to see where its located in terms of memory, and then lastly running the lower method on the s object to change the string to lowercase.

    Now that we have a basic understanding, let's walk up the hierarchy from the attributes, to the class type, followed by the base class. Enter the following in the same window:

    s.__class__
    s.__class__.__class__
    s.__class__.__base__
    

    Refer to the diagram below to see how we walked up the hierarchy, and how we must now go down the right-side to get to a place where we can execute arbitrary commands. As you can see in the diagram, we have an object class that inherits its type from the object of s that we created earlier. From here we can walk down through the sub-classes all the way down to the os.system which will allow us to execute commands.

    In the same window with the Python Interpreter, add these commands from below:

    import os
    import subprocess
    s.__class__.__base__.__subclasses__()
    

    What we just accomplished was beginning to enumerate subclasses. As you can see, there are tons of subclasses available. From here, let's save that list of subclasses in an object named c and use the len function to count them.

    c = 'GREETINGS'.__class__.__base__.__subclasses__()
    len(c)
    

    Take a screenshot here showing how many things were counted. Use my screenshot for reference but the count will be hidden and yours should be showing.

    Now lets generate a list of function names and have them indexed by a number for easy reference. PLEASE PUT YOUR NUMBER FOUND FROM ABOVE IN THE XX values.

    Note: To get to the next line (...) press Shift and Enter at the same time. Tab to indent your commands.

    for i in range(xx):
    	print( i, c[i].__name__ )
    

    As you can see, it should create a long list of functions by numbers. The screenshot from above is just a sample of the output. From here, lets dig deeper and find a function called warning.

    for i in range(xx):
    	n = c[i].__name__ 
    	if n.find('warning') > -1:
    		print ( i, n )
    

    The result should show 168 catch_warnings. Lets take this a step further. Refer to the diagram from above. Let's see if we can enumerate beyond the warnings function:

    Note: Module only has one underscore (_module) instead of two.

    x = c[168]
    x()._module
    x()._module.__builtins__
    

    This should start looking familiar based on what we've done before. Let's store these commands into a new variable called b with the length just like before.

    b = x()._module.__builtins__
    len(b)
    b.__class__
    

    As you can see, its a dictionary type with 153 characters in it. Now let's look at imports.

    for key in b:
    	if key.find('import') > -1:
    		print( key, b[key] )
    

    As you might expect based on its name, this function allows us to import Python libraries and functions. So let's see all of our hardwork in action. We've finally reached our os.system base class.

    x()._module.__builtins__['__import__']('os').system("date")
    

    If it works, you should be seeing the system calling on the date. For final part of this project, you need to do the following:

    Create a command that will use the builtin functions that contain the letter 't'. Take a screenshot of your commands used and showcase the first 10 entries in the list. Again, use my screenshots as references.

    The next project will put the previous two together so you can see how inheritance works with SSTI.

    This concludes this project. PLEASE MAKE SURE YOU ARE SHUTTING DOWN YOUR VM EACH TIME UNLESS YOU ARE LEAVING IT RUNNING FOR A TASK IN THE COURSE. Please make sure to submit your project as instructed by your instructor by it's due date and time.


    New Project created Mar 2021
    Updated for Debian 11 Feb 2022

    References
    Python object and classes- a visual guide
    Working with subprocesses
    Playing with inheritance in Python