Skip to content

2023

Object oriented bash

Here's a thought experiment - you can only program in bash, and you have been assigned a task that demanded you to model your program with behaviors that could be easily modeled with objects eg. an airplane or a car (you get the idea). Imagine the complexities that might arise from a command-styled, procedurally programmatic language like bash to define the modularity, components, and properties of a vehicle.

Well, driven by my curiosity and lots of free time, I sought to explore how we might go about implementing object-oriented programming in bash.

Object Oriented Programming Principles

Object-Oriented Programming (OOP) is a paradigm that uses "objects" to design applications. These objects bundle related data, known as state, and methods to manipulate this data, known as behavior. Objects interact via message passing which invokes methods, allowing dynamic behavior. OOP is implemented using techniques like dynamic dispatch and closures. Behaviors are defined in a class but executed at runtime. A key requirement for OOP is lexical scoping, which allows a function to access variables from its enclosing scope. Without it, OOP implementation becomes challenging.

Object Oriented Example

Below shows a class diagram that illustrates a PlantUML example of how we can apply the OOP Principles

In our UML Diagram, a base class Animal with subclasses Dog, Cat, and Mosquito inherits from Animal and overrides the sound method. The State class is associated with the Animal class and has two properties: isAlive and Health. This class represents the state of an animal, indicating whether it's alive and its health status.

classDiagram
    Animal <|-- Dog
    Animal <|-- Cat
    Animal <|-- Mosquito
    Animal : +String name
    Animal : +int age
    Animal: +sound() String
    State <|-- Animal
    State: +boolean isAlive
    State: +String Health
    class Dog{
      +String breed
      +sound() String (returns "woof")
      +findOwner()
    }
    class Cat{
      +String color
      +sound() String (returns "meow")
      +scratch()
    }
    class Mosquito{
      +sound() String (returns "eeeeeeeeë")
      +feed()
    }

Experimentation

Compgen is used as a command in Unix-like operating systems, to generate possible completions for commands and filenames. For example, running compgen -c will list all the available commands, while compgen -f will generate a list of filenames in the current directory.

We utilize a this variable to declare a pointer variable which we could then use to initialize a static member function, The constructor can be called with the 1$ which references itself and performs the memory allocation before it has been completely initialized for future classes or functions that have not yet been created. We can utilize the built-in export keyword in bash to create variables that can be accessed through the child processes created by the subsequent scripts. By utilizing compgen, we could enable bash files to inherit methods between files, allowing the program execution to access the built-in methods of the class. This allows for methods to dynamic bind explicitly to another class and allows for the method cast to be called.

Class structure setup by simulating the _this keyword.

Heres an example:

#!/bin/bash

function name() {
    # Pointer to the base state class
    base=$FUNCNAME
    this=$1

    # Variable properties
    export ${this}_var1=$2
    export ${this}_var2=$3
    export ${this}_var3=$4

    # Declare methods of base class 
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${this}"
    done

}
Project Setup
# create object files and make them executable
touch state.sh animal.sh main.sh
chmod +x state.sh animal.sh main.sh

Getting Animal to inherit from State. Consider state.sh as a base class file to define a state object.

Based on our UML diagram, we need to create a base class called State. through the OOP concept of inheritence, we want the attributes of the State class to be inherited from the Animal class and the animals we create later in the application to all inherit from the state class. This means each Animal will have a boolean attribute isAlive and a String attribute Health indicating their current state.

state.sh
#!/bin/bash 

# Base state class
function State() {
    # Pointer to the base state class
    base=$FUNCNAME
    this=$1

    # Declare state properties
    export ${this}_isAlive=$2 # boolean value
    export ${this}_Health=$3 # String value

    # Declare methods of base class 
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${this}"
    done

    state=$(eval "echo \$${this}_isAlive")
}

# Display human readable representation of the state of the animal
function State_show() {

    # Obtain reference from base class
    base=$(expr "$FUNCNAME" : '\([a-zA-Z][a-zA-Z0-9]*\)')
    this=$1

    # Retrieve base components
    isAlive=$(eval "echo \$${this}_isAlive")
    Health=$(eval "echo \$${this}_Health")

    echo "State_show() -> $this ($isAlive, $Health)"
}

Testing inheritance in the Animal class and defining the attributes

We create an Animal class to test the inheritance of the state object, for testing purposes we utilize the animal class to create animal states for dog, cat and mosquito. As you can see, the state functions and variables can be invoked in the animal class. This proves that lexical scoping can be achieved by calling the state variables with an underscore eg. _isAlive or _animalState.

animal.sh
#!/bin/bash 

# import state object 
. state.sh

# Defining base class 
function Animal() {

    # A pointer to the Animal class
    base=$FUNCNAME
    this=$1

    # Inherited classes (eg State)
    export ${this}_inherits="dogState, catState, mosquitoState" # (3.1)

    for class in $(eval "echo \$${this}_inherits")
    do
        for property in $(compgen -A variable ${class}_)
        do
            export ${property/#$class\_/$this\_}="${property}" # (3.2)
        done

        for method in $(compgen -A function ${class}_)
        do
            export ${method/#$class\_/$this\_}="${method} ${this}"
        done
    done

    # creating default states for all the animals
    State "dogState" true "healthy"
    State "catState" true "obese"
    State "mosquitoState" false "splattered"

    # Test animal state variables
    echo "Animal state variable properties : 
                dogState ($dogState_isAlive, $dogState_Health), 
                catState ($catState_isAlive, $catState_Health), 
                mosquitoState ($mosquitoState_isAlive, $mosquitoState_Health)"

    # Test animal state methods
    echo "Animal base state methods :"
    $dogState_show
    $catState_show
    $mosquitoState_show
}

Animal

output from animal.sh (reformatted to show property and method data)

The data can be transiently accessed through the animal class, which means that theoretically, we can abstract the animal into other classes eg. dog.sh, cat.sh or mosquito.sh.

./animal.sh

Animal state variable properties : 
                dogState (true, healthy), 
                catState (true, obese), 
                mosquitoState (false, splattered)

Animal base state methods :
                Health Status -> dogState (true, healthy)
                Health Status -> catState (true, obese)
                Health Status -> mosquitoState (false, splattered)

Creating Animal Objects - Implementation

File Structure

animal
    ├── state.sh
    ├── animal.sh
    └── main.sh

Rewriting the animal class into an abstract class.

Now by utilizing the same idea as State() we can simulate the same idea of creating the inheritnace of our animal attributes using the _this keyword and compgen. Abstract classes are designed to be a generalization of animals, and it is not meant to be instantiated on their own. Instead, it serves as a blueprint for more specific classes, often called concrete classes eg. Dog(), Cat(), Mosquito(), which inherit from the Animal class.

animal.sh
#!/bin/bash 

# import state object 
. state.sh

# Defining base class 
function Animal() {

    # A pointer to the Animal class
    base=$FUNCNAME
    this=$1

    # Inherited classes (eg State)
    export ${this}_inherits="dogState, catState, mosquitoState" # (3.1)

    for class in $(eval "echo \$${this}_inherits")
    do
        for property in $(compgen -A variable ${class}_)
        do
            export ${property/#$class\_/$this\_}="${property}" # (3.2)
        done

        for method in $(compgen -A function ${class}_)
        do
            export ${method/#$class\_/$this\_}="${method} ${this}"
        done
    done

    # Declare animal properties 
    export ${this}_sound=$2
    export ${this}_age=$3
    export ${this}_animalState=$4
    export ${this}_isAlive=$5
    export ${this}_Health=$6

    # Declare animal methods
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${this}"
    done
}

# Defining animal sound property
function sound() {
    # Obtaining reference from base class 
    base=$(expr "$FUNCNAME" : '\([a-zA-Z][a-zA-Z0-9]*\)')
    this=$1

    # Retrieve base compenents
    animal_sound=$(eval "echo \$${this}_sound")
    echo "$this a dog says $animal_sound"    
}

To demonstrate the simulation of inheriting from an abstract class, I will generalize the application layer into a main file called main.sh. The methods and attributes of our abstract class Animal() has been inherited and are accessible to our concrete class. This theoretically implies that we can also utilize OOP concepts such as polymorphism and Encapsulation to manipulate our programs through base Inheritance and Abstraction.

main.sh
#!/bin/bash 

# import state and animal objects
. state.sh
. animal.sh 

function main() {
    # Create animal objects
    # Animal 'animal_type' 'animal_type_sound' 'animal_type_age' 'animal_type_stateid' 'animal_isAlive' 'anial_health'
    Animal 'dog' 'woof' 12 'dogState' true 'healthy'
    Animal 'cat' 'meow' 9 'catState' true 'obese'
    Animal 'mosquito' 'eeeeeeë' 0.5 'mosquitoState' false 'splattered'

    # display inherited states 
    echo "dog ($dog_sound, $dog_age, $dog_dogState, $dog_isAlive, $dog_Health)"
    echo "cat ($cat_sound, $cat_age, $cat_catState, $cat_isAlive, $cat_Health)"
    echo "mosquito ($mosquito_sound, $mosquito_age, $mosquito_mosquitoState, $mosquito_isAlive, $mosquito_Health)"
}

main

output from main.sh (simulated as a general environment to create object classes)

The abstract class data of animal can be accessed transiently in our main function and used to initialize different animals.

./main.sh 
dog (woof, 12, , true, healthy)
cat (meow, 9, , true, obese)
mosquito (eeeeeeë, 0.5, , false, splattered)

In Conclusion

"If you have to write bash functions, you might as well write it in Python or Golang"

Bash is painful to write unless you're using it to mock up a procedural, command-styled setup or linker script - it's not worth trying to model Minecraft using pure bash. While the ideas I've proposed are theoretically possible and can be simulated using bash utils - and the idea of writing fully-fledged OO scripts in CICD processes might be tempting, but the juice is simply not worth the squueze.

Resources
  1. Original article that inspired the idea
  2. Custom useful bash libraries
  3. Wikipedia to study OO concepts

Voilent python

Abtract

My review and open source contribution to "Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers"

In early 2020 when I first ventured into the world of ethical computer hacking and cyber security, I stumbled upon an incredible book called Violent Python by TJ O'Connor. The book explores the practical applications and features of the now deprecated Python 2.6 applied to offensive security techniques such as network scanning, packet manipulation, password cracking and web reconnaissance. While the key takeaways from the book were invaluable, and the book offers a practical guide into the world of cyber security, it is important to note that at the time of writing this, Python 2.6 reached its end-of-life status in October 2013 and is no longer officially supported.

Python 3 introduced several backward-incompatible changes including:

  1. Print Statements
  2. Integer Division
  3. Unicode Handling
  4. Module Modifications
  5. Syntax Changes

This left me in a bind while following the book, as code snippets and step-by-step instructions didn't work on a python3 interpreter which I had installed at that time. Resulting in many unintended syntax errors and exception raises.


The Project

I found a guy on Github that had identified the same problem of language backward compatibility with programming books, at the time that I stumbled upon this problem he had made a full source code conversion of the book Black Hat Python, by Justin Seitz. He'd started a repository on the exact book I was looking to refactor, so I started submitting pull requests to his repository and we both started hacking on a full release.

This project setup assumes you are on a linux environment

1. Copy this snippet into a file called setup.sh

#!/bin/bash
git clone https://github.com/EONRaider/violent-python3
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

2. Make the file an executable, and run the environment setup:

user@host:~/DIR$ chmod +x setup.sh
user@host:~/DIR$ ./setup.sh


Release Notes - 0.1.0

Python 3 compatibility

The code has been fully converted to Python 3, reformatted to comply with PEP8 standards and refactored to eliminate dependency issues involving the implementation of deprecated libraries.

Added: - Directory and file names are now more intuitive and reflect chapter contents. - Adopted PEP 8 naming conventions for files, variables, functions, classes, and methods. - Refactored code in Chapter 5; however, note that some examples may be outdated or impractical.

Updated: - Replaced string concatenation with string interpolation for better readability. - Replaced deprecated 'optparse' library with 'argparse' for command-line argument parsing. - Improved error handling by replacing generic exceptions with specific clauses. - Employed context managers to ensure files and databases are properly closed.

Removed: - Removed encoding comments, as UTF-8 is now the standard in Python 3. - Kept global variables despite not being a best practice, to maintain the original code's logic.

Unchanged: - Code in Chapter 6 for Google and Twitter APIs is outdated and not refactored.


Refactoring Summary

The following includes the changes made in our full release, files not listed below can be assumed to have been refactored in one way or another as established in the Realease Notes section.

Sections Changes
chapter01/vuln_scanner.py Moved iteration control into the conditional statement in the main function to handle OSError for non-existent files.
chapter02/nmap_scan.py Replaced deprecated optparse with argparse. Moved iteration into nmap_scan function for efficiency.
chapter02/ssh_command.py Moved initialization code into main scope, renamed conflicting variables, and decoded prompt information for readability.
chapter02/ssh_brute.py Fixed import statement and indentation errors.
chapter02/ssh_brutekey.py Added a compressed archive with necessary pre-generated keys due to an inaccessible URL.
chapter02/ssh_botnet.py Removed an unused import, reorganized code under main, and unified command issuance.
chapter02/conficker.py Removed an unused call to the sys library.
chapter03/discover_networks.py Reimplemented using WiGLE API via the requests library, and added exception handling.
chapter03/pdf_read.py Replaced deprecated PyPDF with PyPDF4.
chapter03/exif_fetch.py Added an argument to BeautifulSoup for compatibility.
chapter03/skype_parse.py & firefox_parse.py Added example files for convenience.
chapter03/iphone_messages.py Refactored but remains untested due to unavailability of files.
chapter04/geo_ip.py Replaced deprecated pygeoip with geoip2, and added CLI via argparse.
chapter04/print_direction.py Fixed file opening argument to handle UnicodeDecodeError.
chapter04/find_ddos.py Corrected the output to display the destination address.
chapter04/test_domain_flux.py Modified packet analysis logic for accuracy.
chapter05/blue_bug.py Fixed object reference and noted dependency installations.
chapter05/ftp_sniff.py Corrected logic for displaying username and password.
chapter05/ninja_print.py Noted the code remains in Python 2 due to library limitations.
chapter05/ & chapter06/__init__.py module imports.
chapter06/anon_proxy.py & anon_browser.py Re-implemented with MechanicalSoup library and updated related modifications.
chapter06/link_parser.py Updated implementations of re and bs4.

In conclusion

The changes encompass a series of significant refactoring efforts to improve the efficiency readability and compatibility of the Python project. With the update of deprecated libraries, restructuring of code examples, and the inclusion of dependency files.

My exposure to this project has been an enlightening journey, particularly as it marked my first exploration into the expansive realm of computer exploitation. Through my involvement, I've gained a wealth of knowledge on hacking computer systems.

I would like to take this opportunity to thank TJ O'Connor for writing this marvellous book, and EONRaider for creating this project.


My first blog

I'm excited to announce the start of my very own blog. My aim is to adopt a holistic approach to self-improvement through documentation and notes. This endeavour will undoubtedly push me beyond my comfort zone, and while I don't anticipate a large readership, if you're reading this, I want to thank you for being here.

Why am I doing this?

Objective Description
Learning I will document my journey and progress in life and professional career by sharing my ideas, challenges and breakthroughs as I learn new techically challenging topics
Self Reflection I will share my personal insights, ideas and experiences in the realm of technology and value creation. I also hope to highlight and internalize the lessons I've learned and identify opportunities for personal growth
Therapy I want help myself by cultivating a positive mindset, managing stress, finding motivation and overcoming obstacles. Writing it out helps me manifest my desires through action

Note

This blog will be an evolving entity, just as we are in our own lives. I am excited to see how my writings transform and grow over time.

In closing

They say the first step is the hardest, but it's also the most exhilarating. Through this platform, I am taking my first step, and I sincerely invite you to take this journey alongside me. In this connected world, it’s important to remember that we don’t have to walk alone. So, whether you’re a fellow engineer, someone looking for self-improvement, or just a stranger who stumbled upon this blog, I hope my thoughts can brings a smile to your face or offer a different perspective in your own quest for self-improvement.

Until next time,

Thank you for being here ❤️.