Book Image

Hands-On Penetration Testing with Python

By : Furqan Khan
Book Image

Hands-On Penetration Testing with Python

By: Furqan Khan

Overview of this book

With the current technological and infrastructural shift, penetration testing is no longer a process-oriented activity. Modern-day penetration testing demands lots of automation and innovation; the only language that dominates all its peers is Python. Given the huge number of tools written in Python, and its popularity in the penetration testing space, this language has always been the first choice for penetration testers. Hands-On Penetration Testing with Python walks you through advanced Python programming constructs. Once you are familiar with the core concepts, you’ll explore the advanced uses of Python in the domain of penetration testing and optimization. You’ll then move on to understanding how Python, data science, and the cybersecurity ecosystem communicate with one another. In the concluding chapters, you’ll study exploit development, reverse engineering, and cybersecurity use cases that can be automated with Python. By the end of this book, you’ll have acquired adequate skills to leverage Python as a helpful tool to pentest and secure infrastructure, while also creating your own custom exploits.
Table of Contents (18 chapters)

Getting started

Throughout the course of this book, we will aim to cover advanced and well-known industry standards in Python, cyber security, penetration testing, and the data science space. However, as they say, every remarkable journey starts with small steps. Let's go ahead and start our journey by understanding the basics of Python.

Variables and keywords

Variables, as the name suggests, are placeholders that hold a value. A Python variable is nothing but a name that can hold a user-defined value during the scope of a Python program or script. If we compare Python variables to other conventional languages, such as C, C++, Java, and so on, we will see that they are a little bit different. In the other languages, we have to associate a data type with the name of the variable. For example, to declare an integer in C or Java, we have to declare it as int a=2, and the compiler will immediately reserve two bytes of memory in C and four bytes in Java. It would then name the memory location as a, which is to be referenced from the program with the value 2 stored in it. Python, however, is a dynamically typed language, which means that we do not need to associate a data type with the variable that we will declare or use in our program.

A typical Python declaration of an integer might look like a=20. This simply creates a variable named a and places the value 20 in it. Even if we change the value in the next line to be a="hello world", it would associate the string hello world with the variable a. Let's see that in action on the Python Terminal, as follows:

To use the Python Terminal, simply type the python3 command in your Terminal prompt. Let's think about how this works. Take a look at the following diagram, which compares statically typed languages with dynamically typed languages:

As you can see in the preceding diagrams, in the case of Python, the variable actually holds a reference to the actual object. Every time the value is changed, a new object is created in memory and the variable points toward this new object. The previous object is claimed by the garbage collector.

Having discussed that Python is a dynamically typed language, we must not confuse it with a weakly typed one. Though Python is dynamically typed, it is also a strongly typed language, just like Java, C, or C++.

In the following example, we declare a variable, a, of string type and a variable, b, of integer type:

When we carry out the operation c=a+b, what might happen in a weakly typed language is that the integer value of b would be typecasted to a string, and the result that was stored in variable c would have been hello world22. However, because Python is strongly typed, the function adheres to the type that is associated with the variable. We need to make the conversion explicitly to carry out any operations of this kind.

Let's take a look at the following example to understand what it means to be a strongly typed language; we explicitly change the type of variable b and typecast it to a string type at runtime:

Variable naming conventions

Having understood the basics of how variables can be declared and used, let's try to understand the naming conventions they follow. A variable, also known as an identifier, can be named by anything that starts with any letter between A-Z, a-z, or an underscore. This can then be followed by any number of digits or alphanumeric characters.

It must be noted that certain special characters, such as %, @, #, -, and !, are reserved in Python and can't be used with variables.

Python keywords

Keywords, as the name implies, are certain reserved words that have a predefined meaning within a particular language implementation. In other languages, we cannot usually name our variables with the same name as that of the keywords, but Python is a slightly different case. Although we shouldn't name the variables or identifiers with the same name as those reserved for keywords, even if we do, the program will not throw any errors and we will still get an output. Let's try to understand this with the help of a conventional C program and an equivalent Python script:

It should be noted that this is a simple C program in which we have declared an integer and used the int identifier to identify it, following which we simply print hello world.

When we try to compile the program, however, it throws a compilation error, as shown in the following screenshot:

Let's try to do the same in a Python shell and see what happens:

It can be seen that the program did not throw any errors when we declared our variable with the names int and str. Although both int and str are Python keywords, in the preceding case, we saw that a variable declared with name as int held a string value and a variable declared with str type held an int value. We also saw how a normal variable, a, was typecasted from int to string type. From this, it can be established that we can use reserved words as variables in Python. The downside of this is that if we are to make use of keywords as variables or identifiers, we are overriding the actual functionality that these reserved words possess. When we override their actual behavior within the scope of our program, they will follow the updated or overridden functionality, which is very dangerous as this would make our code fall out of Python's conventions. This should always be avoided.

Let's extend the preceding example. We know that str() is a built-in Python function, the purpose of which is to convert a numeric data type into a string type, as we saw for variable a. Later on, however, we overwrote its functionality and, for the scope of our program, we assigned it to an integer type. Now, at any point in time during the scope of this program, if we try to use the str function to convert a numeric type into a string, the interpreter will throw an error, saying that the int type variables can't be used as methods, or that they are not callable, as shown in the following screenshot:

The same would hold true for the int method and we would no longer be able to use it to type cast a string to its equivalent integer.

Now, let's take a look at other types of keywords that are available in Python that we should try not to use as our variable names. There is a cool way to do this with the Python code itself, which lets us print the Python keywords in the Terminal window:

The import statement is used to import the libraries in Python, just as we use imports for importing packages in Java. We will get into the details of using imports and loops in future sections. For now, we will look at what the different Python keywords mean:

  • false: The Boolean false operator.
  • none: This is equivalent to Null in other languages.
  • true: The Boolean true operator.
  • and: The logical and that can be used with conditions and loops.
  • as: This is used to assign an alias to a module that we import.
  • assert: This is used with the objective of debugging code.
  • break: This exits the loop.
  • class: This is used to declare a class.
  • continue: This is the traditional continue statement used with loops that can be used to continue the execution of a loop.
  • def: This is used to define a function. Every Python function needs to be preceded by the def keyword.
  • del: This is used to delete objects
  • elif: The conditional else...if statement.
  • else: The conditional else statement.
  • except: This is used to catch exceptions.
  • finally: This is used with exception handling as part of the final block of code in which we clean our resources.
  • for: The traditional for loop declaration keyword.
  • global: This is used to declare and use global variables.
  • if: The conditional if statement.
  • import: This is used to import Python libraries, packages, and modules.
  • in: This is used to search between Python strings, lists, and other objects.
  • is: This is used to test the identity of an object.
  • lambda: This is used with Lambda functions.
  • nonlocal: This is used to declare a variable inside a nested function that is not local to it.
  • not: This is a conditional operator.
  • or: This is another conditional operator.
  • pass: This is used as a placeholder in Python.
  • raise: This is used to raise an exception in Python.
  • return: This is used to return from a function.
  • try: The traditional try keyword that's used with exception handling.
  • while: This is used with the while loop.
  • with: This is used with file opening and so on.
  • yield: This is used with generators.
  • from: This is used with relative imports.

Throughout this book, we will learn about all the keywords mentioned in this list.