Book Image

Mastering Python for Networking and Security - Second Edition

By : José Ortega
Book Image

Mastering Python for Networking and Security - Second Edition

By: José Ortega

Overview of this book

It’s now more apparent than ever that security is a critical aspect of IT infrastructure, and that devastating data breaches can occur from simple network line hacks. As shown in this book, combining the latest version of Python with an increased focus on network security can help you to level up your defenses against cyber attacks and cyber threats. Python is being used for increasingly advanced tasks, with the latest update introducing new libraries and packages featured in the Python 3.7.4 recommended version. Moreover, most scripts are compatible with the latest versions of Python and can also be executed in a virtual environment. This book will guide you through using these updated packages to build a secure network with the help of Python scripting. You’ll cover a range of topics, from building a network to the procedures you need to follow to secure it. Starting by exploring different packages and libraries, you’ll learn about various ways to build a network and connect with the Tor network through Python scripting. You will also learn how to assess a network's vulnerabilities using Python security scripting. Later, you’ll learn how to achieve endpoint protection by leveraging Python packages, along with writing forensic scripts. By the end of this Python book, you’ll be able to use Python to build secure apps using cryptography and steganography techniques.
Table of Contents (22 chapters)
1
Section 1: The Python Environment and System Programming Tools
4
Section 2: Network Scripting and Extracting Information from the Tor Network with Python
8
Section 3: Server Scripting and Port Scanning with Python
12
Section 4: Server Vulnerabilities and Security in Python Modules
16
Section 5: Python Forensics

Exploring Python data structures

In this section, we will review different types of data structures, including lists, tuples, and dictionaries. We will see methods and operations for managing these data structures and practical examples where we review the main use cases.

Lists

Lists in Python are equivalent to structures as dynamic vectors in programming languages such as C. We can express literals by enclosing their elements between a pair of brackets and separating them with commas. The first element of a list has index 0.

Consider the following example: a programmer can create a list using the append() method by adding objects, printing the objects, and then sorting them before printing again. We describe a list of protocols in the following example, and use the key methods of a Python list as add, index, and remove:

>>> protocolList = []
>>> protocolList.append(“ftp”)
>>> protocolList.append(“ssh”)
>>> protocolList.append(“smtp”)
>>> protocolList.append(“http”)
>>> print(protocolList)
[‘ftp’,’ssh’,’smtp’,’http’]
>>> protocolList.sort()
>>> print(protocolList)
[‘ftp’,’http’,’smtp’,’ssh’]
>>> type(protocolList)
<type ‘list’>
>>> len(protocolList)
4

To access specific positions, we can use the index() method, and to delete an element, we can use the remove() method:

>>> position = protocolList.index(“ssh”)
>>> print(“ssh position”+str(position))
ssh position 3
>>> protocolList.remove(“ssh”)
>>> print(protocolList)
[‘ftp’,’http’,’smtp’]
>>> count = len(protocolList)
>>> print(“Protocol elements “+str(count))
Protocol elements 3

To print out the whole protocol list, use the following instructions. This will loop through all the elements and print them:

>>> for protocol in protocolList:
>>> 	print (protocol)
ftp
http
smtp

Lists also have methods that help manipulate the values within them and allow us to store more than one variable within them and provide a better way to sort object arrays in Python. These are the techniques commonly used to control lists:

  • .append(value): Appends an element at the end of the list
  • .count(‘x’): Gets the number of ‘x’ in the list
  • .index(‘x’): Returns the index of ‘x’ in the list
  • .insert(‘y’,’x’): Inserts ‘x’ at location ‘y’
  • .pop(): Returns the last element and also removes it from the list
  • .remove(‘x’): Removes the first ‘x’ from the list
  • .reverse(): Reverses the elements in the list
  • .sort(): Sorts the list in ascending order

The indexing operator allows access to an element and is expressed syntactically by adding its index in brackets to the list, list [index]. You can change the value of a chosen element in the list using the index between brackets:

protocols[4] = ‘ssh’
print(“New list content: “, protocols)

Also, you can copy the value of a specific position to another position in the list:

protocols[1] = protocols[4]
print(“New list content:”, protocols)

The value inside the brackets that selects one element of the list is called an index, while the operation of selecting an element from the list is known as indexing.

Adding elements to a list

We can add elements to a list by means of the following methods:

  • list.append(value): This method allows an element to be inserted at the end of the list. It takes its argument’s value and puts it at the end of the list that owns the method. The list’s length then increases by one.
  • list.insert(location, value): The insert() method is a bit smarter since it can add a new element at any place in the list, and not just at the end. It takes as arguments first the required location of the element to be inserted and then the element to be inserted.

Reversing a list

Another interesting operation that we perform in lists is the one that offers the possibility of getting elements in a reverse way in the list through the reverse() method:

>>> protocolList.reverse()
>>> print(protocolList)
[‘smtp’,’http’,’ftp’]

Another way to do the same operation is to use the -1 index. This quick and easy technique shows how you can access all the elements of a list in reverse order:

>>> protocolList[::-1]
>>> print(protocolList)
[‘smtp’,’http’,’ftp’]

Searching elements in a list

In this example, we can see the code for finding the location of a given element inside a list. We use the range function to get elements inside protocolList and we compare each element with the element to find. When both elements are equal, we break the loop and return the element.

You can find the following code in the search_element_list.py file:

protocolList = [“FTP”, “HTTP”, “SNMP”, “SSH”]
toFind = “SSH”
found = False
for i in range(len(protocolList)):
        found = protocolList[i] == toFind
        if found:
                break
if found:
        print(“Element found at index”, i)
else:
        print(“Element not found”)

Now that you know how to add, reverse, and search for elements in a list, let’s move on to learning about tuples in Python.

Tuples

A tuple is like a list, except its size cannot change and cannot add more elements than originally specified. The parentheses delimit a tuple. If we try to modify a tuple element, we get an error that indicates that the tuple object does not support element assignment:

>>>tuple=(“ftp”,”ssh”,”http”,”snmp”)
>>>tuple[0]
‘ftp’
>>>tuple[0]=”FTP”
Traceback (most recent call last):
    File “<stdin>”, line 1, in <module>
TypeError: ‘tuple’ object does not support item assignment

Now that you know the basic data structures for working with Python, let’s move on to learning about Python dictionaries in order to organize information in the key-value format.

Python dictionaries

The Python dictionary data structure is probably the most important in the entire language and allows us to associate values with keys. A key is any immutable object. The value associated with a key can be accessed with the indexing operator. In Python, dictionaries are implemented using hash tables.

A Python dictionary is a way of storing information in the format of key: value pairs. Python dictionaries have curly brackets, {}. Let’s look at a protocols dictionary, with names and numbers, for example:

>>> services = {“ftp”:21, “ssh”:22, “smtp”:25, “http”:80}

The limitation with dictionaries is that we cannot use the same key to create multiple values. This will overwrite the duplicate key preceding value.

Using the update method, we can combine two distinct dictionaries into one. In addition, the update method will merge existing elements if they conflict:

>>> services = {“ftp”:21, “ssh”:22, “smtp”:25, “http”:80}
>>> services2 = {“ftp”:21, “ssh”:22, “snmp”:161, “ldap”:389}
>>> services.update(services2)
>>> print(services)
{“ftp”:21, “ssh”:22, “smtp”:25, “http”:80,”snmp”:161, “ldap”:389}

The first value is the key, and the second the key value. We can use any unchangeable value as a key. We can use numbers, sequences, Booleans, or tuples, but not lists or dictionaries, since they are mutable.

The main difference between dictionaries and lists or tuples is that values contained in a dictionary are accessed by their name and not by their index. You may also use this operator to reassign values, as in the lists and tuples:

>>> services[“http”]= 8080

This means that a dictionary is a set of key-value pairs with the following conditions:

  • Each key must be unique: That means it is not possible to have more than one key of the same value.
  • A key may be data of any type: It may be a number or a string.
  • A dictionary is not a list: A list contains a set of numbered values, while a dictionary holds pairs of values.
  • The len() function: This works for dictionaries and returns the number of key-value elements in the dictionary.

    Important note

    In Python 3.7, dictionaries have become ordered collections by default.

When building a dictionary, each key is separated from its value by a colon, and we separate items by commas. The .keys() method will return a list of all keys of a dictionary and the .items() method will return a complete list of elements in the dictionary. The following are examples involving these methods:

  • services.keys() is a method that will return all the keys in the dictionary.
  • services.items() is a method that will return the entire list of items in a dictionary:
>>> keys = services.keys()
>>> print(keys)
[‘ftp’, ‘smtp’, ‘ssh’, ‘http’, ‘snmp’]

Another way is based on using a dictionary’s method called items(). The method returns a list of tuples (this is the first example where tuples are something more than just an example of themselves) where each tuple is a key-value pair:

  1. Enter the following command:
    >>> items = services.items()
    >>> print(items)
    [(‘ftp’, 21), (‘smtp’,25), (‘ssh’, 22), (‘http’, 80), (‘snmp’, 161)]

    From the performance point of view, when it is stored, the key inside a dictionary is converted to a hash value to save space and boost efficiency when searching or indexing the dictionary. The dictionary may also be printed, and the keys browsed in a particular order.

  2. The following code sorts the dictionary elements in ascending order by key using the sort() method:
    >>> items.sort()
    >>> print(items)
    [(‘ftp’, 21), (‘http’, 80), (‘smtp’, 25), (‘snmp’, 161), (‘ssh’, 22)]
  3. Finally, you might want to iterate over a dictionary and extract and display all the key-value pairs with a classical for loop:
    >>> for key,value in services.items():
    >>>	print(key,value)
    ftp 21
    smtp 25
    ssh 22
    http 80
    snmp 16

Assigning a new value to an existing key is simple due to dictionaries being fully mutable. There are no obstacles to modify them:

  1. In this example, we’re going to replace the value of the http key:
    >>> services[‘http’] = 8080
    >>> print(services)
    {“ftp”:21, “ssh”:22, “smtp”:25, “http”:8080,”snmp”:161}
  2. Adding a new key-value pair to a dictionary is as easy as modifying a value. Only a new, previously non-existent key needs to be assigned to one:
    >>> services[‘ldap’] = 389
    >>> print(services)
    {“ftp”:21, “ssh”:22, “smtp”:25, “http”:8080,”snmp”:161, “ldap”:389}

Note that this is very different behavior compared to lists, which don’t allow you to assign values to non-existing indices.

Now that you know the main data structures for working with Python, let’s move on to learning how to structure our Python code with functions and classes.