# Python: Building Scripts 

Link to the Python Scripting Basics on TryHackMe: [**Python: Building Scripts**](https://tryhackme.com/room/pythonbuildingscripts)

## Introduction

In the [Python: Core Concepts](https://tryhackme.com/room/pythoncoreconcepts) room, we learned how to work with data types, strings, lists, dictionaries, operators, and loops. We can now store data, inspect it, iterate over it, and make decisions based on conditions. These are the building blocks. In this room, we will learn how to *assemble* those building blocks into real programs.

Consider the difference between knowing individual musical notes and being able to play a song. In Core Concepts, you learned the notes. In this room, you will play the song.

Specifically, we will cover four capabilities that separate a code snippet from a real script: **functions** for organizing and reusing logic, **error handling** for building resilience, **file I/O** for reading input and writing output, and **libraries** for leveraging code that others have already written. By the end, we will combine every concept from both rooms into a single, working **Password Strength Checker**: a tool that evaluates passwords against length requirements, character variety rules, and a common-passwords wordlist.

## **Learning Objectives**

After completing this room, you will:

*   Define and call functions with parameters, return values, and defaults
    
*   Understand variable scope
    
*   Handle runtime errors with `try`/`except`
    
*   Read from and write to files using context managers (`with`)
    
*   Import modules from the Python standard library
    
*   Install third-party packages with `pip`
    
*   Combine all concepts into a complete, security-relevant program
    

## **Prerequisites**

It is best if you tackle this room after finishing:

*   [Python: Simple Demo](https://tryhackme.com/room/pythonsimpledemo)
    
*   [Python: Core Concepts](https://tryhackme.com/room/pythoncoreconcepts)
    

## Functions

As programs grow larger, you will find yourself writing the same logic in multiple places. Consider our Password Strength Checker: we might want to check password strength in one part of the program and then again later when the user retries. Copying and pasting the same block of code is error-prone and tedious. This is precisely the problem that **functions** solve.

One analogy would be a vending machine. You press a button (the **call**), optionally insert money (the **arguments**), and the machine gives you a drink (the **return value**). The internal mechanism is hidden; you only care about what goes in and what comes out. In technical terms, a function is a reusable block of code that takes input, performs a task, and optionally returns a result.

## **Defining a Function**

In Python, you define a function using the `def` keyword, followed by the function name, parentheses containing any **parameters**, and a colon. The indented code block beneath it is the function body.

```python
def greet(name):
    print(f"Hello, {name}. Welcome to the system.")
```

To use (or **call**) this function:

```python
greet("Alice")    # Hello, Alice. Welcome to the system.
greet("Bob")      # Hello, Bob. Welcome to the system.
```

We defined the function once, but we can call it as many times as we want with different arguments.

## **Parameters vs. Arguments**

A **parameter** is the variable name in the function definition (`name` in `def greet(name)`). An **argument** is the actual value you pass in when you call the function (`"Alice"` in `greet("Alice")`). In everyday conversation, people often use these terms interchangeably, but the distinction matters when reading documentation.

## **Return Values**

Functions can send a result back to the caller using the `return` keyword. This is what makes functions truly powerful: they can compute something and hand it back for you to store, print, or use in further calculations.

```python
def check_length(password, min_length):
    if len(password) >= min_length:
        return True
    else:
        return False

result = check_length("Tr0ub4dor", 8)
print(result)   # True
```

When Python encounters `return`, it immediately exits the function and sends the specified value back. If a function has no `return` statement, it returns `None` by default.

## **Multiple Parameters**

Functions can accept any number of parameters, separated by commas:

```python
def score_password(password, common_list):
    score = 0

    if len(password) >= 8:
        score += 1
    if len(password) >= 12:
        score += 1
    if any(c.isdigit() for c in password):
        score += 1
    if any(c.isupper() for c in password):
        score += 1
    if password not in common_list:
        score += 1

    return score
```

This function takes a password and a list of common passwords, then returns a numeric score. Notice how it uses `len()`, `.isdigit()`, `.isupper()`, and the `in` operator from Core Concepts. We will refine and use this exact function in our capstone task.

## **Default Parameter Values**

You can give a parameter a default value so that the caller does not have to supply it every time:

```shell
def check_length(password, min_length=8):
    return len(password) >= min_length

print(check_length("short"))          # False   (uses default min_length of 8)
print(check_length("short", 4))       # True    (overrides default with 4)
```

## **Scope**

Variables created inside a function exist only within that function. They are not accessible from the outside. This concept is called **scope**.

```shell
def calculate():
    result = 42     # local variable
    return result

calculate()
# print(result)    # This would cause an error: result is not defined here
```

Scope prevents functions from accidentally interfering with each other's data. It is a feature, not a limitation.

Open `functions_`[`demo.py`](http://demo.py) on the attached VM and run it. The script defines `greet()`, `check_length()`, and `score_password()`. It calls each one and prints the results. After running it, try creating your own function called `is_long_enough(password)` that returns `True` if the password is 12 or more characters.

### Answer the questions below

What keyword sends a value back from a function to the caller? `return`

If a function is defined as `def scan(target, port=80):`, what value does `port` take if you call `scan("192.168.1.1")` without specifying a port? `80`

On the VM, run `functions_`[`demo.py`](http://demo.py). What score does `score_password` return for the password `"TryHackMe2025!"`? `5`

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/3e528822-3e8c-49dd-b894-5d38e713511b.png align="center")

## Error Handling

What happens when a user types `"abc"` at a prompt that expects a number? In Simple Demo, our "Guess the Number" game would crash with a `ValueError` because `int("abc")` is not a valid conversion. In a polished program, we do not want a crash; we want a helpful message and a chance to try again. This is where **error handling** comes in.

## **The Problem**

Consider this short script:

```python
text = input("Enter a number: ")   # user types "hello"
number = int(text)                  # CRASH: ValueError
print(f"You entered {number}")
```

An example of running this script could be:

Terminal

```shell-session
Enter a number: hello
Traceback (most recent call last):
  File "demo.py", line 2, in <module>
    number = int(text)
ValueError: invalid literal for int() with base 10: 'hello'
```

The program stops entirely. Line 3 never runs. In a security tool that is scanning thousands of hosts, a single unexpected input could halt the entire operation.

## **The** `try`**/**`except` **Block**

Python's `try`/`except` structure lets you *attempt* a risky operation and *catch* the error if it occurs, rather than letting the program crash, the program would ask the user to try again:

```python
try:
    text = input("Enter a number: ")
    number = int(text)
    print(f"You entered {number}")
except ValueError:
    print("That is not a valid number. Please try again.")
```

An example of running this script could be:

Terminal

```shell-session
Enter a number: hello
That is not a valid number. Please try again.
```

The code inside `try` runs first. If no error occurs, Python skips the `except` block entirely. If an error *does* occur, Python jumps to the matching `except` block and runs that code instead of crashing.

## **Common Exception Types**

| **Exception** | **When It Occurs** | **Example** |
| --- | --- | --- |
| `ValueError` | Invalid type conversion | `int("abc")` |
| `FileNotFoundError` | File does not exist | `open("missing.txt")` |
| `ZeroDivisionError` | Division by zero | `10 / 0` |
| `KeyError` | Dictionary key not found | `d["missing_key"]` |
| `IndexError` | List index out of range | `mylist[99]` |

## **Catching Multiple Exceptions**

You can handle different errors differently:

```python
try:
    filename = input("File to open: ")
    with open(filename) as f:
        data = f.read()
    port = int(data.strip())
except FileNotFoundError:
    print(f"Error: '{filename}' does not exist.")
except ValueError:
    print(f"Error: the file does not contain a valid number.")
```

## **The Generic** `except`

Using `except Exception as e` catches most exceptions. Use it sparingly, as it can hide bugs you would want to know about:

```python
try:
    risky_operation()
except Exception as e:
    print(f"Something went wrong: {e}")
```

The `as e` part stores the exception's message in the variable `e`, which you can print for debugging.

## **Combining Error Handling with Loops**

A common and powerful pattern is to wrap user input in a loop so the program keeps asking until valid input is provided:

```python
while True:
    try:
        age = int(input("Enter your age: "))
        break   # valid input received; exit the loop
    except ValueError:
        print("Invalid input. Please enter a whole number.")
```

This pattern is essential in interactive tools. Our Password Strength Checker will use it to ensure the user enters a non-empty password before processing.

On the VM, open `error_`[`demo.py`](http://demo.py) and run it. The script deliberately triggers several types of exceptions and catches each one. Study how each `try`/`except` block handles its specific error type.

### Answer the questions below

What type of exception is raised when you attempt `int("hello")`? `ValueError`

What type of exception is raised when you try to open a file that does not exist? `FileNotFoundError`

On the VM, open a Python terminal and type `print(10 / 0)`. What is the name of the exception that Python raises? `ZeroDivisionError`

## Reading and Writing Files

In cyber security, you will constantly work with files: reading wordlists for brute-force attacks, parsing log files for suspicious activity, or writing scan results to a report. Python makes file operations straightforward, and modern best practices ensure they are done safely.

### Reading a File

The built-in `open()` function opens a file. You pass it the file path and a mode: `"r"` for reading, `"w"` for writing, and `"a"` for appending.

```markdown
f = open("passwords.txt", "r")
content = f.read()
print(content)
f.close()
```

This works, but there is a problem. If an error occurs between `open()` and `close()`, the file stays open in memory. Over time, leaked file handles can cause performance issues or data corruption. Python solves this with context managers.

### The `with` Statement (Context Managers)

The recommended way to work with files in modern Python is the with statement. It automatically closes the file when the indented block ends, even if an error occurs:

```markdown
with open("passwords.txt", "r") as f:
    content = f.read()

print(content)   # the file is already closed at this point
```

From this point forward, always use with for file operations. It is safer, cleaner, and considered a best practice across the industry.

## **Reading Methods**

| **Method** | **Returns** | **Best For** |
| --- | --- | --- |
| `.read()` | Entire file as a single string | Small files you process as one block |
| `.readline()` | The next single line | Processing one line at a time |
| `.readlines()` | A list where each element is one line | When you need all lines as a list |

A common and memory-efficient pattern is to loop directly over the file object:

```markdown
with open("common_passwords.txt", "r") as f:
    for line in f:
        password = line.strip()    # remove the trailing newline
        print(password)
```

The `.strip()` call is essential here. Each line in a text file ends with a newline character `(\n)`. Without`.strip()`, you would get `"password123\n"` instead of `"password123"`.

### Loading a Wordlist into a List

For our Password Strength Checker, we want to load a file of common passwords into a Python list so we can check user input against it:

```markdown
common_passwords = []

with open("common_passwords.txt", "r") as f:
    for line in f:
        common_passwords.append(line.strip())

print(f"Loaded {len(common_passwords)} common passwords.")
```

Now we can use the in operator to check whether a password is weak:

```markdown
if user_password in common_passwords:
    print("This password appears in the common-passwords list.")
```

### Writing to a File

To create a new file or overwrite an existing one, use mode `"w"`:

```markdown
with open("results.txt", "w") as f:
    f.write("Scan Results\n")
    f.write("============\n")
    f.write(f"Target: 192.168.1.1\n")
    f.write(f"Open ports: 22, 80, 443\n")
```

To add content to the end of an existing file without erasing it, use mode `"a"` (append):

```markdown
with open("results.txt", "a") as f:
    f.write(f"Additional finding: port 3306 open\n")
```

### Common File Modes

| **Mode** | **Description** |
| --- | --- |
| `"r"` | Read (file must exist) |
| `"w"` | Write (creates file or *overwrites* existing content) |
| `"a"` | Append (creates file or adds to existing content) |

Be careful with `"w"` mode. If the file already exists, opening it in write mode will erase all its contents immediately. If you want to preserve the original content and add to it, always use `"a"`.

On the VM, a file called `common_passwords.txt` is saved in the `/home/ubuntu/Building-Scripts/` directory. Open `files_`[`demo.py`](http://demo.py) and run it. The script loads the wordlist, prints how many passwords it found, and checks a sample password against the list. Then, open `common_passwords.txt` itself in VS Code and note how many lines it contains.

### Answer the questions below

What keyword introduces a context manager for safely opening files? `with`

What string method removes the trailing newline character from each line read from a file? `.strip()`

On the VM, run `files_demo.py`. How many passwords did it load from `common_passwords.txt`? `58`

## Libraries and Pip

In Simple Demo, we used `import random` to access the `random.randint()` function. That single line enabled us to generate random numbers without writing the algorithm ourselves. This concept of importing pre-written code is one of Python's greatest strengths.

### What Is a Library?

A **library** (also called a **module** or **package** depending on context) is a collection of pre-written code that you can import into your own programs. One analogy would be a toolbox: instead of forging a wrench from raw metal every time you need one, you reach into the toolbox and grab the wrench someone already made. In technical terms, libraries save you from reinventing the wheel.

### Importing Libraries

Python provides several ways to import:

```markdown
# Import the entire library
import os
print(os.getcwd())          # prints the current working directory

# Import a specific function
from datetime import datetime
now = datetime.now()
print(f"Current time: {now}")

# Import with an alias (nickname)
import datetime as dt
now = dt.datetime.now()
```

### The Python Standard Library

Python ships with a large collection of built-in modules called the **standard library**. These are available out of the box without installing anything. Here are a few you will encounter often:

| **Module** | **Purpose** | **Example Use** |
| --- | --- | --- |
| `os` | Interact with the operating system | List files, get environment variables |
| `sys` | System-specific parameters | Read command-line arguments |
| `random` | Generate random numbers | Pick random items, shuffle lists |
| `datetime` | Work with dates and times | Timestamps, time calculations |
| `json` | Read and write JSON data | Parse API responses, config files |
| `hashlib` | Cryptographic hashing | MD5, SHA-256 hashes |
| `string` | String constants and utilities | Access `string.punctuation`, `string.digits` |

For our Password Strength Checker, the `string` module is particularly useful. It provides constants like `string.ascii_uppercase`, `string.digits`, and `string.punctuation` that we can use to check password character variety without hardcoding every special character ourselves:

```plaintext
import string

password = "S3cure!Pass"

has_upper = any(c in string.ascii_uppercase for c in password)
has_digit = any(c in string.digits for c in password)
has_special = any(c in string.punctuation for c in password)

print(f"Uppercase: {has_upper}")     # True
print(f"Digit: {has_digit}")         # True
print(f"Special: {has_special}")     # True
```

### Installing Third-Party Libraries with pip

Beyond the standard library, thousands of community-built libraries are available through the Python Package Index (PyPI). You install them using pip, Python's package manager, from the terminal:

```markdown
pip install requests
```

Once installed, you import them just like any standard library module:

```markdown
import requests

response = requests.get("https://tryhackme.com")
print(response.status_code)   # 200
```

### Security-Relevant Libraries

As you progress through the Jr Penetration Tester path, you will encounter these libraries in the Python for Pentesters room:

*   `requests`: send HTTP requests (web scraping, API interaction)
    
*   `scapy`: craft, send, and sniff network packets
    
*   `pwntools`: CTF and binary exploitation toolkit
    
*   `paramiko`: SSH client and server implementation
    
*   `beautifulsoup4`: parse and extract data from HTML pages
    

You do not need to memorize these now. The key takeaway is that Python's ecosystem lets you go from "I need to send a packet" to "the packet is sent" in just a few lines of code, because someone else already built the hard parts.

On the VM, open `imports_demo.py` and run it. The script demonstrates `imports` from `os`, `datetime`, `string`, and `hashlib`. After running it, observe how `hashlib` computes a SHA-256 hash. Try changing the input string and rerunning to see how the hash changes completely.

### Answer the questions below

What is the name of Python's package manager used to install third-party libraries? `pip`

Which module from the standard library provides constants like ascii\_uppercase, digits, and punctuation? `string`

On the VM, run `imports_demo.py`. What is the first character of the SHA-256 hash it prints for the default input string? `5`

## Putting It All Together: Password Strength Checker

Over the previous tasks and the Core Concepts room, we learned about data types, strings, lists, dictionaries, operators, loops, functions, error handling, file I/O, and libraries. Now it is time to combine all those concepts into a single, working program: a Password Strength Checker.

Our program will do the following:

1.  Load a list of common passwords from a file (`common_passwords.txt`)
    
2.  Prompt the user for a password
    
3.  Check the password's length, character variety, and presence in the common list
    
4.  Assign a strength score and a label (Weak, Moderate, or Strong)
    
5.  Display the result and write it to a log file
    

Let's build it step by step. The complete program is saved as password\_checker.py in the `/home/ubuntu/Building-Scripts/` directory on the attached VM.

**Step 1: Imports and Common Passwords**

```markdown
import string

def load_common_passwords(filepath):
    """Load a list of common passwords from a text file."""
    common = []
    try:
        with open(filepath, "r") as f:
            for line in f:
                common.append(line.strip().lower())
    except FileNotFoundError:
        print(f"Warning: '{filepath}' not found. Skipping common-password check.")
    return common
```

This function uses file I/O (Task 4), error handling (Task 3), lists and loops (Core Concepts), and a function definition (Task 2). Notice the `try/except` block: if the file does not exist, the program prints a warning and continues instead of crashing. The `.lower()` call normalises each password, making comparisons case-insensitive.

**Step 2: The Scoring Function**

```plaintext
def check_password(password, common_list):
    """Evaluate a password and return (score, feedback_list)."""
    score = 0
    feedback = []

    # Length checks
    if len(password) >= 8:
        score += 1
    else:
        feedback.append("Password should be at least 8 characters.")

    if len(password) >= 12:
        score += 1

    # Character variety checks
    if any(c in string.ascii_uppercase for c in password):
        score += 1
    else:
        feedback.append("Add at least one uppercase letter.")

    if any(c in string.digits for c in password):
        score += 1
    else:
        feedback.append("Add at least one digit.")

    if any(c in string.punctuation for c in password):
        score += 1
    else:
        feedback.append("Add at least one special character (e.g., !, @, #).")

    # Common password check (overrides all other scoring)
    if password.lower() in common_list:
        score = 0
        feedback = ["This password is in the common-passwords list. Choose another."]

    return score, feedback
```

This function uses strings and string methods, the `in` operator, conditional logic, lists, and the `string` module (Task 5). It returns two values: a numeric score and a list of suggestions. In Python, you can return multiple values separated by commas; the caller receives them as a tuple.

**Step 3: The Main Program**

```plaintext
def main():
    strength_labels = {
        0: "Weak", 1: "Weak",
        2: "Moderate", 3: "Moderate",
        4: "Strong", 5: "Strong"
    }

    common_list = load_common_passwords("common_passwords.txt")

    while True:
        password = input("\nEnter a password to check (or 'quit' to exit): ")

        if password.lower() == "quit":
            print("Goodbye.")
            break

        if len(password) == 0:
            print("Password cannot be empty. Try again.")
            continue

        score, feedback = check_password(password, common_list)
        label = strength_labels.get(score, "Unknown")

        print(f"\nStrength: {label} ({score}/5)")

        if feedback:
            print("Suggestions:")
            for tip in feedback:
                print(f"  - {tip}")

        # Log the result (mask the actual password with asterisks)
        with open("password_log.txt", "a") as log:
            log.write(f"Password: {'*' * len(password)} | Strength: {label} ({score}/5)\n")

main()
```

This final section uses a dictionary for label mapping, a while True loop with break and continue, `f-strings`, file writing in append mode (Task 4), and function calls (Task 2). Notice that we do not log the actual password; we write asterisks instead. In a security context, never store plaintext passwords in logs.

### Running the Program

```markdown
$ python3 password_checker.py

Enter a password to check (or 'quit' to exit): password

Strength: Weak (0/5)
Suggestions:
  - This password is in the common-passwords list. Choose another.

Enter a password to check (or 'quit' to exit): Tr0ub4dor

Strength: Moderate (3/5)
Suggestions:
  - Add at least one special character (e.g., !, @, #).

Enter a password to check (or 'quit' to exit): C0mpl3x!Pass#99

Strength: Strong (5/5)

Enter a password to check (or 'quit' to exit): quit
Goodbye.
        
```

Every concept from both rooms is present in this program. We encourage you to open `password_checker.py` on the VM, run it, study the code, and experiment. Try adding new checks (minimum number of special characters, detection of consecutive repeated characters, dictionary word rejection) to make the checker even more robust.

### Answer the questions below

In the `check_password` function, what score does the password receive if it is found in the `common_passwords` list? `0`

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/1b42cb46-d6bb-40e1-a8de-2fc04db9dfc9.png align="center")

On the VM, run `password_checker.py` and enter the password TryHackMe!2025. What strength label does the program display? `Strong`

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/a6b5727c-12c9-41ca-8cb6-145608bc835a.png align="center")

After running the checker with the password TryHackMe!2025, open `password_log.txt` on the VM. How many asterisks appear on that line? `14`

![](https://cdn.hashnode.com/uploads/covers/5f4a98085ee1ba597542e097/b2325b56-6245-4371-a1bf-cf3cc039339d.png align="center")

## Conclusion

In this room, we covered the capabilities that turn code snippets into real scripts:

*   **Functions**: defining reusable blocks of code with `def`, parameters, return values, defaults, and scope
    
*   **Error handling**: using `try`/`except` to catch exceptions and build resilient programs that do not crash on unexpected input
    
*   **File I/O**: reading from and writing to files safely with the `with` context manager, and understanding `"r"`, `"w"`, and `"a"` modes
    
*   **Libraries**: importing standard modules like `os`, `string`, and `hashlib`, and installing third-party packages with `pip`
    

We tied all of these concepts together by building a Password Strength Checker, a practical tool that mirrors the kind of text-processing and validation work you will do as a penetration tester.

Looking back at the two rooms, you started with individual notes and ended with a complete song. You can now read, modify, and write Python programs that process data, make decisions, handle errors, work with files, and leverage an ecosystem of libraries. The next step is to apply these skills in a security context. It is time to join the [Python: Pentesting Scripts](https://tryhackme.com/room/pythonpentestingscripts) room, where you will use Python to enumerate targets, interact with web applications, scan networks, and more.
