Tracking who deleted an Active Directory account

Someone just deleted a very important AD account. Your job, should you choose to accept it, is to track down the vile perpetrator and enact swift justice!  😉

Account name: user5.test

Location: domain1.com

Tools for the job: ldp.exe; repadmin; event-log

Note. This is done with 2008R2 DC servers, Forest functional level 2003 / Domain Functional level 2003.  If you run server 2003 the event log numbers will be most likely different.

Step 1. ldp.exe

Acquire the deleted object’s DN.

The object’s old DN is not valid anymore, what we need is the new tombstone DN (a bit special since it includes the GUID to insure uniqueness)

Start Ldp.exe

Connect > domain1.com

Bind > appropriate credentials

Before we do anything else we must enable ldp.exe to show the deleted objects:

Options > Controls > Load Predefined > Return deleted objects > OK

Should look like this:

ldp_view_del_items

View > Tree > BaseDN “DC=domain1, DC=com”

Navigate to “CN=Deleted Objects,DC=domain1, DC=com” and expand

Note. Replace domain1.com with your own naming convention of course 😉

Locate user5 test and grab the tombstone DN ( object’s old GUID + old DN)

Note. I will provide in a later article the details on managing and recovering tombstone objects, and further details about ldp.exe (amazing AD admin tool)

Step 2. repadmin

Find out on what DC the object was deleted.

repadmin /showobjmeta DCname “DN”

Note. Mare sure the DC used has replicated, and use the deleted object’s tombstone DN we grabbed in Step 1.

Aha! What do we see here? The isDeleted attribute originated on the DC2-2008 domain controller:

Note: More details on this function can be found here: https://sysadminemporium.wordpress.com/2012/11/22/hello-world/

Step 3. event-log

Accessing the DC2-2008’s event-log

Windows Logs > Security

What do we have here? A 4726 Account management Log!

A closer look:

Success! User5.test was deleted by Domain1\andrei.ursuleac

Ugh…that is my account…time to run!

Advertisements

PowerShell GUI for your scripts. Episode 2

DropDown Lists

Why enter the information manually into a text box when we can have a nice dropdown list with a selection ready?

To start off we need an array:

$wksList=@("hrcomputer1","hrcomputer2","hrcomputer3","workstation1","workstation2","computer5")

You can populate the array using an external file, an active directory query or even an SQL query. In this case we hand built an array for simplicity’s sake

Build the drop down list (we’ll replace the InputBox from the Episode 1 script):

$DropDownBox = New-Object System.Windows.Forms.ComboBox #creating the dropdown list
$DropDownBox.Location = New-Object System.Drawing.Size(20,50) #location of the drop down (px) in relation to the primary window's edges (length, height)
$DropDownBox.Size = New-Object System.Drawing.Size(180,20) #the size in px of the drop down box (length, height)
$DropDownBox.DropDownHeight = 200 #the height of the pop out selection box
$Form.Controls.Add($DropDownBox) #activating the drop box inside the primary window

Populate the drop down list from the array we built earlier:

foreach ($wks in $wksList) {
                      $DropDownBox.Items.Add($wks)
                              } #end foreach

Populate a variable (named $computer in this case) with the value you select in the dropdown:

$computer=$DropDownBox.SelectedItem.ToString()

—————————–

Let’s put it all together. We’ll take the GUI ping script from Episode 1 and replace the InputBox with a dropdown list

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

$Form = New-Object System.Windows.Forms.Form    
$Form.Size = New-Object System.Drawing.Size(600,400)  

############################################## Start functions

function procInfo {
$computer=$DropDownBox.SelectedItem.ToString() #populate the var with the value you selected
$pingResult=ping $computer | fl | out-string;
$outputBox.text=$pingResult
                     } #end procInfo

############################################## end functions

############################################## Start drop down boxes

$DropDownBox = New-Object System.Windows.Forms.ComboBox
$DropDownBox.Location = New-Object System.Drawing.Size(20,50) 
$DropDownBox.Size = New-Object System.Drawing.Size(180,20) 
$DropDownBox.DropDownHeight = 200 
$Form.Controls.Add($DropDownBox) 

$wksList=@("hrcomputer1","hrcomputer2","hrcomputer3","workstation1","workstation2","computer5")

foreach ($wks in $wksList) {
                      $DropDownBox.Items.Add($wks)
                              } #end foreach

############################################## end drop down boxes

############################################## Start text fields

$outputBox = New-Object System.Windows.Forms.TextBox 
$outputBox.Location = New-Object System.Drawing.Size(10,150) 
$outputBox.Size = New-Object System.Drawing.Size(565,200) 
$outputBox.MultiLine = $True 
$outputBox.ScrollBars = "Vertical" 
$Form.Controls.Add($outputBox) 

############################################## end text fields

############################################## Start buttons

$Button = New-Object System.Windows.Forms.Button 
$Button.Location = New-Object System.Drawing.Size(400,30) 
$Button.Size = New-Object System.Drawing.Size(110,80) 
$Button.Text = "Ping" 
$Button.Add_Click({procInfo}) 
$Form.Controls.Add($Button) 

############################################## end buttons

$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()

The end result should look like this:

Select a computer name and PING away 🙂

PS might want to add to the array an actual computer name from your network or localhost 😉

PowerShell GUI front end for your scripts. Episode 1

Why? To give your scripts a nice looking interface, to make it user-friendly (can make a script look like an application usable by the other  non IT departments, and last but not least make the script more comfortable to use.

How? Leveraging the power of the .net framework.

—————————————–

Background Window | Single/Multi line Input/Output Box | Action Button

And now let’s get to it:

Part A. Creating the background window:

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  #loading the necessary .net libraries (using void to suppress output)

$Form = New-Object System.Windows.Forms.Form    #creating the form (this will be the "primary" window)
$Form.Size = New-Object System.Drawing.Size(600,400)  #the size in px of the window length, height

$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()   #activating the form

Final result should look like this:

—————————————–

Part B. Adding some functional elements:

Creating a single line text box (we’ll use it for input):

$InputBox = New-Object System.Windows.Forms.TextBox #creating the text box
$InputBox.Location = New-Object System.Drawing.Size(20,50) #location of the text box (px) in relation to the primary window's edges (length, height)
$InputBox.Size = New-Object System.Drawing.Size(150,20) #the size in px of the text box (length, height)
$Form.Controls.Add($InputBox) #activating the text box inside the primary window

Creating a multi line text box (we’ll use it for output):

$outputBox = New-Object System.Windows.Forms.TextBox #creating the text box
$outputBox.Location = New-Object System.Drawing.Size(10,150) #location of the text box (px) in relation to the primary window's edges (length, height)
$outputBox.Size = New-Object System.Drawing.Size(565,200) #the size in px of the text box (length, height)
$outputBox.MultiLine = $True #declaring the text box as multi-line
$outputBox.ScrollBars = "Vertical" #adding scroll bars if required
$Form.Controls.Add($outputBox) #activating the text box inside the primary window

Since we have now two fields how about we add an element to generate an action. How about a Button?

$Button = New-Object System.Windows.Forms.Button #create the button
$Button.Location = New-Object System.Drawing.Size(400,30) #location of the button (px) in relation to the primary window's edges (length, height)
$Button.Size = New-Object System.Drawing.Size(110,80) #the size in px of the button (length, height)
$Button.Text = "Action" #labeling the button
$Button.Add_Click({}) #the action triggered by the button
$Form.Controls.Add($Button) #activating the button inside the primary window

All we need now is a script….how about ol’trusty ping :).We’ll make a function of course to keep things nice and proper

function pingInfo {
$wks=$InputBox.text; #we're taking the text from the input box into the variable $wks
$pingResult=ping $wks | fl | out-string;  #ping $wks
$outputBox.text=$pingResult #send the ping results to the output box
                     } #end pingInfo

Next we need to add the function name to the Button’s Add_Click() method

$Button.Add_Click({pingInfo})

—————————————–

Part C. Putting it all together

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

$Form = New-Object System.Windows.Forms.Form    
$Form.Size = New-Object System.Drawing.Size(600,400)  

############################################## Start functions

function pingInfo {
$wks=$InputBox.text;
$pingResult=ping $wks | fl | out-string;
$outputBox.text=$pingResult
                     } #end pingInfo

############################################## end functions

############################################## Start text fields

$InputBox = New-Object System.Windows.Forms.TextBox 
$InputBox.Location = New-Object System.Drawing.Size(20,50) 
$InputBox.Size = New-Object System.Drawing.Size(150,20) 
$Form.Controls.Add($InputBox) 

$outputBox = New-Object System.Windows.Forms.TextBox 
$outputBox.Location = New-Object System.Drawing.Size(10,150) 
$outputBox.Size = New-Object System.Drawing.Size(565,200) 
$outputBox.MultiLine = $True 
$outputBox.ScrollBars = "Vertical" 
$Form.Controls.Add($outputBox) 

############################################## end text fields

############################################## Start buttons

$Button = New-Object System.Windows.Forms.Button 
$Button.Location = New-Object System.Drawing.Size(400,30) 
$Button.Size = New-Object System.Drawing.Size(110,80) 
$Button.Text = "Ping" 
$Button.Add_Click({pingInfo}) 
$Form.Controls.Add($Button) 

############################################## end buttons

$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()

Final result should look like this:

Voilà! Your first script with a graphical interface.

But wait we’re not done. Let’s add some optional BLING! 🙂

—————————————–

Part D. Optional decoration elements

Main window

$Form.StartPosition = "CenterScreen" #loads the window in the center of the screen
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow #modifies the window border
$Form.Text = "Ping GUI tool" #window description

Modifying the textbox font:

$outputBox.Font = New-Object System.Drawing.Font("Verdana",8,[System.Drawing.FontStyle]::Italic)

Changing the button cursor onHover to a hand icon, the background color to green and a larger font

$Button.Cursor = [System.Windows.Forms.Cursors]::Hand
$Button.BackColor = [System.Drawing.Color]::LightGreen
$Button.Font = New-Object System.Drawing.Font("Verdana",14,[System.Drawing.FontStyle]::Bold)

Those are just some of the customizations possible, integrate some of them in the GUI objects we created, see what they do…change them at will…have FUN!

Finding disabled user accounts

To start off, if you’ve never used LDAP filters it would be very useful to familiarize yourself with them (they are very handy in any Directory implementations of significant size). Some resources on LDAP filters can be found here:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa746475%28v=vs.85%29.aspx

and for a comprehensive coverage of the subject:

http://www.ldapexplorer.com/en/manual/109010000-ldap-filter-syntax.htm

————————————————————————–

And now to the actual LDAP filter we’ll be using:

(&(objectCategory=Person)(UserAccountControl:1.2.840.113556.1.4.803:=2))

Breaking it up

(&(...)(...))

the & (AND) operator so both statements must be true

objectCategory=Person

will return only user accounts

UserAccountControl:1.2.840.113556.1.4.803:=2

Will return only accounts that have the UserAccountControl attribute flagged as disabled.

The weird number we see here (1.2.840.113556.1.4.803) represents the LDAP_MATCHING_RULE_BIT_AND rule

Info regarding bitwise filter: http://support.microsoft.com/kb/269181

2 is the decimal value for ACCOUNTDISABLE

Note:  Pay special attention to this attribute (UserAccountControl) and its flags: http://support.microsoft.com/kb/305144

————————————————————————–

And now to put the filter to work:

Option 1 using the dsquery command

dsquery * -filter "(&(objectCategory=Person)(UserAccountControl:1.2.840.113556.1.4.803:=2))" -limit 3

Note: Use the limit switch to control the number of objects returned. For unlimited use 0

Option 2 PowerShell

Import-Module ActiveDirectory
Get-ADObject -LDAPFilter {(&(objectCategory=Person)(UserAccountControl:1.2.840.113556.1.4.803:=2))}

Option 3 PowerShell using a .net object (useful in old domains where you cannot use the PowerShell AD module)

$ADSearch = New-Object DirectoryServices.DirectorySearcher
$ADSearch.Filter = '(&(objectCategory=Person)(UserAccountControl:1.2.840.113556.1.4.803:=2))'
$ADSearch.SearchRoot = 'LDAP://DC=Contoso,DC=com'
$ADSearch.PageSize=1000
$ADSearch.FindAll()

Option 4 use ldp.exe

A tutorial on the usage of ldp.exe will be provided in another post.

Getting an object’s replication metadata

Why? How?

Why? You can find out when an object’s attribute was changed and what domain controller made that change. Now if you audit the DC’s logs you could also track who made those changes.

Here is a very interesting case study from Microsoft documenting such an issue:

http://blogs.technet.com/b/askpfeplat/archive/2012/03/05/how-to-track-the-who-what-when-and-where-of-active-directory-attribute-changes-part-i-the-case-of-the-mysteriously-modified-upn.aspx

How? Repadmin command using the following syntax:

 repadmin /showobjmeta DCname “Object’s DN”

e.g.

repadmin /showobjmeta ContosoDC01 "CN=Doe, John,OU=User Accounts, DC=Contoso,DC=com"

Note: The command in the example runs only on one DC. If replication has not occurred yet the information might be outdated. If in doubt run the command against multiple DCs and check the attribute version.

Here is an action shot from my home lab:

We can see that the description attribute change for user2.test was replicated from the DC2-2008 controller on 2012-11-19 23:08.

(yes I know domain1.com is the most original domain name ever 🙂 )

p.s. A quick PowerShell script to make the command more user friendly (fills in the object’s DN):

$SAMAcc=Read-Host;
$objDN=dsquery * -filter "(SAMAccountName=$SAMAcc)";  
repadmin /showobjmeta ContosoDC01 $objDN;