Tuesday, January 12, 2021

Finding the local computer's machine account SID

The Get-ADComputer cmdlet, I hear, can get the SID of a computer's Active Directory machine account. But because it contacts Active Directory to look up the account, it won't work if not connected to the domain. If you have administrative privileges on the computer, you can elevate to SYSTEM (e.g. with PsExec) and get the computer's machine account SID from the default value in this Registry key:

HKLM\SECURITY\Policy\PolMachineAccountS

It's in binary SID format and so may be tricky to read by eye. If you just need the RID, that's the last four bytes, or you can get it as the only contents of the sister key PolMachineAccountR.

Wednesday, January 6, 2021

Logging into a domain account without connection to a real domain controller

Windows domain member computers cache some domain users' password hashes so that recent users of the machine can still log in even when the computer can't reach a domain controller to check the password against Active Directory. Of course, if the user was not one of the most recent logins or has never logged into the machine to get their credentials cached, they cannot log in without contacting a domain controller since Windows cannot verify their password or even their existence. It's pretty easy to get into a machine by using the cmd.exe-as-sethc.exe trick and creating a new local user at the login screen, but if you want to avoid offline writes to the disk for some reason, want the new profile to be associated with a domain user once reconnected to the domain, or just want to have a fun adventure (for some definition of "fun"), it's possible to set up a fake domain controller. To avoid bizarre behavior after reconnection to the real domain, you will need to know a domain user's username, NetBIOS domain name, and SID. Conveniently, all of this can be obtained on one line from whoami /user when logged into a domain-joined computer that presumably has a DC connection or cached credentials.

Of course, it would be very bad if just anyone could set up a fake DC and distribute arbitrary policy changes to execute arbitrary code without touching the machine. To prevent this, every domain-joined computer has a "machine account" in Active Directory and keeps the password to it in an LSA secret locally. The computer uses that password to log onto and verify the domain. If machine logon fails, e.g. due to a machine account password mismatch, user logon fails with "the trust relationship between this workstation and the primary domain failed." 

So we'll need to extract the machine password. Boot the target computer from a flash drive (Windows PE or portable Linux, doesn't matter) and copy out SYSTEM and SECURITY from C:\Windows\System32\config. On a computer you already control, start Mimikatz in interactive mode. Elevation is not necessary for the program itself, but it probably will be to tell your antivirus to stop complaining about the tool. Run this command to dump LSA secrets:

lsadump::secrets /system:C:\copied\SYSTEM /security:C:\copied\SECURITY

Note the $MACHINE.ACC secret's NTLM hash. The DC doesn't need the password itself.

We will also need to know the domain's DNS/realm name and the target computer's name. If those are not known, they can be obtained from the exfiltrated SYSTEM hive, which can be opened in the Registry Editor with File | Load Hive when HKEY_LOCAL_MACHINE is selected. The domain DNS name can be found in the ControlSet001\Services\Tcpip\Parameters key; the computer name is in ControlSet001\Control\ComputerName\ComputerName.

With that information, we can set up a Samba Active Directory domain controller. I tested this on a standard Ubuntu 20.04 distribution in a VM based on this wiki page. First install the ifupdown package since we're going to remove the other networking components that might get in the way. (On the first try I only thought to install that after I ripped out the other network tools. Oops.) Disable the systemd-resolved service, then uninstall/purge the network-manager package. Remove the /etc/resolv.conf symlink and replace it with a new file specifying a public nameserver like 8.8.8.8. Rewrite /etc/network/interfaces to specify a static IP. Bring the network interface online with ifup eth0 and make sure the IP is correct with ip addr. Adjust /etc/hosts to resolve both the domain's fully-qualified DNS name (e.g. example.com) and the server's FQDN (e.g. mirage.example.com) to that static IP.

Now you can install the necessary packages for a DC. One of the Kerberos-related packages will prompt for realm information during setup - I think that configuration is going to get rewritten by Samba later, but just to be safe, give it the domain FQDN as the realm and the server's name as the server list. Delete the Samba configuration, Samba state, and Kerberos configuration as directed in "Preparing the Installation".

The domain SID is just the domain user's SID without the last component (the RID). Make sure to specify that when provisioning the domain:

sudo samba-tool domain provision --domain=EXAMPLE --domain-sid=S-1-5-X-Y-Z --realm=EXAMPLE.COM --adminpass=SecurePassword!

Copy Samba's desired Kerberos configuration into place:

sudo cp /var/lib/samba/private/krb5.conf /etc/krb5.conf

During the provisioning, Winbind was set up as the DNS server and set its forwarding server to the public server specified previously in resolv.conf. Now that Winbind handles name resolution, update /etc/resolv.conf to use the local server itself. Unmask, enable, and start the samba-ad-dc service. The domain controller should now be running. Test the three aspects of it.

Now things should move quickly. Create a machine account for the target computer:

sudo pdbedit -a -u TARGET$ -m

Set its NTLM password hash:

sudo pdbedit -r -u TARGET$ --set-nt-hash LONGHASH

Disable machine account password changes so the target doesn't change its password with the fake domain and render itself unable to connect to the real one:

sudo pdbedit -P "refuse machine password change" -C 1

Optionally disable user password complexity requirements:

sudo samba-tool domain passwordsettings set --complexity=off

Then create the user account you want to log in with (you will be prompted for a password):

sudo pdbedit -a -u person

At this point you can add the user to any other groups you want, perhaps Domain Administrators.

Despite pdbedit's documentation, Samba does not support setting account SIDs. We will therefore need to directly-ish edit the Samba state, specifically the LDB file in /var/lib/samba/private/sam.ldb.d/ corresponding to the main naming context for the domain. Install the ldb-tools package, stop the samba-ad-dc service, then invoke ldbedit to bring up a text editor on the user account object:

sudo ldbedit -e nano -H /var/lib/samba/private/sam.ldb.d/DC=EXAMPLE,DC=COM.ldb '(samaccountname=person)'

Find the objectSid line, change the last component (the RID) to match the real user's SID, save, and exit. The changes are immediately applied and you can start the Samba service again.

To make the target computer see the domain, you will need to adjust your DHCP server (probably your LAN's router) to specify the fake DC as the first DNS server. Power the target machine on and you should be able to log in with the credentials you decided for the user account! Once that one login works, the credentials will be cached, so the fake DC is not needed anymore. You can turn off the server and put the DHCP server's DNS settings back how they were.