Introduction
In the Python: Core Concepts 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:
Machine Access
You can follow along by starting the attached virtual machine (VM). Visual Studio Code (VS Code) should open automatically with a modern Python installation ready for you. All example scripts discussed in this room are saved in the /home/ubuntu/Building-Scripts/ directory. We strongly encourage you to run, modify, and experiment with every code snippet on the VM as you progress through the tasks.
?Answer the questions below
- Let's build real scripts!
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.
def greet(name):
print(f"Hello, {name}. Welcome to the system.")To use (or call) this function:
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.
def check_length(password, min_length):
if len(password) >= min_length:
return True
else:
return False
result = check_length("Tr0ub4dor", 8)
print(result) # TrueWhen 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:
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 scoreThis 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:
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.
def calculate():
result = 42 # local variable
return result
calculate()
# print(result) # This would cause an error: result is not defined hereScope prevents functions from accidentally interfering with each other's data. It is a feature, not a limitation.
Open functions_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?
- 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?
- On the VM, run functions_demo.py . What score does score_password return for the password "TryHackMe2025!" ?
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:
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:
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:
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:
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:
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:
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:
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 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") ?
- What type of exception is raised when you try to open a file that does not exist?
- On the VM, open a Python terminal and type print(10 / 0) . What is the name of the exception that Python raises?
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.
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:
with open("passwords.txt", "r") as f:
content = f.read()
print(content) # the file is already closed at this pointFrom 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:
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:
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:
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":
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):
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 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?
- What string method removes the trailing newline character from each line read from a file?
- On the VM, run files_demo.py . How many passwords did it load from common_passwords.txt ?
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:
# 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:
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}") # TrueInstalling 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:
pip install requests
Once installed, you import them just like any standard library module:
import requests
response = requests.get("https://tryhackme.com")
print(response.status_code) # 200Security-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?
- Which module from the standard library provides constants like ascii_uppercase , digits , and punctuation ?
- On the VM, run imports_demo.py . What is the first character of the SHA-256 hash it prints for the default input string?
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:
- Load a list of common passwords from a file (
common_passwords.txt) - Prompt the user for a password
- Check the password's length, character variety, and presence in the common list
- Assign a strength score and a label (Weak, Moderate, or Strong)
- 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
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 commonThis 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
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, feedbackThis 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
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
$ 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?
- On the VM, run password_checker.py and enter the password TryHackMe!2025 . What strength label does the program display?
- After running the checker with the password TryHackMe!2025 , open password_log.txt on the VM. How many asterisks appear on that line?
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/exceptto 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
withcontext manager, and understanding"r","w", and"a"modes - Libraries: importing standard modules like
os,string, andhashlib, and installing third-party packages withpip
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 room, where you will use Python to enumerate targets, interact with web applications, scan networks, and more.
?Answer the questions below
- I have successfully completed the room!