This repository documents my hands-on journey into cybersecurity, built on a foundation of QA engineering and software testing.
Notes come from TryHackMe labs, CTF platforms, home lab work, and self-study — written to be clear, practical, and useful for anyone learning the same material.
Continuously updated as I progress.
| Section | Topics | Source |
|---|---|---|
| 🐧 Linux Fundamentals | Core commands, find, grep, shell operators | TryHackMe |
| 🌐 Network Services | SMB, Telnet — theory, enumeration, exploitation | TryHackMe |
| 🗺️ Nmap — Network Scanning | Scan types, flags, NSE scripts, recipes | TryHackMe + NetworkChuck |
| 🧪 CTF Labs — CyLab / picoCTF | Flags, CyberChef, Linux tools, Python | CyLab Security Academy |
| 🔍 KC7 — Threat Hunting with KQL | KQL queries, SOC investigations — A Rap Beef: An Intro to Security Investigations | KC7 / CyLab |
| 🛠️ QA Engineering Reference | Linux CLI, Python, Pytest, API testing | TripleTen Bootcamp |
Source: TryHackMe — Linux Fundamentals
Topics: Core commands, file searching, shell operators
Linux is the standard operating system in cybersecurity. Kali Linux, most servers, and most CTF environments run on it. These are the commands used every day in the terminal.
| Command | What it does | Example |
|---|---|---|
echo |
Print text to the screen | echo "hello" |
whoami |
Show which user you're logged in as | whoami |
ls |
List files and folders | ls -la |
cd |
Move into a folder | cd Documents |
pwd |
Show full path of current location | pwd |
cat |
Print a file's contents | cat passwords.txt |
find |
Search for files by name or type | find -name passwords.txt |
grep |
Search inside files for text | grep "admin" access.log |
wc |
Count lines, words, or characters | wc -l access.log |
mkdir |
Create a new folder | mkdir my_folder |
rm |
Remove a file | rm file.txt |
rm -r |
Remove a folder and its contents | rm -r folder_name |
cp |
Copy a file | cp file.txt /backup/ |
mv |
Move or rename a file | mv old.txt new.txt |
find searches the filesystem for files. Useful when you know a filename but not where it is.
# Find a specific file by name
find -name passwords.txt
# Find all files with a certain extension
find -name *.txt
# Search entire system, suppress permission errors
find / -name flag.txt 2>/dev/null💡 In CTFs,
findis essential for hunting flag files hidden deep in a filesystem. The2>/dev/nullpart hides "Permission denied" errors so results are clean.
grep searches inside files for a specific word or pattern — much faster than reading files manually.
# Find a specific IP in a log file
grep "81.143.211.90" access.log
# Case-insensitive search
grep -i "admin" config.txt
# Search recursively through all files in a folder
grep -r "password" /etc/
# Show line numbers with results
grep -n "error" app.logOperators let you chain commands and control where output goes.
| Operator | What it does | Example |
|---|---|---|
| |
Pipe — sends output of one command into the next | strings file | grep -i "pico" |
> |
Redirect output to a file (overwrites) | echo "hello" > file.txt |
>> |
Redirect output to a file (appends) | echo "world" >> file.txt |
&& |
Run second command only if first succeeds | mkdir dir && cd dir |
& |
Run command in the background | cp large_file /backup/ & |
⚠️ >silently overwrites existing files. Always use>>if you want to keep existing content.
Every file in Linux has permissions that define who can read, write, or execute it.
ls -l # View permissions
chmod 755 script.sh # Set rwx for owner, rx for others
chown user:group file # Change file ownershipPermission format: -rwxr-xr-x
- First character: file type (
-= file,d= directory) - Next 3: owner permissions (rwx)
- Next 3: group permissions (r-x)
- Last 3: everyone else (r-x)
Source: TryHackMe — Network Services
Topics: SMB and Telnet — understanding, enumeration, and exploitation
SMB is a protocol that lets computers on the same network share resources — files, folders, and printers. It works on a client-server model over TCP port 445.
Linux machines use Samba, an open-source implementation of SMB, to communicate with Windows systems in mixed environments.
Why SMB matters in security:
- EternalBlue (MS17-010) — the exploit behind the 2017 WannaCry ransomware attack, which spread globally via SMB
- Misconfigured shares — servers that allow anonymous access, meaning anyone can connect without a password and browse sensitive files
- In penetration testing, SMB is one of the first services checked — an open share can give an attacker a foothold into an entire network
Enumeration means gathering as much information as possible before attempting any attack.
Step 1 — Confirm SMB is running:
nmap -A -Pn -T4 -p- <IP>
# Look for port 445 or 139 to be openStep 2 — Enumerate with Enum4Linux:
Enum4Linux is a tool built to extract information from SMB servers.
# Full enumeration (recommended starting point)
enum4linux -a <IP>
# Just list shares
enum4linux -S <IP>
# Just list users
enum4linux -U <IP>
# Check password policy
enum4linux -P <IP>What to look for in the output:
- Share names — especially
files,backup,data,public - Shares showing
Mapping: OK, Listing: OK— these allow anonymous access - Usernames — real accounts usable in later attacks
- Password policy weaknesses — no minimum length, no lockout threshold
The most common SMB vulnerability isn't a complex exploit — it's misconfiguration. Many servers allow anonymous access: connecting without any credentials.
Connect to a share anonymously using SMBClient:
smbclient //<IP>/<SHARENAME> -U Anonymous -p 445
# When prompted for a password → press Enter (leave blank)Commands inside the SMB shell:
| Command | What it does |
|---|---|
ls |
List files in current directory |
cd <folder> |
Move into a folder |
get <file> |
Download a file to your local machine |
put <file> |
Upload a file |
exit |
Disconnect |
smb: \> ls
smb: \> cd confidential
smb: \> get passwords.txt💡 As a Security Engineer, finding an anonymously accessible SMB share is a critical finding. It means anyone on the network could read sensitive documents, credentials, or backups — and it's often the first step toward full network compromise.
SSH (Secure Shell) is the standard protocol for securely connecting to and controlling remote machines over a network. It replaced Telnet by encrypting everything in transit — including credentials. Understanding SSH is foundational to both offensive and defensive security work.
How it works:
SSH uses public-key cryptography for the handshake and can authenticate via password or SSH key pair. A key pair consists of a private key (stays on your machine) and a public key (placed on the server). Key-based authentication is significantly more secure than passwords.
Connecting via SSH:
# Default connection (uses port 22)
ssh username@ip_address
# Connect on a custom port
ssh username@ip_address -p 2222
# Connect using a specific key file
ssh -i ~/.ssh/my_key username@ip_addressWhy you sometimes need a port number:
SSH defaults to port 22 — you only need to specify -p when the server runs SSH on a non-default port. This is a common hardening practice: moving SSH off port 22 reduces automated bot scanning significantly. It's also required when port forwarding maps an external port to an internal machine's port 22.
One of the most important concepts in network security is understanding which machines are reachable from the internet — and which aren't.
| IP Type | Reachable from internet | Example ranges |
|---|---|---|
| Private | ❌ No | 192.168.x.x, 10.x.x.x, 172.16–31.x.x |
| Public | ✅ Yes | Assigned by your ISP |
Private IP addresses are reserved ranges the internet is designed to ignore. Routers will never forward them outside a local network. This means a machine on 192.168.x.x has no internet attack surface — bots scanning for open SSH ports will never find it, because there's no path to it from outside.
This is why moving SSH off port 22 matters on a public-facing server, but is unnecessary for a VM on a home lab with a private IP.
If your machines are on a private network, you can't SSH into them directly from another location. There are three main approaches:
1. WireGuard VPN (Recommended)
A VPN creates an encrypted tunnel between your laptop and your home network. With WireGuard running on a Raspberry Pi (via PiVPN), you connect from anywhere and your device behaves as if it's physically on your home network.
- Only one port exposed to the internet: UDP 51820 (encrypted)
- Once connected, access everything on the home network normally
- WireGuard is modern, fast, and far simpler to configure than older VPNs like OpenVPN
- This is the correct approach for a home lab: minimal exposure, maximum access
2. Port Forwarding
You configure your home router to forward an external port to a specific internal machine. Works, but exposes that port to the internet — meaning bots will find and probe it. Requires proper SSH hardening (key auth, non-default port, fail2ban) to be safe.
3. Reverse SSH Tunnel
The remote machine connects out to a cloud server (VPS), creating a tunnel you can reach back through. Useful when you can't control the router. Requires a VPS.
# On the remote machine — initiate the outbound tunnel
ssh -R 2222:localhost:22 user@your-vps.com
# From anywhere — connect back through the VPS
ssh -p 2222 user@your-vps.comWhy this matters in cybersecurity:
SSH is one of the most targeted services on the internet. Credential brute-force attacks against SSH on public port 22 are constant and automated. Understanding private vs public IP exposure, default port risks, and how to securely enable remote access is core knowledge for any security role — both for defending infrastructure and for identifying attack vectors during penetration testing or red team engagements.
Telnet is an old remote access protocol that sends all data — including passwords — in plain text. It's largely been replaced by SSH, but still appears in legacy systems, CTFs, and misconfigured environments.
Enumerate with a thorough Nmap scan:
nmap -A -Pn -T4 -p- <IP>| Flag | What it does | Why use it |
|---|---|---|
-A |
Aggressive — OS detection, version detection, scripts, traceroute | Maximum info in one command |
-Pn |
Skip ping — scan even if host appears offline | Hosts that block ICMP will show as "down" without this |
-T4 |
Timing level 4 — faster scan | Good balance of speed and accuracy |
-p- |
All 65,535 ports | Finds services on non-standard ports |
What to look for:
- Any open port — Telnet might be on port 23 or something unexpected like 8012
- Service banners — text the service sends when you connect, often revealing software name, version, or hints about how to proceed
- OS detection results
# Once you find an open port, connect directly
telnet <IP> <PORT>
# Or use netcat to grab the banner
nc <IP> <PORT>💡 Banners are valuable. When a service responds with text upon connection, that banner often tells you exactly what software is running — and sometimes hints at how to proceed. Never ignore it.
Source: TryHackMe — Further Nmap · NetworkChuck Tutorial
Topics: Port scanning, scan types, key flags, NSE scripts
In cybersecurity, you never attack blind. Nmap (Network Mapper) is the industry-standard tool for reconnaissance — scanning a target's ports, identifying open services, detecting operating systems, and finding known vulnerabilities.
Every port is like a door. Nmap walks around the building, knocks on every door, and reports back: "this one's open, this one's locked, this one has a firewall in front of it." That report is your map before the real work starts.
Every computer has 65,535 ports. Each port is a numbered channel where a specific service listens for connections.
| Port | Service |
|---|---|
| 22 | SSH |
| 23 | Telnet |
| 80 | HTTP |
| 443 | HTTPS |
| 139 / 445 | SMB (file sharing) |
| 3389 | RDP (Windows Remote Desktop) |
Port states Nmap reports:
| State | Meaning |
|---|---|
open |
A service is actively listening |
closed |
Port reachable but nothing is listening |
filtered |
A firewall is blocking Nmap — can't determine state |
| Flag | Scan Type | Notes |
|---|---|---|
-sT |
TCP Connect | Full 3-way handshake. Reliable but logged. No sudo needed. |
-sS |
SYN / Stealth | Sends SYN, gets SYN/ACK, sends RST to abort. Never completes handshake. Faster, less likely to be logged. Requires sudo. Default when running as root. |
-sU |
UDP | Scans UDP ports. Much slower than TCP. |
-sN |
TCP Null | No flags set. Evades some firewalls. |
-sF |
TCP FIN | FIN flag only. Similar evasion technique. |
-sX |
Xmas | FIN + PSH + URG flags. Named for lighting up like a Christmas tree. |
TCP Connect vs SYN — the key difference:
TCP Connect (-sT): SYN → SYN/ACK → ACK → RST (full handshake, appears in logs)
SYN Scan (-sS): SYN → SYN/ACK → RST (half-open, many apps won't log it)
⚠️ "Stealth" is relative — modern IDS/IPS systems absolutely detect SYN scans. Don't rely on them being invisible.
| Flag | What it does |
|---|---|
-O |
Detect the target's operating system |
-sV |
Detect service versions (e.g. Apache 2.4.41, OpenSSH 7.9) |
-A |
Aggressive — OS + version detection + script scanning + traceroute |
-sC |
Run default NSE scripts (checks for common vulnerabilities) |
| Flag | What it does |
|---|---|
-p 80 |
Scan only port 80 |
-p 80,443,445 |
Scan specific ports |
-p 1000-2000 |
Scan a range |
-p- |
Scan all 65,535 ports |
--top-ports 20 |
Scan the 20 most commonly used ports |
| Flag | What it does |
|---|---|
-T0 to -T5 |
Timing: 0 = paranoid/slow, 3 = default, 4 = aggressive, 5 = insane |
-v / -vv |
Verbose — show results as they come in |
-oN file.txt |
Save output in readable format |
-oG file.txt |
Save in grepable format |
-oA basename |
Save in all three formats at once |
| Flag | What it does |
|---|---|
--script=<name> |
Run a specific script |
--script=vuln |
Run all vulnerability detection scripts |
--script=default |
Same as -sC |
# Quick scan of top 1000 ports
nmap <IP>
# Full scan — all ports, versions, OS, scripts
sudo nmap -A -p- <IP>
# Fast stealth scan, all ports
sudo nmap -sS -T4 -p- <IP>
# Identify services on common ports
nmap -sV -p 22,80,443,445 <IP>
# Save results to file
nmap -oN results.txt <IP>
# Run vulnerability scripts
sudo nmap --script=vuln <IP>
# Find all live hosts on local network
nmap -sP 192.168.1.0/24
# Scan UDP top ports (slow but important)
sudo nmap -sU --top-ports 20 <IP>Platform: CyLab Security Academy (formerly picoCTF) — Carnegie Mellon University
URL: https://cylabacademy.org
Path: Beginner's Guide to the Challenge Library
A Capture the Flag competition is a cybersecurity challenge where you find hidden strings called flags — formatted like picoCTF{some_text_here} — by exploiting intentionally vulnerable systems, decoding files, or solving puzzles. CTFs are one of the best hands-on ways to build real security skills.
| Section | Modules | Status |
|---|---|---|
| CTF Onboarding | 3/3 | ✅ Complete |
| Section 1 — Sanity Checks | 3/3 | ✅ Complete |
| Section 2 — CyberChef | 4/4 | ✅ Complete |
| Section 3 — General Skills | 6/6 | ✅ Complete |
| Section 4 — Python | 6/6 | ✅ Complete |
| Section 5 — Web Exploits | 1/1 | 🔄 In Progress |
| Tool | What it does | When to use |
|---|---|---|
cat |
Print file contents | Read any text file |
strings |
Extract readable text from any file | Binary files with hidden flags |
grep |
Search for a pattern in output | Filter large outputs, find flags |
file |
Identify what type a file actually is | Always run first on unknown files |
nc (netcat) |
Connect to a remote server | Challenges with a host + port |
ssh |
Log into a remote machine | Challenges with SSH credentials |
base64 -d |
Decode Base64 strings | Encoded strings in challenges |
| CyberChef | Browser-based encoder/decoder | ROT13, Base64, hex, and more |
unzip |
Extract zip archives | Compressed file challenges |
bvi |
Binary file viewer | Inspect .bin or binary files in hex |
wget |
Download files from a URL | Getting challenge files |
When you receive a new challenge file, work through this in order:
# 1. Find out what type of file it actually is
file <filename>
# 2. Extract readable text and hunt for the flag
strings <filename> | grep -i "pico"
# 3. If encoded — open CyberChef and try Base64, ROT13, hex
# 4. If it's a website — view source, check /robots.txt, check JS files
# 5. If given a host + port
nc <host> <port>
# 6. If given SSH credentials
ssh user@host -p <port>Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — CTF Onboarding
Date: June 2026
Skills demonstrated: Platform orientation, understanding flags, using the webshell
The onboarding section introduces what a CTF is, what a flag looks like, and how to submit one. No technical skills are tested yet — the goal is to get comfortable with the platform and environment before the real challenges begin.
- A flag is always formatted as
picoCTF{some_text_here}— copy it exactly and paste it into the answer box. Flags are case-sensitive. - The Challenge Library (picoGym) is a permanent practice arena — challenges are always available, at your own pace, with hints allowed.
- The Webshell is a browser-based Linux terminal provided by picoCTF. It's a real Ubuntu environment, useful if you don't have your own Linux setup.
- Every challenge shows its category (General Skills, Cryptography, Web, Forensics, etc.) — use this to know which tools to reach for.
- Points don't matter for learning — understanding the why behind each solution does.
- The category label is your first clue about which tools and techniques apply.
- Having your own Kali Linux VM is better than the webshell — full tool access, no browser dependency.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 1
Date: June 2026
Skills demonstrated: Reading files with cat, SSH remote login, netcat connections
Three challenges that introduce the most fundamental Linux commands and networking tools: reading files, connecting via SSH, and using netcat. These three tools alone solve a large portion of beginner CTF challenges and are used constantly in real security work.
Challenge 1 — Obedient Cat: The flag was hidden in a plain text file. Reading it required one command:
cat flagChallenge 2 — Super SSH: Logging into a remote machine using SSH with credentials provided by the challenge.
ssh ctf-player@titan.picoctf.net -p 65341
# Accept the fingerprint prompt → type: yes
# Enter the password when asked| Part | Meaning |
|---|---|
ctf-player |
Username to log in as |
@titan.picoctf.net |
The remote machine's address |
-p 65341 |
Custom port (default SSH is 22) |
Challenge 3 — What's a Netcat?: Connecting to a raw TCP service with no credentials — the server sent the flag immediately on connection.
nc jupiter.challenges.picoctf.org 64287| Tool | Use when... |
|---|---|
ssh |
Challenge gives a username + password |
nc |
Challenge gives just a host + port |
cat,ssh, andncare three of the most-used tools in CTFs and real security work — get comfortable with all three.- The SSH fingerprint warning is normal in CTFs; in real environments always verify it before accepting.
- Netcat is called the "Swiss Army knife of networking" — it's also used for reverse shells, file transfers, and port listeners in real engagements.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 2
Date: June 2026
Skills demonstrated: ROT13, hex/decimal/binary conversion, Base64 decoding, CyberChef
This section introduces encoding and number systems — the foundation of almost all cryptography challenges. CyberChef is the primary tool: a free browser-based encoder/decoder built by GCHQ. Drag an operation into the Recipe, paste your input, and it transforms it instantly.
CyberChef: https://gchq.github.io/CyberChef/
Challenge 1 — Mod 26 (ROT13): ROT13 replaces every letter with the one 13 positions away in the alphabet. Applying it twice returns the original — it's its own reverse. Not real encryption, just obfuscation.
# Terminal method
echo "cvpbPGS{...}" | tr A-Za-z N-ZA-Mn-za-mChallenge 2 — Warmed Up (Hex → Decimal): The prefix 0x signals a hexadecimal number. Converting to decimal:
0x3D = (3 × 16) + 13 = 61
echo $((0x3D)) # → 61Challenge 3 — 2Warm (Decimal → Binary): Divide by 2 repeatedly, read remainders bottom-up.
42 → 101010 (binary)
echo "obase=2; 42" | bc # → 101010Challenge 4 — Bases (Base64): Base64 encodes binary data as readable text. It's used in JWTs, email attachments, and data URLs. It's not encryption — anyone can decode it instantly. Recognise it by the trailing = or == padding.
echo "bDNhcm5fdGgzX3IwcDM1" | base64 -d| Base | Name | Prefix | Example |
|---|---|---|---|
| 2 | Binary | none / 0b |
101010 = 42 |
| 10 | Decimal | none | 42 |
| 16 | Hexadecimal | 0x |
0x2A = 42 |
| Encoding | Recognise it by | Decode with |
|---|---|---|
| ROT13 | Scrambled letters | CyberChef ROT13 or tr |
| Base64 | Alphanumeric + +/=, no spaces |
CyberChef or base64 -d |
| Hex | 0x prefix or pairs like 48 65 6c |
CyberChef "From Hex" |
- Number system conversions (hex, binary, decimal) appear constantly in CTFs and real security work — memory addresses, file signatures, and colour codes all use hex.
- Base64 is not encryption. Seeing it in a web token or URL parameter is worth investigating.
- CyberChef's "Magic" operation can auto-detect the encoding — useful when you're not sure what you're looking at.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 3
Date: June 2026
Skills demonstrated: strings, grep, unzip, tab completion, web source inspection, robots.txt
The core toolkit for CTF survival: extracting text from binary files, filtering large outputs, tab completion for deep directory trees, inspecting web page source code, and exploiting robots.txt. These techniques appear in almost every CTF category.
Challenge 1 — Wave a Flag / Challenge 4 — Strings It: Extract readable text from a binary file and filter for the flag.
strings <file> | grep -i picoThis pattern is so common it should be automatic — run it on any unknown file before anything else.
Challenge 2 — Tab, Tab, Attack: Navigate a deeply nested directory tree using tab completion. Instead of typing full folder names, type the first few letters and press Tab — the terminal fills in the rest.
unzip Addadshashanammu.zip
strings Add<TAB>/Alm<TAB>/Ash<TAB>/... # tab-complete each folderChallenge 3 — Insp3ct0r: The flag was split across three files of a web page — the HTML, CSS, and JS — hidden in comments.
- HTML comment:
<!-- hidden here --> - CSS/JS comment:
/* hidden here */ - Steps: View Page Source → find part 1 → open linked
.cssand.jsfiles → find parts 2 and 3
Challenge 5 — First Grep: Search a large plain text file for the flag without reading every line.
grep -i pico <file>Challenge 6 — Where are the Robots?: robots.txt tells search engine crawlers which pages not to index — but those "hidden" URLs are visible to anyone. Always check it on web challenges.
https://challenge-url/robots.txt
→ Disallow: /8028f.html
https://challenge-url/8028f.html → flag is here
strings <file> | grep -i picois the first command to run on any unknown file in a CTF.- Tab completion saves enormous time and prevents typos — use it constantly.
- Always check
/robots.txton web challenges. TheDisallowentries are essentially a list of interesting pages the site owner wanted to hide from Google — but not from you. - View Page Source and check all linked CSS and JS files; flags and sensitive data are often hidden in comments.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 4
Date: June 2026
Skills demonstrated: Running Python scripts, reading source code, hex-to-ASCII conversion, MD5 hashing, dictionary attacks
This section builds Python skills through a series of password-cracking challenges. Each one adds a new layer of complexity — from a hardcoded plain-text password, to hex-encoded characters, to MD5 hashing. Reading the provided source code before doing anything else is the key skill throughout.
The challenge gave a Python script (ende.py), an encrypted flag file, and a password file. Reading the script revealed it needed a -d flag to decrypt and would prompt for a password.
cat pw.txt # read the password
python3 ende.py -d flag.txt.en
# enter password when promptedThe script used Fernet encryption — a symmetric scheme where the same key encrypts and decrypts. Since the key was in pw.txt, decryption was straightforward once the script's usage was understood.
Always
catornanoa script before running it — even a quick skim tells you what arguments it expects.
The password was hardcoded directly in the source code as plain text inside an if condition.
nano level1.py # find: if user_pw == "1e1a"
python3 level1.py -d level1.flag.txt.enc
# enter: 1e1aHardcoded credentials are a real-world vulnerability — penetration testers use grep -r "password" . to hunt for exactly this in source code and config files.
The password was stored as four hex character codes: chr(0x33) + chr(0x39) + chr(0x63) + chr(0x65). Each chr() call converts a number to its ASCII character.
# In Python shell — decode each value:
chr(0x33) # → '3'
chr(0x39) # → '9'
chr(0x63) # → 'c'
chr(0x65) # → 'e'
# Password: 39cepython3 level2.py -d level2.flag.txt.enc
# enter: 39ceWhen you see chr(0x__) in Python source code, the script is building a string character by character from hex values — decode each one and join them.
Skills demonstrated: MD5 hash comparison, dictionary attack, Python function modification, binary file inspection, reading source code
In this challenge, the password was no longer stored as plain text or a simple encoding — it was stored as an MD5 hash in a binary file (level3.hash.bin). Because MD5 is a one-way function, it can't be reversed to recover the original password. The solution was a dictionary attack: hash each candidate password from the provided list using the same MD5 function, and compare the result to the stored hash. When they match, the original password is found.
This mirrors a core real-world technique used by both attackers (cracking leaked password databases) and defenders (understanding why MD5 is no longer safe for password storage).
A hash is a one-way transformation of data. You put a password in, you get a fixed-length string of bytes out. Three key properties:
- One-way: You cannot reverse a hash to recover the original input
- Deterministic: The same input always produces the exact same output
- Fixed length: MD5 always produces a 16-byte (128-bit) result, regardless of input size
Password → MD5 hash function → Hash output
"87ab" → MD5 → E1 6D 55 A5 5D 80 DD DD 52 A8 3E AB EA 57 2B 7B
💡 Why
bvi? The hash is stored in binary format (.bin), not plain text. Opening it withcatproduces garbled output.bviis a binary file viewer that shows the raw hex bytes — which is exactly what you see when you inspectlevel3.hash.bin:E1 6D 55 A5 5D 80 DD DD 52 A8 3E AB EA 57 2B 7B. That's the MD5 hash of the correct password stored on disk.
The relevant parts of level3.py provided by the challenge:
import hashlib
flag_enc = open('level3.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level3.hash.bin', 'rb').read() # the stored hash
def hash_pw(pw_str):
pw_bytes = bytearray()
pw_bytes.extend(pw_str.encode())
m = hashlib.md5()
m.update(pw_bytes)
return m.digest() # returns the MD5 hash as raw bytes
# 7 possible passwords — only 1 is correct
pos_pw_list = ["f09e", "4dcf", "87ab", "dba8", "752e", "3961", "f159"]The script already has everything needed:
hash_pw()— hashes any password string using MD5correct_pw_hash— the stored hash to match againstpos_pw_list— the 7 candidates to test
1. Inspect the hash file (optional — useful to understand what you're working with):
bvi level3.hash.bin
# Shows raw bytes: E1 6D 55 A5 5D 80 DD DD 52 A8 3E AB EA 57 2B 7B
# :q to exit2. Modify the script to automate the search:
The key technique is to modify the original script rather than writing a separate loop. Three changes are needed:
- Add
user_pwas a parameter tolevel_3_pw_check()— so passwords can be fed in automatically instead of typed - Comment out the
input()line — prevents the program from stopping to ask for keyboard input on every iteration - Comment out the error print — keeps output clean (no noise for wrong guesses)
- Replace the single
level_3_pw_check()call with a for loop overpos_pw_list
import hashlib
flag_enc = open('level3.flag.txt.enc', 'rb').read()
correct_pw_hash = open('level3.hash.bin', 'rb').read()
def hash_pw(pw_str):
pw_bytes = bytearray()
pw_bytes.extend(pw_str.encode())
m = hashlib.md5()
m.update(pw_bytes)
return m.digest()
def level_3_pw_check(user_pw): # ← added parameter
# user_pw = input("Please enter correct password for flag: ") # ← commented out
user_pw_hash = hash_pw(user_pw)
if( user_pw_hash == correct_pw_hash ):
print("Welcome back... your flag, user:")
decryption = str_xor(flag_enc.decode(), user_pw)
print(decryption)
return
# print("That password is incorrect") # ← commented out
pos_pw_list = ["f09e", "4dcf", "87ab", "dba8", "752e", "3961", "f159"]
for pw in pos_pw_list: # ← replaces the single call
level_3_pw_check(pw)Output: the flag prints directly when the matching password is found.
| Step | What the code does |
|---|---|
hash_pw(pw) |
Takes the candidate password, converts it to bytes, runs MD5, returns the hash |
hash_pw(pw) == correct_pw_hash |
Compares the freshly computed hash to the one stored in .hash.bin |
| Match found | The loop prints the password — you now know which one to enter |
str_xor() |
The challenge script uses the password to XOR-decrypt the flag from the .enc file |
| Concept | What it means |
|---|---|
| MD5 | One-way hash function — same input always gives same output, cannot be reversed |
| Dictionary attack | Hash every candidate and compare — when hashes match, password is found |
| Rainbow tables | Pre-computed hash lookup tables — why storing passwords as plain MD5 is insecure |
bvi |
Binary file viewer — needed since cat on a .bin file produces garbled output |
m.digest() |
Returns the MD5 result as raw bytes — must match the binary stored in .hash.bin |
- MD5 is a one-way function — you can't reverse it, but you can replicate it. If you can hash all the candidates and compare, you don't need to reverse anything.
- Dictionary attacks are the standard approach to cracking hashed passwords when a candidate list exists. In the real world, attackers use wordlists with millions of entries (like
rockyou.txt) and tools likehashcatto crack MD5 hashes from breached databases almost instantly. - Storing passwords as plain MD5 hashes is considered insecure precisely because of dictionary and rainbow table attacks. Modern systems use slow hashing algorithms like bcrypt or Argon2 with salting to make this computationally infeasible.
- Reading source code is always the first step — the script told us exactly which hash function was used (
hashlib.md5), where the stored hash lived (level3.hash.bin), and what the candidate passwords were (pos_pw_list). - Python is case-sensitive:
correct_pw_hash≠Correct_pw_hash. ANameErroralmost always means a capitalisation mistake.
Skills demonstrated: MD5 dictionary attack, function parameterisation, automated password testing at scale
PW Crack 4 builds directly on PW Crack 3 — same MD5 hashing technique, same dictionary attack approach — but scales up from 7 candidates to 100. The challenge reinforces the same script-modification pattern while adding a practical detail: with 100 candidates, uncontrolled input() calls and noisy error output become real friction that must be handled cleanly.
The same three modifications from PW Crack 3 apply here:
| Change | Why it matters |
|---|---|
Add user_pw as a function parameter |
Allows the loop to feed each candidate in automatically — no keyboard input needed |
Comment out input() |
Without this, Python stops and waits for typed input on every single iteration |
| Comment out error print | With 100 candidates, leaving it active produces 99 noise lines before the flag appears |
def level_4_pw_check(user_pw): # ← parameter instead of input()
# user_pw = input("...") # ← commented out
user_pw_hash = hash_pw(user_pw)
if( user_pw_hash == correct_pw_hash ):
print("Welcome back... your flag, user:")
decryption = str_xor(flag_enc.decode(), user_pw)
print(decryption)
return
# print("That password is incorrect") # ← commented out
for pw in pos_pw_list:
level_4_pw_check(pw)The modified script iterates through all 100 candidates, hashing each one with MD5 and comparing the result to the hash stored in level4.hash.bin. When the hashes match, the function calls str_xor() internally and prints the decrypted flag — no manual step required.
- The pattern from PW Crack 3 is reusable: add parameter → comment out input → comment out error print → replace single call with loop. This generalises to any number of candidates.
- Passing values as function parameters instead of relying on
input()is a foundational programming concept — it's what makes functions testable and automatable. - Commenting out noise (error prints, prompts) is good practice when automating repetitive operations. Clean output is faster to read and less error-prone.
- At scale (millions of candidates), this same logic is what tools like
hashcatimplement — just much faster, with GPU acceleration and optimised data structures.
Skills demonstrated: MD5 dictionary attack, reading passwords from an external file, Python file I/O, strip() for text cleaning
PW Crack 5 introduces a key real-world pattern: instead of hardcoding candidate passwords inside the script, they are read from an external file (dictionary.txt) — one password per line. The core MD5 hashing and comparison logic is identical to PW Crack 3 and 4. The new challenge is reading the file correctly and cleaning each line before hashing.
This mirrors how password cracking works in practice: tools like hashcat and john consume wordlist files (such as rockyou.txt with 14 million entries) and hash each candidate against a target hash. The only difference is scale.
Reading a file and building the candidate list:
with open('dictionary.txt', 'r') as f:
pos_pw_list = [line.strip() for line in f]| Component | What it does |
|---|---|
open('dictionary.txt', 'r') |
Opens the file in text read mode |
with ... as f |
Safely opens the file; Python closes it automatically when the block ends |
f |
A variable name for the file object — could be called anything |
for line in f |
Reads each line from the file one at a time |
line.strip() |
Removes the \n newline character from the end of each line |
Why strip() is essential:
Python includes the newline character when reading lines from a file:
"password123\n"
Hashing "password123\n" produces a completely different MD5 result from hashing "password123" — so without strip(), nothing would ever match, even the correct password.
The complete modified function:
def level_5_pw_check():
with open('dictionary.txt', 'r') as f:
pos_pw_list = [line.strip() for line in f]
for pw in pos_pw_list:
if hash_pw(pw) == correct_pw_hash:
print("Correct password found:", pw)
decryption = str_xor(flag_enc.decode(), pw)
print(decryption)
return
print("Password not found in dictionary")Note: unlike PW Crack 3 and 4, no input() existed in the original script — the function body was replaced entirely rather than modified.
| Feature | PW Crack 4 | PW Crack 5 |
|---|---|---|
| Password source | Hardcoded list in script | External dictionary.txt file |
| File I/O needed | No | Yes — open() + iteration |
strip() needed |
No | Yes — removes \n from each line |
input() to comment out |
Yes | No (not in original) |
| Hash comparison loop | Same | Same |
- Reading passwords from an external file is the standard approach in real-world tooling. Hardcoding candidates is fine for small CTF lists but doesn't scale — a file can hold millions of entries without changing any code.
strip()is mandatory when processing text files line by line. Forgetting it is one of the most common bugs when working with file input in Python.- The
with open(...) as fpattern is the correct way to open files in Python — it guarantees the file is closed even if an error occurs inside the block. fis a conventional short name for a file object, the same wayiis conventional for loop counters. Variable names in Python are arbitrary — what matters is consistency and readability.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 5.
Date: June 2026
Skills demonstrated: SVG file analysis, XML source inspection, identifying data hidden in plain sight, source modification to understand rendering
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 5, Module 1: "Enhance!"
Date: June 2026
Skills demonstrated: SVG file analysis, XML source inspection, identifying data hidden in plain sight, source modification to understand rendering
This challenge introduced the SVG file format — a text-based image format written in XML. Unlike raster images (JPEG, PNG), SVG files are plain readable text that describes how to draw an image using shapes, coordinates, and properties. The flag was hidden inside the file using three simultaneous techniques, each of which alone might seem insufficient but together made the data completely invisible when rendered.
The approach that found the flag: open the raw XML source directly and read it — because the browser renders what it chooses to show you, but the source always tells the truth.
SVG (Scalable Vector Graphics) is an XML-based image format. Instead of storing pixels, it stores instructions for how to draw shapes:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<circle cx="100" cy="100" r="80" fill="blue"/>
<text x="50" y="110" font-size="20">Hello</text>
</svg>This means SVG files are fundamentally different from JPEG or PNG:
| Property | JPEG / PNG | SVG |
|---|---|---|
| Format type | Raster — pixel grid | Vector — XML instructions |
| Readable as text | No — binary data | Yes — plain XML |
| Can embed JavaScript | No | Yes — <script> is valid |
| Can hide data in source | Only in binary metadata | In comments, invisible elements, attributes |
| Scales without quality loss | No | Yes — rendered mathematically |
The flag picoCTF{3nh4nc3d_aab729dd} was hidden in the SVG using:
Layer 1 — Microscopic font size:
style="font-size:0.00352781px;"Sub-pixel text: rendered by the browser but at 0.003px — physically invisible.
Layer 2 — White text on white background:
style="fill:#ffffff;"#ffffff is white. The page background is also white. Even at readable size, invisible.
Layer 3 — Hidden inside a white circle:
<circle cx="107.59055" cy="132.30211" r="3.3341289" style="fill:#ffffff;"/>A white-filled circle positioned directly over the text, acting as a visual mask.
The flag was also split character by character across multiple <tspan> elements stacked vertically at fractional y-coordinate intervals — like a word search read from source, not from the rendered page.
The challenge was solved by opening the raw XML source and reading it directly — noticing <text> and <tspan> elements containing characters of the flag format, even without the benefit of searching.
For future SVG challenges, the systematic approach:
# 1. Read the raw XML source
cat drawing.svg | less
# 2. Search for the flag directly
grep -i "pico" drawing.svg
grep -i "CTF" drawing.svg
# 3. Look for hidden-content indicators
grep -i "font-size" drawing.svg # tiny fonts
grep -i "fill:#ffffff" drawing.svg # white-on-white
grep -i "opacity:0" drawing.svg # invisible elements
grep -i "<!--" drawing.svg # XML comments
# 4. Extract all text strings
strings drawing.svgAfter finding the flag, the source was edited — increasing font-size to something readable and changing fill:#ffffff to fill:#000000. The image was reopened and the characters became visible, but overlapping and jumbled.
Why jumbled: The y coordinates between <tspan> elements were designed for microscopic spacing (differences of ~0.004 units). When the font size became 12px, each character occupied real vertical space but the positions still stacked them nearly on top of each other.
Why the circle was hard to resize: All elements share the same coordinate system. Resizing one without adjusting the others causes them to misalign.
The principle this reveals: SVG separates content from presentation. The flag text existed in the file regardless of how it was displayed. Changing display properties (font-size, fill, opacity) does not touch the data — it only changes what the renderer shows. This is exactly why source inspection is more reliable than visual inspection.
| Risk | Mechanism |
|---|---|
| XSS via SVG upload | <script> tags are valid SVG — a malicious SVG rendered by a web app can execute JavaScript in a victim's browser |
| SSRF via SVG | SVG can make HTTP requests via embedded links — server-side rendering of SVGs can leak internal network data |
| Upload filter bypass | SVG is classified as text/xml, not a binary image — some upload filters designed to block image-based payloads miss it |
| Steganography | Data hidden in comments, invisible elements, off-screen coordinates, or microscopic text — as in this challenge |
In incident response and malware analysis, SVG files are always inspected as text files, never just opened as images.
- The rendered view and the source are never the same thing. A browser decides what to show you — the XML source always shows what is actually there.
cat,strings, andgrepwork on SVG files because they are plain text. This is the correct first approach for any SVG in a CTF or investigation.- Three simultaneous hiding techniques (tiny font, white colour, masked by white shape) made this flag visually undetectable while it remained in fully readable plain text.
- SVG is a legitimate attack vector in real security work — XSS via malicious SVG uploads is a documented vulnerability class. Understanding SVG source inspection is directly applicable to web application security testing.
- Source modification is a powerful learning technique: change a property, reopen the file, observe what changed. This is how you build intuition for how rendering systems work.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 5, Module 2: "Big Zip"
Date: June 2026
Skills demonstrated: unzip, recursive grep -r, understanding compression format differences
This challenge presented a .zip archive containing hundreds of files and subdirectories. The flag was hidden somewhere inside — the task was to locate it efficiently without manually inspecting every file. The solution introduced one of the most practical command-line patterns in CTF work: extract first, then search recursively.
Why grep can't read a .zip directly:
grep operates on plain text. A .zip file is a binary container — its contents are compressed and not directly readable. You must extract the archive first and then search the resulting files.
During research, man grep revealed zgrep, zegrep, and zfgrep — wrapper tools that can search inside compressed files without manually decompressing them. However, these tools work with gzip (.gz) format only, not .zip. The file command confirms the difference:
file BigZip.zip
# → Zip archive dataThe solution — unzip, then recursive grep:
# Step 1: extract the archive
unzip BigZip.zip
# Step 2: search recursively through all extracted files
grep -r "picoCTF" big-zip-files/| Flag | What it does |
|---|---|
-r |
Recursively search all files and subfolders inside the directory |
-i |
Case-insensitive — useful when unsure about capitalisation |
-R |
Same as -r but also follows symbolic links |
The flag appeared immediately: picoCTF{gr3p_15_m4g1c_ef8790dc}
Recursive grep is one of the most frequently used patterns in both CTF work and real security investigations — searching through log dumps, extracted filesystem images, source code repositories, or any large collection of files for a specific string.
# General pattern
grep -r "search_term" target-directory/
# Case-insensitive version
grep -ri "picoctf" target-directory/grepcannot read inside.ziparchives — always unzip first, then search the extracted contents.-ris the recursive flag: it searches every file inside a directory tree automatically, no matter how many files or how deep the nesting goes.zgrepand friends are useful tools to know — but only for.gz(gzip) files. Format confusion is a common stumbling block;file <filename>always resolves it.- This pattern — extract → recursive grep — appears constantly in CTF forensics, log analysis, and incident response work.
Platform: CyLab Security Academy / picoCTF
Module: Beginner's Guide to the Challenge Library — Section 5, Challenge 3: "Vault Door Training"
Date: June 2026
Skills demonstrated: Java source code analysis, static analysis, identifying hardcoded secrets, introduction to reverse engineering
This challenge introduced source code analysis as a security technique. The task is framed as a spy mission — infiltrate Dr. Evil's vault by cracking the password for a series of locked doors. The training vault comes with its Java source code (VaultDoorTraining.java) included. No exploitation required: reading the code is the entire solution.
The checkPassword() method compares user input directly to a hardcoded string in plain text. The flag is the hardcoded string wrapped in picoCTF{...}.
How the program validates input:
// main() strips the picoCTF{} wrapper before calling checkPassword()
String input = userInput.substring("picoCTF{".length(), userInput.length()-1);
if (vaultDoor.checkPassword(input)) {
System.out.println("Access granted.");
}
// checkPassword() compares the stripped input to a hardcoded string
public boolean checkPassword(String password) {
return password.equals("w4rm1ng_Up_w1tH_jAv4_000wYdiGTvt");
}The hardcoded string in password.equals(...) is the flag content. No decoding, no running the program — just read the source.
Java vs JavaScript — not the same language:
| Java | JavaScript | |
|---|---|---|
| File extension | .java (source), .class (compiled) |
.js |
| Compiled? | Yes — javac compiles to JVM bytecode |
No — interpreted at runtime |
| Primary use | Desktop/backend apps, Android, CTF RE challenges | Web browsers, Node.js |
Despite the similar names, these are completely unrelated languages.
# Read the source file
cat VaultDoorTraining.java
# Jump straight to the password line
grep "equals" VaultDoorTraining.java
# → return password.equals("w4rm1ng_Up_w1tH_jAv4_eec0716b713");
# Flag: picoCTF{w4rm1ng_Up_w1tH_jAv4_eec0716b713}Compiling and running is optional — the flag is readable from source alone. If you do run it: javac VaultDoorTraining.java → java VaultDoorTraining → enter the full flag with the picoCTF{...} wrapper → "Access granted."
- Hardcoded secrets in source code are a real vulnerability class — not just a CTF trick. Java
.classfiles decompile cleanly, so embedding a password in code is equivalent to writing it in a comment. - In real systems, secrets belong in environment variables or dedicated secret managers (AWS Secrets Manager, HashiCorp Vault) — never in source code.
- This challenge is the entry point to the full Vault Door series (1–8), where each subsequent challenge obfuscates the password further (character arrays, encoding, XOR, bit shifting). The obfuscation increases, but the core weakness — password embedded in code — is always present.
grep "equals"is a fast pattern for finding string comparisons in Java CTF source files.
Learning journey: CyLab Beginner's Guide → Section 5 (Web Exploits & Source Analysis) → Vault Door series continues
Platform: KC7 — CyLab Security Academy
URL: https://kc7cyber.com
Role: SOC Analyst simulation — investigate real-style attacks using log data
KC7 is a hands-on cybersecurity training platform where you play the role of a SOC (Security Operations Center) analyst investigating cyberattacks using log data and KQL queries. Each scenario is a self-contained investigation — you're given access to logs and must figure out what happened.
KQL is a read-only query language used to search and analyse large volumes of log data. Think of it like SQL, but built specifically for security logs and event data. It's used in real-world tools like Microsoft Sentinel, Azure Data Explorer, and Defender for Endpoint.
| Use Case | What analysts do |
|---|---|
| Threat Hunting | Proactively search logs for hidden attackers before an alert fires |
| Incident Investigation | After a breach, trace exactly what happened — logins, file access, data exfiltration |
| Anomaly Detection | Find unusual patterns like a user logging in at 3am from two countries simultaneously |
TableName
| operator condition
| operator conditionStart with the table, use | to chain operators (just like piping in Linux), and each line filters or transforms the data further.
where — filter rows by a condition (like grep in Linux):
Employees
| where role == "Editorial Director"count — count how many rows remain:
Email
| where recipient == "target@example.com"
| countdistinct — return only unique values (like sort | uniq in Linux):
Email
| where sender has "suspicious-domain.com"
| distinct sender
| countlet — store a result as a variable to reuse it:
let target_ips = Employees
| where name has "Mary"
| distinct ip_addr;
OutboundNetworkEvents
| where src_ip in (target_ips)
| countThe semicolon
;closes theletblock. The second query runs immediately after using the stored values.
| Operator | Meaning | Example |
|---|---|---|
== |
Exact match (case-sensitive) | role == "Admin" |
has |
Contains a whole word | sender has "gmail.com" |
contains |
Contains a substring | domain contains "hire" |
| Table | What it contains |
|---|---|
Employees |
Staff info: name, role, email, IP, username |
Email |
Email logs: sender, recipient, subject, timestamp |
OutboundNetworkEvents |
Web browsing: source IP, URL, timestamp |
PassiveDns |
DNS records: domains and the IPs they resolved to |
AuthenticationEvents |
Login attempts: username, success/fail, timestamp, IP |
| # | Scenario | Date | Status |
|---|---|---|---|
| 0 | How to Play KC7 | Jun 2026 | ✅ Complete |
| 1 | A Rap Beef — Module 1: Enough Beef for a Burger | Jun 2026 | ✅ Complete |
| 2 | A Rap Beef — Module 2: Less Beef, More Phish | Jun 2026 | 🔄 In Progress |
Platform: KC7 — CyLab Security Academy
Module 1: Enough Beef for a Burger
Date: June 2026
Skills demonstrated: KQL querying, log analysis, reconnaissance detection, information disclosure vulnerabilities, account takeover investigation
In this investigation, I played the role of a SOC analyst at OWL Records — a music label that had been targeted by a threat actor. The attacker's goal was to take over the account of one of the label's artists. The scenario introduced core log analysis skills: querying inbound network traffic, reading browsing activity to establish attacker intent, and tracing an account takeover step by step.
This type of investigation directly mirrors real SOC work: an alert fires, you pull the logs, and you follow the trail from first contact to full compromise.
Tables used:
| Table | What it contains |
|---|---|
Employees |
Staff info: names, roles, email addresses |
InboundNetworkEvents |
Web server logs: source IP, URL, timestamp, HTTP method |
KQL operators introduced:
| Operator | What it does | Example |
|---|---|---|
where |
Filter rows by condition | where role == "CEO" |
between |
Filter by a time range | where timestamp between (datetime("...") .. datetime("...")) |
has |
Match a whole word within a field | where src_ip has "18.66.52.227" |
has_any() |
Match any one of several keywords | where url has_any("washington", "fluffy") |
Vulnerability identified: Information disclosure via GET request URL parameters. The target's email address and username were exposed directly in a password reset URL — allowing the attacker to harvest credentials passively just by browsing the site.
The investigation unfolded in stages:
1. Identifying the target organisation's leadership
Employees
| where role == "CEO"2. Scoping the attacker's activity to a single day
InboundNetworkEvents
| where timestamp between (datetime("2024-04-10T00:00:00") .. datetime("2024-04-11T00:00:00"))
| where src_ip has "18.66.52.227"Result: 19 rows — confirming the attacker (IP 18.66.52.227) was actively browsing the OWL Records website on 10 April 2024.
3. Reading the browsing trail
Reviewing the 19 URLs told the full story. The attacker started with broad searches ("OWL Records rapper contact info"), narrowed to a specific artist (Dwake), and eventually found his email address and username exposed in a password reset URL:
https://owl-records.com/account/reset-password?username=dwaubrey&email=dwake_audrey@owl-records.com
4. Catching the account takeover in the logs
InboundNetworkEvents
| where timestamp between (datetime("2024-04-10T00:00:00") .. datetime("2024-04-11T00:00:00"))
| where url has_any("washington", "fluffy")
| where src_ip has "18.66.52.227"This surfaced the exact moment the attacker submitted answers to Dwake's security questions — completing the account takeover.
Full attack chain:
Reconnaissance → Information disclosure (URL leak) → Password reset → Security questions bypass → Account takeover
- Sensitive data (emails, usernames) should never appear in GET request URLs — they end up in server logs, browser history, and referrer headers, making them trivially harvestable from logs alone.
- Security questions are a weak authentication fallback; answers are often publicly available or guessable. Modern systems replace them with MFA/TOTP.
has_any()is a powerful KQL operator for quickly surfacing events matching any one of several known values — useful when you already know what you're looking for.- Attacker behaviour in logs often tells a story: the progression from broad searches to targeted queries to account actions maps directly to the reconnaissance → exploitation chain seen in real incidents.
Learning journey: Threat Hunting with KQL → A Rap Beef Module 1 → Module 2: Less Beef, More Phish
Source: TripleTen QA Engineering Bootcamp
Topics: Linux CLI, Python basics, Pytest, API testing
This section documents the technical foundations from QA engineering training — Linux command line, Python, automated testing with Pytest, and HTTP/API testing. These skills directly transfer to cybersecurity work.
| Command | Description | Example |
|---|---|---|
pwd |
Print current working directory | pwd |
ls |
List files and folders | ls -l |
cd |
Change directory | cd Documents |
mkdir |
Create a new directory | mkdir new_folder |
rm |
Remove files | rm file.txt |
rm -r |
Remove directory and contents recursively | rm -r folder_name |
cp |
Copy files | cp file.txt /home/user/Desktop |
mv |
Move or rename files | mv file.txt newfile.txt |
touch |
Create a new empty file | touch notes.txt |
cat |
Display file contents | cat file.txt |
grep |
Search for text inside files | grep "error" logfile.txt |
find |
Search for files | find /home -name notes.txt |
sudo |
Execute as superuser | sudo apt update |
chmod |
Change file permissions | chmod 755 script.sh |
chown |
Change file ownership | sudo chown user:group file.txt |
💡 Use --help after any command (e.g. ls --help) to see available options.
Variables and Types
name = "Camilo" # string
age = 30 # integer
height = 1.80 # float
is_active = True # booleanFunctions
def greet(name):
return f"Hello, {name}!"
print(greet("Camilo")) # Hello, Camilo!Lists and Loops
tools = ["nmap", "wireshark", "burpsuite"]
for tool in tools:
print(tool)Conditionals
port = 80
if port == 80:
print("HTTP")
elif port == 443:
print("HTTPS")
else:
print("Unknown service")Pytest is a testing framework for Python. A test is any function whose name starts with test_.
def test_sum():
assert 2 + 3 == 5
def test_deposit_positive():
deposit = get_user_deposit()
assert deposit > 0Running tests:
pytest # run all tests in project
pytest calc_test.py # run tests in a specific fileThree rules for clean tests:
- ✅ One test → one assertion
- ✅ Tests use independent data — no shared state
- ✅ Each test can run alone, in any order
import requests
import configuration
def get_docs():
return requests.get(configuration.URL_SERVICE + configuration.DOC_PATH)
response = get_docs()
print(response.status_code) # 200 = OKCommon response attributes:
| Attribute | Purpose |
|---|---|
status_code |
HTTP response code (200, 404, 500...) |
text |
Response body as a string |
json() |
Response body parsed as a dictionary |
headers |
Response headers |
ok |
True if status code is 2xx |
A Python reference by Jack Rhysider (host of Darknet Diaries) is included in this repo for study and reference.
📄 Credit: Original creator — Jack Rhysider · All rights belong to the original author.
Areas actively in progress — notes will be added as completed:
- Home Lab Setup — Kali Linux VM, VPN server (PiVPN + WireGuard), Pi-hole, network segmentation
- KC7 Investigations — ongoing SOC simulation writeups
- picoCTF / CyLab — Section 4 (Python) and beyond
- PowerShell for Security — scripting and automation
- Networking Deep Dive — TCP/IP, DNS, HTTP in depth
- Web Application Security — OWASP Top 10, Burp Suite
Built while studying for a Master of Cyber Security at Edith Cowan University (ECU), Perth — starting 2026.