From Linux to AD

…or how to read the SAMBA machine account.

Occasionally, during a penetration test, when you gain access to a Linux box you will try to gather as much information as possible. In particular, if the compromised Linux box is running SAMBA and is joined to a Windows Domain, you may want find a way to extract the “machine account” (also known as Machine Trust Account) that is used to authenticate a client host to the Domain Controller. If we manage to find the password for the machine account, then we will have an unprivileged user that can be used to go a step further in the domain compromise. This user account will provide us with access to the Domain Controller via LDAP to obtain valuable information as well as to access, at least, the SYSVOL share (remember CVE-2014–1812?).

Old method (SAMBA 3)

According to SAMBA documentation (https://wiki.samba.org/index.php/Keytab_Extraction), you can obtain the plaintext password of the machine account by simple dumping the content of the “secrets.tdb” database.

Let’s use an old Linux box, joined to a test domain, to try it (hostname ubuntu13):

Dumping the content of secrets.tdb on Samba 3

Observe in the above image the dump of the secrets.tdb database with the following values:

SECRETS/SALTING_PRINCIPAL/DES/LAB.BRANSH.COM : The value (data) for this key gives us the host (ubuntu13) and domain (lab.bransh.com) names. Remember that the machine account name ends with a $ char, that is, ubuntu13$ in this case.

: The value (data) for this key gives us the host (ubuntu13) and domain (lab.bransh.com) names. Remember that the machine account name ends with a $ char, that is, ubuntu13$ in this case. SECRETS/MACHINE_PASSWORD/LAB: the value (data) for this key gives us the password for the machine account (ubuntu13$), in plaintext and with a length of 14 bytes (plus the null terminator), in this case “UeHjnbam_zdtr#”.

Let’s use the machine account to get a Kerberos TGT and then query the LDAP DB:

Using the machine account credentials to access LDAP.

In the above image we can observe the following:

A TGT is obtained from the KDC via kinit for the machine user ubuntu13 The tickets are listed via klist An LDAP query is performed using Kerberos authentication method via ldapsearch

Let’s use the machine account to access the domain default SYSVOL share:

Using the machine account credentials to access the SYSVOL share.

In the above image we can see the following:

A TGT is obtained for the ubuntu13 machine account via kinit The list of tickets is obtained via klist Access to the default domain share “SYSVOL” is obtained via smbclient using Kerberos authentication

Let’s use the machine account to access a unrestricted share on the network:

Using the machine account credentials to access a Windows share on the network.

In the above image we can see the following:

Access to a share in a domain member server is obtained with the Kerberos ticket for a machine account via smbclient The list of tickets is obtained via klist. Observe the TGT and the Tickets for CIFS protocol on the ad and mssql1 servers

New method (SAMBA 4)

With Samba 4 the story is a little different. When we check the secrets.tdb database in a new Samba server, we observe a very long hex value that is not ASCII:

secrets.tdb content on Samba 4

In order to understand how this key is stored in the secrets.tdb file, we need to inspect the Samba 4 source code. As the Samba code is really huge, we should find something to start.

As you probably know, the “net ads changetrustpw” command can be used to change the password of the machine account:

Changing the machine account password on a Linux box.

We can search on the files associated to “net ads” command (net_ads.c), or grep the string “Changing password for principal” and inspect the resulted source files. Either way, we will end up with the following source code:

The following image shows the source code of the net_ads_changetrustpw function.

net_ads_changetrustpw function code.

Observe that in the line 2378, the function ads_change_trust_account_password is called with two arguments ads and host_principal.

The following image shows the ads_change_trust_account_password function within the util.c source code file:

ads_change_trust_account_password function code.

Observe in the line 38 that the trust_pw_new_value function is called with three arguments. In particular, observe the parameter SEC_ADS. Let’s search for that function.

The following image shows the code of the function trust_pw_new_value. Observe that it calls the function generate_random_machine_password with the arguments min = 128 and max = 255:

trust_pw_new_value function code.

Observe the code comments that specifies the random buffer is converted to UTF8.

So, before continuing looking at the source code let’s try to decode it as if it were UTF8. Let’s copy the hex values obtained from secrets.tdb database:

Machine account password obtained from secrets.tdb.

And use the following Python script to generate an NTLM hash for this hex string:

# echo “\E7\9E\B7…\88\00” | python -c “import hashlib,binascii;print binascii.hexlify(hashlib.new(‘md4’,binascii.unhexlify(raw_input().replace(‘\\\’, ‘’).replace(‘00’,’’)).decode(‘utf-8’).encode(‘utf-16le’)).digest())”

The following image shows the NTLM hash generation for this machine account value:

Getting the NTLM hash for the machine account.

The obtained NTLM hash value is a879b96849623f7ab57bf35f8a3a658c. Let’s use it to access the SYSVOL and test if it works.

The following image shows the access to the SYSVOL default share with the calculated NTLM hash:

Using the NTLM hash for accessing the SYSVOL share.

Now, let’s use the NTLM hash to obtain a TGT from the KDC and perform LDAP queries via ldapsearch:

Using the machine account NTLM hash to access LDAP.

In the above image, it can be observed the use of a simple Python script that can be used to obtain a TGT from the DC. The code within get_tgt.py is very simple an entirely based on Impacket suite (thank you very much @agsolino for this). The rest of the commands should be meaningful if you have read from the beginning.

Python code for getting a TGT: