Foundations of Ethical Security Testing with Python: A Beginner's Laboratory Guide
Chapter 11 of 11 · updated Jul 08, 2026
Troubleshooting and Common Pitfalls: When Labs Break and Scripts Fail
Troubleshooting and Common Pitfalls: When Labs Break and Scripts Fail
Lab Wintermute will break. Not might—will. Every student who has ever built an isolated pentesting environment has watched Kali lose its network adapter after a kernel update, or seen Metasploitable2's FTP banner disappear mid-exercise, or run a Python reconnaissance script against 192.0.2.20 only to stare at a traceback that means nothing. This page is the runbook I wish I'd had during my first dozen lab builds. Each block follows the same rhythm: symptom, diagnostic, fix, and what I learned the hard way.
Symptom: python launches Python 2, python3 launches 3.x, and your scripts fail with print syntax errors or missing urllib attributes
Likely cause: The system has both Python 2 and Python 3 installed, and your shebang line, virtual environment, or muscle memory is pointing at the wrong interpreter. This is especially common on Kali Linux, which historically symlinked python to Python 2 for compatibility with legacy tools.
Diagnostic:
which python && python --version
which python3 && python3 --version
ls -la /usr/bin/python*
Fix: Be explicit. In every script header, use #!/usr/bin/env python3. When installing packages, use pip3 rather than pip unless you have verified which interpreter pip serves. If you have created a virtual environment (a self-contained Python installation for project isolation), activate it with source venv/bin/activate and verify the path:
which python
# illustrative output — verify on your target: /home/student/lab-wintermute/venv/bin/python
Why this matters: A virtual environment copies the Python interpreter into your project directory. When activated, it prepends itself to your
PATH. This prevents system-wide package conflicts, but only if you actually activate it. I have lost hours to scripts that ran against the system Python while myrequestslibrary sat installed in an inactive venv.
If you see ModuleNotFoundError despite installing the package, you almost certainly installed it for the wrong interpreter. Uninstall from the wrong one, reinstall from the right one, and move on.
Symptom: pip install fails with SSL certificate errors, connection timeouts, or " externally-managed environment" warnings
Likely cause: Kali's apt-managed Python packages sometimes conflict with pip installations. Network issues in a NAT-isolated lab can also block PyPI (the Python Package Index, the default repository for Python libraries). The "externally-managed" error is newer systems refusing to let pip pollute the system Python.
Diagnostic:
pip3 --version
# Check if you're in a venv first:
echo $VIRTUAL_ENV
# Test connectivity to PyPI:
curl -I https://pypi.org/simple/requests/
# Check Kali's package management logs for conflicts:
sudo tail -n 20 /var/log/apt/term.log
Fix: Inside a virtual environment, pip should work freely. If you must install system-wide on Kali, prefer sudo apt install python3-<package> when Kali provides it. For pure pip installs, use the --user flag or a venv. When SSL errors appear in isolated labs, the cause is usually a captive portal or proxy you forgot about, not a real certificate attack. Verify your VM's network mode first (see next block).
Symptom: Kali VM can reach the internet but cannot ping 192.0.2.20 or 192.0.2.30; or conversely, it reaches lab targets but leaks to the external network
Likely cause: VirtualBox network adapter misconfiguration. The four modes that matter for Lab Wintermute are NAT (Kali reaches out through the host), Host-Only (VMs talk to each other and the host only), Internal Network (VMs talk to each other, host is excluded), and Bridged (VM appears as a physical device on your real network—dangerous for pentesting labs).
Diagnostic:
# On Kali:
ip addr show
ip route show
ping -c 3 192.0.2.20
# On the host, check VirtualBox network settings:
# VM Settings → Network → Adapter 1/2/3 → Attached to:
Fix: For Lab Wintermute's air-gapped requirement, use Internal Network mode for the lab subnet. Kali needs a second adapter in NAT mode if you want package downloads during setup, but disable or firewall that adapter before running any scans. I configure Kali with Adapter 1: NAT (for updates), Adapter 2: Internal Network "wintermute-lab" (for targets). Then I verify isolation:
# Verify no route to real network:
ip route | grep default
# Should show only the NAT adapter's gateway, not a route through Internal
# illustrative output — verify on your target:
# default via 10.0.2.2 dev eth0
Why this matters: Bridged mode once caused a student's
nmapscan to hit their roommate's printer. The scan was harmless, but the roommate's IT department was not amused. Internal Network mode is your legal safety net.
If targets are unreachable despite correct mode, check for IP conflicts. VirtualBox's Internal Network does not run DHCP by default. Assign static IPs as established in the build phase, or run a small DHCP server on Kali if you must.
Symptom: Metasploitable2 services appear down; nmap shows no open ports on 192.0.2.20
Likely cause: The target VM has drifted from its static IP, services failed to start on boot, or the VM was restored to a corrupted snapshot.
Diagnostic:
# From Kali:
nmap -sn 192.0.2.0/24
# Check if Metasploitable2 is alive at all:
arp -a | grep 192.0.2.20
# On Metasploitable2 console (VirtualBox → Show):
ifconfig
# Check service status for common ports:
netstat -tlnp | grep -E '21|22|23|25|80|445|3306'
Fix: If the IP has changed, the DHCP lease expired or the static configuration was lost. Reset it in /etc/network/interfaces or via the console. If services are down, start them manually—Metasploitable2's old init system can be finicky:
# On Metasploitable2 (illustrative — verify service names):
sudo service ssh start
sudo service vsftpd start
sudo service apache2 start
Earned insight: Metasploitable2's MySQL sometimes fails to start if the virtual disk is full from logs. The
df -hcommand on the target will show this. I keep a pristine snapshot labeled "CLEAN_POST_BOOT" and never overwrite it. If a service won't start and disk space is fine, I restore that snapshot rather than debug init scripts for an hour. The learning goal is understanding vulnerabilities, not nursing along a decade-old Ubuntu installation.
Symptom: DVWA at 192.0.2.30 shows "Database connection failed" or setup.php reports missing configuration
Likely cause: DVWA's config/config.inc.php points to the wrong database host, credentials, or the MySQL/MariaDB service is not running. XAMPP or LAMP stack components may have failed to start.
Diagnostic:
# On DVWA server:
sudo systemctl status mysql || sudo service mysql status
# Check DVWA configuration:
grep -E 'db_(user|password|server)' /var/www/html/dvwa/config/config.inc.php
# Test database connectivity directly:
mysql -u dvwa -p -h 127.0.0.1 -e "SHOW DATABASES;"
Fix: The default DVWA configuration often uses root with empty password for localhost. In a lab environment, you may have changed this during hardening exercises. If credentials were modified and forgotten, check your lab notes or reset via the MySQL root console. Ensure the database exists and the DVWA user has privileges:
-- illustrative SQL — verify on your target
CREATE DATABASE IF NOT EXISTS dvwa;
GRANT ALL ON dvwa.* TO 'dvwa'@'localhost' IDENTIFIED BY 'your_lab_password';
FLUSH PRIVILEGES;
If XAMPP is the platform, sudo /opt/lampp/lampp start may be required. Check /var/log/apache2/error.log and MySQL's error log for specifics.
Symptom: Python script crashes with OSError or socket exceptions; connection refused, timeout, or "name or service not known"
Likely cause: Network programming in Python exposes every layer of failure. The socket module (Python's interface to the Berkeley sockets API for network communication) raises exceptions that subclass OSError. Common triggers: target service not running (connection refused), firewall or network partition (timeout), DNS or IP typo (unresolved).
Diagnostic:
# Test connectivity outside Python first:
nc -vz 192.0.2.20 21
# Check DNS resolution if using hostnames:
getent hosts metasploitable2.wintermute.lab
# In Python, catch and inspect the exception:
python3 -c "
import socket, errno
try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5) s.connect(('192.0.2.20', 9999))
except OSError as e: print(f'Error number: {e.errno}') print(f'Error name: {errno.errorcode.get(e.errno, "UNKNOWN")}') print(f'Message: {e.strerror}')
"
Fix: Match the errno value to the actual problem. Python's errno module borrows names from Linux system headers. errno.EPERM (mapped to PermissionError) suggests a firewall rule or capability issue. A refused connection means the port is closed or the service is down—verify with nmap. Timeouts suggest routing problems or the target is overwhelmed.
Why this matters: Students often catch generic
Exceptionand print "it failed." Theerrnoattribute tells you whether to check the target, the network, or your own permissions.os.strerror()translates the numeric code to a human message. The complete list of defined symbols is available viaerrno.errorcode.keys()for reference.
For encoding issues when receiving network data: Python 3 distinguishes bytes (raw binary) from str (Unicode text). Socket recv() returns bytes. Decode explicitly with .decode('utf-8', errors='replace') rather than assuming the peer's encoding.
Symptom: Temptation to scan "just one" external host, or scope document is vague about 192.0.2.0/24 boundaries
Likely cause: Lab fatigue, curiosity, or poorly written authorization. Scope creep (expanding the test beyond agreed boundaries) is the most common ethical failure in junior testers.
Diagnostic: Pause. Re-read the written scope agreement. Lab Wintermute's authorization covers 192.0.2.10 (Kali), 192.0.2.20 (Metasploitable2), and 192.0.2.30 (DVWA) only. Any packet leaving the Internal Network adapter toward external addresses is unauthorized.
Fix: Verify isolation before every session:
# Confirm no default route through lab interface:
ip route show | grep 192.0.2
# Should show only local subnet routes, not a default gateway
# Check active connections during scans:
ss -tuln | grep -v 127.0.0.1 | grep -v 192.0.2
# Any non-lab IP here is a red flag
⚠️ Authorized, defensive use only.
Document your scope in the lab report. If you accidentally contact an external address—perhaps because VirtualBox Bridged mode was left enabled—stop immediately, preserve logs, and disclose to your instructor. Responsible disclosure applies even to mistakes.
Symptom: Lab is "weird" after multiple exploitation exercises; behavior doesn't match clean state; possible persistent compromise of the target
Likely cause: You forgot to restore from snapshot. Metasploitable2 and DVWA accumulate changes: created users, modified files, crashed services, installed backdoors from successful exploit demonstrations. The "learning report" phase requires a known-good baseline.
Diagnostic:
# On target VMs, check for unexpected changes:
find /tmp -type f -mtime -1
last | head
# Compare against your initial reconnaissance notes from Page 5
Fix: The decision tree is simple. If you have a clean snapshot and need to continue exercises: restore it. If you have reached the report phase and need to document "post-exploitation artifacts" for learning: preserve logs first, then restore. If the snapshot was corrupted or overwritten: rebuild the VM from base image.
Pre-session checklist (print and tick):
- [ ] VirtualBox network adapters verified: NAT for updates, Internal for lab
- [ ] Kali
ip addrshows expected 192.0.2.10 - [ ] Targets respond to ping at documented IPs
- [ ] Snapshot restored to "CLEAN_POST_BOOT" or equivalent
- [ ] Scope document open and reviewed
- [ ] Host firewall blocking unintended VM traffic (if applicable)
Post-session recovery:
# On Kali, preserve session logs before snapshot restore:
mkdir -p ~/wintermute-reports/session-$(date +%Y%m%d)
cp -r ~/.bash_history ~/wintermute-reports/session-$(date +%Y%m%d)/
cp /var/log/syslog ~/wintermute-reports/session-$(date +%Y%m%d)/ 2>/dev/null
# Then restore target VMs via VirtualBox snapshot manager
Earned insight: I once spent three hours debugging "strange" Metasploitable2 behavior that turned out to be my own successful vsftpd exploit from the previous week, still resident. The snapshot I thought I'd restored was actually a post-exploitation snapshot I'd mislabeled. Now I use timestamps and "CLEAN_" prefixes religiously. A snapshot naming convention is operational security for your own memory.
When logs are preserved, analyze them rather than delete them. Kali's logs in /var/log/syslog, /var/log/auth.log, and /var/log/kern.log show what actually happened. For tool-specific failures, consult man <tool> or check /usr/share/doc/<package> for documentation. If the tool is third-party, report issues to its GitHub or GitLab rather than to Kali or Debian maintainers—this channels your effort correctly and builds professional relationships.
In plain terms: Labs break because they are complex systems with many moving parts. The fix is almost never magic—it is methodical verification of network, service, code, and scope, in that order. Document everything, snapshot religiously, and when in doubt, restore clean.