My Very Own CA, How Sweet :)

Just splurging this down here for next time, as I had to go trawling for this info in various old text files today… Very related to https://blog.oholics.net/creating-simple-ssl-certificates-for-server-authentication-using-openssl/, but using my own CA rather than an enterprise or public CA.

I was working in my lab today to setup SLDAP on my lab domain controller. I was doing this to validate the syntax of ldapsearcher, on a Ubuntu machine, in different cases and also to see if I could determine the reason I was seeing a particular error (see https://blog.oholics.net/ldapsearch-syntax-for-simple-ldap-and-sldap/).

I want to KISS (keep things simple stupid), so was going to use the rootCA that I setup with OpenSSL a few years ago (running on my Windows machine).

Back then I ran the following commands to create the “top level” Root CA certificate and Private Key:

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

To generate my domain controller certificate today, I used (with an edited openssl.conf file of course):

openssl genrsa -out dc.oholics.net.key 2048
openssl req -new -key dc.oholics.net.key -out dc.oholics.net.csr
openssl x509 -req -in dc.oholics.net.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out dc.oholics.net.crt -days 500 -sha256
openssl pkcs12 -export -out dc.oholics.net.pfx -inkey dc.oholics.net.key -in dc.oholics.net.crt -certfile dc.oholics.net.crt

On the domain controller, I installed the dc.oholics.net.pfx file into the computer personal store and the rootCA.pem into the computer trusted root certification authorities store. Reboot and done..

###################################################################################

Minor edit…. I originally created the root certificate a rather long time ago… Today I discovered it was expired, thus the few certificates issued by it are also fubared.

Simple fix (where I don’t have to publish anything very far or wide):

Regenerate the rootCA certificate using the original key:

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 10240 -out rootCA.pem

Then start re-issuing those certificates that I was actually using (again using the keys and csr’s previously used:

openssl x509 -req -in MyImportantCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out MyImportantCert.crt -days 5000 -sha256

openssl pkcs12 -export -out MyImportantCert.pfx -inkey MyImportantCert.key -in MyImportantCert.crt -certfile MyImportantCert.crt

Note the extra 0’s on the number of valid days, shouldn’t have to do this again for a good while 🙂

Ldapsearch Syntax for Simple LDAP and SLDAP

Another case of “I’ve done this before, but never wrote it down”, so revisiting this took far longer than it should have. But now it is here, that won’t happen again.. right?? I’ll probably never need it again now… typical..

OK, so a straight forward non-secure ldapsearch command, obtains everything (-h can be IP or FQDN):

ldapsearch -h 192.168.1.201 -p 389 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”

A secure ldapsearch command, using TLS on port 389, obtains everything (Note the use of the -Z switch and the use of FQDN):

ldapsearch -h dc.oholics.net -p 389 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>” -Z

A secure ldapsearch command, using SSL on port 636, obtains everything (note the use of -H and the LDAP Uniform Resource Identifier):

ldapsearch -H ldaps://dc.oholics.net:636 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”

These commands all work just fine. Just for fun, make the last query type find something in particular – Look for a user account by its DN:

ldapsearch -H ldaps://dc.oholics.net:636 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>” “(&(objectclass=User)(distinguishedName=CN=John E Smoke,OU=Users,DC=oholics,DC=net))”

Now for some errors!

For both TLS and SSL on port 636, using the IP as the host (-h or -H) fails. It MUST use the FQDN of the target system. Why? because the certificate on the DC only refers to the FQDN of the server.

SSL/ 636 – The error “Can’t contact LDAP server (-1)” was really stumping me as there is little to go on in the error message. Doing a network capture, just shows the handshake start, but the DC ultimately just says “Go Away!”. It resets the connection attempt.

A few things learnt:

1. Using -h FQDN and -p 636 results in Can’t contact LDAP server (-1) (the URI method above must be used)

ldapsearch -h dc.oholics.net -p 636 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”
ldap_sasl_bind(SIMPLE): Can’t contact LDAP server (-1)

2. Using -h IP Address and -p 636 results in Can’t contact LDAP server (-1)

ldapsearch -h 192.168.1.201 -p 636 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”
ldap_sasl_bind(SIMPLE): Can’t contact LDAP server (-1)

3. Using -H with IP Address in URI and -p 636 results in Can’t contact LDAP server (-1)

ldapsearch -H ldaps://192.168.1.201:636 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”
ldap_sasl_bind(SIMPLE): Can’t contact LDAP server (-1)

Additionally, for TLS connection. Using the IP address of the DC, resulted in a different, but much more helpful error message:

ldapsearch -h 192.168.1.201 -p 389 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>” -Z
ldap_start_tls: Connect error (-11)
additional info: TLS: hostname does not match CN in peer certificate

Also, where a Domain Controller has the setting “Domain controller: LDAP server signing requirements” set to Require signing. When trying to initiate an insecure LDAP query with ldapsearch, it fails as follows:

ldapsearch -h 192.168.1.201 -p 389 -b “DC=oholics,DC=net” -D “CN=svc-LDAPBind,OU=ServiceAccounts,DC=oholics,DC=net” -w “<MyPass>”
ldap_bind: Strong(er) authentication required (8)
additional info: 00002028: LdapErr: DSID-0C090257, comment: The server requires binds to turn on integrity checking if SSL\TLS are not already active on the connection, data 0, v2580

Well that was a fun day 🙂

Backup and Clear Domain Controller Security Event Logs

A post related to https://blog.oholics.net/logparser-loves-security-logs/, for Case 3.

If you don’t manage security logs by regularly backing them up and clearing them, you risk losing important historical information. Additionally, running a LogParser query against a large, unmanaged security event log takes a long time.

The below script is designed to be run daily at the end of the day to backup the security event log on a Domain Controller and then clear its contents. Additionally, the logs are archived off to two windows shares to allow for long term storage.

The script makes use of Jaap Brasser’s DeleteOld script (https://gallery.technet.microsoft.com/scriptcenter/Delete-files-older-than-x-13b29c09) to carry out tidy up operations of the local staging folder. In practice, I used the same script to manage the archive folders too, keeping 365 days worth of logs.

Usage: .\BACKUP_AND_CLEAR_EVENTLOGS.ps1 <DomainController> $clear

Make sure that the security event log maximum size is increased to a high enough level to ensure that none of the days logs get overwritten. Judging that size will depend on the number of events per day or alternatively just set to “do not overwrite events”.

Note: the event ID’s are purely made up 😉

Enumerate Azure Role Assignments

The following script can be used to enumerate role assignments for a subscription and role assignments for Resource Groups within that subscription.

Use as-is to just grab everything – note 2 subscriptions are used in the example – fix the subscription GUID’s on lines 6 & 7.

Optionally un-comment the references to -SignInName “Jon@oholics.onmicrosoft.com” to obtain a report showing only those resources that refer to the named user.

The resulting report can be opened in Excel, to product a nice table 😉

LogParser Loves Security Logs

Just digging something up that I used to use regularly to look for logon events related to a certain username (samAccountName). Thought I’d regurgitate them here for “the next time..”

Three different SQL queries for three different use cases:

Case 1. I know that the logon event that I’m looking for occurred on DC01.oholics.net, I’m therefore going to interrogate the live DC log. The primary username I’m looking for is “jon”, a secondary name shown as “dave”. This could be replaced by a junk string if I’m only really looking for “jon”, or just trim the query (up to you.. ).

Case 2. In my domain, there are three domain controllers, I’m not sure where the logon events happened, so as in Case 1 I search the live DC logs, but this time searching all DC’s logs.

Case 3. I have three months of backed up logs to search through (in C:\TEMP\Logs) for all logon events for samAccount name “jon” (and optionally “dave”, as above). I may splurge out the script that I used to use to backup and clear the event logs next, that could be useful again – I’ve got to clean it first.

Usage: logparser -i:EVT file:<SQLFileName>.sql -o:CSV -resolveSIDs:ON 

Where:

  • The above SQL query is saved as LogParserRedaction.sql in the same location as the LogParser binary.
  • The collection of logs to be redacted are in C:\TEMP\Logs\
  • The output file will be written to C:\TEMP\Output\output.csv

Redacting sensitive content from Windows event logs using LogParser

Consider the scenario: opening a ticket within Azure for an issue with an infrastructure component or security event. IP addresses, domain names and Machine names are classed as sensitive and should not be revealed to MS support staff.

You have a folder filled with event logs from the problem machine(s). You need to redact the above mentioned properties.

By using LogParser with the following sql statement, a CSV file is exported which strips out the sensitive properties, replacing parts of the properties with X’s.

  • OHOLICS‘ is replaced by XXXXXXX‘ where it is found in an event log, in the Strings, ComputerName, Message or Data fields
  • The first two octets of an IP address are stripped, where these are ‘192.168.’ in the Strings, Message or Data fields
  • blog.oholics.net‘ is replaced by blog.XXXXXXX.net‘ where it is found in an event log, in the Strings, ComputerName, Message or Data fields

Note that after the output file is created, the header row will need to be updated to remove the replace statements. Where normally just the item name would be added as a header, the full replace query is added as the header for those items.

Usage: logparser -i:EVT file:LogParserRedaction.sql -o:CSV -resolveSIDs:ON 

Where:

  • The above SQL query is saved as LogParserRedaction.sql in the same location as the LogParser binary.
  • The collection of logs to be redacted are in C:\TEMP\Logs\
  • The output file will be written to C:\TEMP\Output\OUTPUT.CSV

Azure Service Principle Authentication

I have recently been working within a client where all Azure/ Office 365 users must perform MFA on logon.

Ages ago I posted about using credential manager to automate Office 365 scripts: https://blog.oholics.net/using-credential-manager-to-authenticate-office-365-scripts/. This method will clearly not suffice where MFA is enforced, as there is no mechanism to allow MFA challenge and response.

Recently I have been looking into using Azure Service Principle objects to bypass MFA and to allow scripts, that need to connect to Azure or other services, to do so without input. Thus, I can then schedule scripted tasks to generate reports on Azure AD objects or AzureRM items.

Firstly I need to create some certificates, these will be used to authenticate, see here: https://blog.oholics.net/creating-simple-ssl-certificates-for-server-authentication-using-openssl/ for details on certificate creation.

Next, once we have the PFX certificate file, we can create the Azure App Registration, using PowerShell:

Then (optionally), if the script that you want to automate will be reading AzureRM objects, run the following script. Note that if the role assignment is to be constrained to a specific resource group, add the  -ResourceGroupName switch to New-AzureRMRoleAssignment

Additionally, the RoleDefinitionName can be altered to suit.

Now we have the Service Principle in place, we can connect! But in the case of the Azure AD connection, I need to first allow the application to Read AAD:

Go to the App Registrations blade in Azure AD, pick the application created earlier, then select settings. Select Required Permissions, add Azure AD and add the permissions shown in the following image:

Now lets connect using the certificate thumbprint:

By installing the certificate in the CurrentUser store, only that user can consume the certificate thumbprint for authentication using this method. Lovely.. 🙂

Why is this method secure?

  • You can only access the application to sign in if you have installed the certificate on the machine that you want to run the script from.
  • To install the certificate, you must know the password that was set on the private key during PFX creation.
  • No AAD user object is created
  • No plain text passwords need to be stored

To sign in using this method, you must know:

  1. The AAD Tenant GUID
  2. The Application GUID of the configured application
  3. The specific thumbprint of the certificate, used to make the connection
  4. The certificate and private key must be installed on the machine on which the connection attempt is being made.

Can’t rename, move or delete an OU

Today, I came across something that had me quite stumped…. well for a few minutes anyway 🙂

I was doing some tidying up of a domain, I found an OU that was incorrectly named, it was not to design. I thought, I’ll just rename it, but found that the option to do so was not available.

I took a look at the attributes of the OU, two immediately struck me as odd:

systemFlags was set to DISALLOW_DELETE|DOMAIN_DISALLOW_RENAME|DOMAIN_DISALLOW_MOVE

 

 

 

 

 

 

 

 

 

isCriticalSystemObject was set to TRUE:

 

 

 

 

 

 

 

 

 

Neither of these attributes could be modified, an error was thrown if attempted.

The simple answer: This OU had been set as the default location for new computer objects via redircmp 

By running redircmp CN=Computers,DC=oholics,DC=net (or your other true destination):

  • The systemFlags attribute was banished
  • The isCriticalSystemObject attribute was set to FALSE
  • The OU could be renamed, moved and deleted 😉