SADPhishes.ps1 Search & Destroy Phishing Emails

I built a new tool – SADPhishes.ps1.  It’s a Powershell script to Search & Destroy emails across all Exchange mailboxes that you’ve identified as Phishing.

The script is available here:

Below are some screenshots showing basic usage.  I’d love some feedback if you use it, improve it, or even if you think it sucks!


SADPhishes.ps1 Search & Destroy Phishing Emails

AES encrypted credentials for Powershell scripts (and a bit about DPAPI)

I recently went through the process of learning about how you could encrypt and store passwords for user accounts in a way that they can be easily used in a Powershell script.  While researching how to do this, I found a lot of information on how to encrypt the password using Microsoft’s Data Protection Application Programming Interface (DPAPI) encryption.  However, I didn’t find very many write-ups on how to do the same thing using AES encryption.  So I’m going to share what I found and how it can be implemented.

AES? DPAPI? Why might I choose one over the other?

Before getting to the details for implementation, I want to mention some reasons why you may (or may not) want to use AES instead of DPAPI.  Both DPAPI and AES have native support in Powershell, so they can both be used relatively easily.

Data that is encrypted using DPAPI can only be decrypted on the same Host that encrypted the data (and it might even need to be the same user profile…I read a couple of things that mentioned that, but haven’t dug deeper to find out if that’s accurate or tested it).  That means that the data that’s encrypted using DPAPI isn’t portable – it can’t be decrypted on other computers (excluding potential attacks against the encryption).  Some people might find DPAPI to be appealing because its lack of portability keeps the data more secure from a Confidentiality perspective.

Data that is encrypted using AES, on the other hand, can be decrypted by any computer that has the AES key that was used to encrypt the data.  That means that the data that’s encrypted using AES is portable.  If the data needs to be migrated to another host for any reason (normal systems migrations, disaster recovery, etc), it will continue to be accessible as long as you have access to the AES key that was used.  Some people might find the portability of AES to be appealing because it keeps the encrypted data more secure from an Availability perspective.

Ultimately, whether you choose to use DPAPI or AES is a decision that each person or organization needs to make, and you need to weigh the pros and cons of each option.  In either case, if any variety of encryption is implemented without an appropriate amount of consideration, it can result in bad situations like an inability to access data that you need (effectively, a self inflicted Denial of Service attack) or the data not being as confidential as you thought because of poor access controls on the keys.

Now that we’ve covered that part, let’s move on to how you can use Powershell to (1) generate and store a 256-bit AES key, (2) encrypt the password for a User Account using that AES key, and (3) use that AES encrypted password in a script (to authenticate with a mail server, in this case).

Preparing your environment to use AES encrypted passwords

Use the Powershell below to get your environment prepared.  Before executing these steps, you will need to have: (1) a secure location to store your key, (2) a secure location to store your encrypted password, (3) the password for the User account that you need to use in your script.

#Prep Step 1 - Use Powershell to Generate a 256-Bit Key and store it in a given path
#            - For this step, you need to identify a secure location to store your Key.  It is critical that you limit access to this location using ACLs.
$KeyStoragePath = "c:\YourKeyStorage   OR   \\FileServer01\KeyStorageShare"
$KeyFileName = "Username@YourDomainDotCom.AES.Key"
$CreateKey = New-Object Byte[] 32
$CreateKey | out-file "$KeyStoragePath\$KeyFileName"

#Prep Step 2 - Capture the password for your User Account as a Secure String, AES Encrypt it using the Key generated in Step 1, and save it to a file.
#            - For this step, you need to identify a secure location to store your encrypted Password.  Although this password is encrypted, you should still limit access to this location using ACLs.
#            - Complete this step immediately after Prep Step 1.  It relies on variables that were defined in Prep Step 1.
#            - After the 4th line below is executed, you will need to type the password for your user account (Username@YourDomainDotCom) at Powershell's Read-Host prompt.
$GetKey = Get-Content "$KeyStoragePath\$KeyFileName"
$CredentialsStoragePath = "C:\YourEncryptedCredentialsStorage   OR   \\FileServer02\CredentialsStorageShare"
$CredentialsFileName = "Username@YourDomainDotCom.securestring"
$PasswordSecureString = Read-Host -AsSecureString
$PasswordSecureString | ConvertFrom-SecureString -key $GetKey | Out-File -FilePath "$CredentialsStoragePath\$CredentialsFileName"


The screenshot below shows Prep Step 1.  A 256-Bit (32-Byte) key is generated using a .NET Random Number Generator.  For demonstration purposes, the contents of the key file is displayed.

Prep Step 1

The screenshot below shows Prep Step 2.  The password for “Username@YourDomainDotCom” is typed into the Powershell “Read-Host -AsSecureString” prompt, and then the password is encrypted and saved to a file.  For demonstration purposes, the content of the encrypted password file is displayed.

Prep Step 2

After you complete those two Prep Steps, you will have your Key and Encrypted Password saved to files, and you will be able to use them when you execute other Powershell scripts in the future.

Using your AES Encrypted password in a script

The script below demonstrates how you can use your AES Encrypted password in a script.  In this example, the password is being used to authenticate with a Mail Server.

#Use your AES Encrypted password file to authenticate with a Mail Server. Define mail server and user, decrypt the encrypted Credentials file, using the Key File, and load it into PSCredential so it can be passed to Send-MailMessage, compose email, and send.
#Define Mail Server Details
$PSEmailServer = "Mail.YourDomainDotCom"
$SMTPPort = 587
$SMTPUsername = "Username"
#Define Key File Details
$KeyStoragePath = "c:\YourKeyStorage\"
$KeyFileName = "Username@YourDomainDotCom.AES.Key"
$GetKey = Get-Content "$KeyStoragePath\$KeyFileName"
#Define Encrypted Password File Detaisl
$CredentialsStoragePath = "C:\YourEncryptedCredentialsStorage"
$CredentialsFileName = "Username@YourDomainDotCom.securestring"
$EncryptedPasswordFile = "$CredentialsStoragePath\$CredentialsFileName"
#Use the Key to decrypt the password and load it into memory as a SecureString
$SecureStringPassword = Get-Content -Path $EncryptedPasswordFile | ConvertTo-SecureString -Key $GetKey
$EmailCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SMTPUsername,$SecureStringPassword
#Define Email Message Options
$MailTo = "RecipientAddress@DomainDotCom"
$MailFrom = "Username@YourDomainDotCom"
$MailSubject = "Hello world"
$MailBody = "Here's the a test email that was sent using Powershell Send-MailMessage to send an email.  The Powershell Script authenticated with the sending mail server using credentials that were stored in an encrypted file and decrypted on the fly during script execution to be passed as a System.Management.Automation.PSCredential."
#Send Email
Send-MailMessage -From $MailFrom -To $MailTo -Subject $MailSubject -Body $MailBody -Port $SMTPPort -Credential $EmailCredential -UseSsl


The screenshot below shows the demonstration script excluding the “Define Email Message Options” and “Send Email” portions at the end of the script.  For demonstration purposes, it also shows what the content of the $EmailCredentials variable, a PSCredential object, looks like.  Because the password was loaded as a SecureString, the Password is displayed as “System.Security.SecureString” instead of the actual password being displayed.

Prep Step 3

I don’t show the email portion in the demonstration screenshots because I’m not using a real Mail Server and User Account for this.  But it all works as long as all of the options that you define for the Send-MailMessage command are valid for the Mail Server that you use.

Decrypting a password file to reveal the plaintext password

As a final note on this post – I mentioned earlier that controlling access to your Key is critical, because anyone that has access to the key can decrypt the data that was secured with it.  In the situation that I’m demonstrating, since the key is being used to encrypt the password for an account, if an attacker can get their hands on the Key and the encrypted password file, then they may be able to use the User Account for accessing other services on the network (in addition to authenticating with the Mail Server, as the scripts demonstrate).

So, you got your hands on the key and password file, and you want to decrypt it to recover the plaintext password?  This will do it (re-using some variables that were used in previous scripts above)

$SMTPUsername = "Username"
$KeyStoragePath = "c:\YourKeyStorage\"
$KeyFileName = "Username@YourDomainDotCom.AES.Key"
$GetKey = Get-Content "$KeyStoragePath\$KeyFileName"
$CredentialsStoragePath = "C:\YourEncryptedCredentialsStorage"
$CredentialsFileName = "Username@YourDomainDotCom.securestring"
$EncryptedPasswordFile = "$CredentialsStoragePath\$CredentialsFileName"
$SecureStringPassword = Get-Content -Path $EncryptedPasswordFile | ConvertTo-SecureString -Key $GetKey
$EmailCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SMTPUsername,$SecureStringPassword


The screenshot below shows that Powershell code being used to expose the plaintext password we encrypted earlier.  The password is “ThisIsMyP@ssw0rdfjaldskf;jNENROIEFDnlndlfw392fdkjslfjo3fdkNLFDSNFKJLSo32eo9#(*$)#$#(*%&NFJEI#fdklew”

Prep Step 4

AES encrypted credentials for Powershell scripts (and a bit about DPAPI)

Thoughts on the handling CVE-2018-0101 / Cisco Bug CSCvg35618

Update: 02/05/2018 – Cisco’s advisory and fixed software versions have changed.  For current info, refer to  The tables and fixed releases listed below are stale.  All currently recommended ASA Software versions to fix CVE-2018-0101 were published on Feb 3. If you patched before that, you need to patch again.

Original Post:

I want to rant for a few moments.  It was never my intention to use this blog as an outlet for ranting, but I don’t seem to find the time to publish much content, and I needed a place to let this out.  Maybe this doesn’t even qualify as a rant.  Either way, let’s get to it.

I want to talk about Cisco Bug ID CSCvg35618, CVE-2018-0101, which received a CVSS 3.0 score of 10.  This is the highest (most critical) rating that a vulnerability can receive.  Cisco published an advisory for this bug here on January 29, 2018 at 17:00 GMT.

The table below is pulled directly from that advisory.  It lists Cisco ASA major software versions that are affected in the column on the left, and the ASA Interim Software Version that the vulnerability was fixed in on the right.

Cisco ASA Major Release  First Fixed Release 
8.x1 Affected; migrate to or later
9.0 Affected; migrate to or later
9.3 Affected; migrate to or later
9.5 Affected; migrate to or later

The next table is something that I just put together.  It has all of the First Fixed Releases from the table above on the left, and the date that they were made available for download from Cisco’s support website on the right.

First Fixed Release   Release Date November 15, 2017 January 8, 2018 December 8, 2017 November 28, 2017 November 10, 2017 November 10, 2017 Unavailable?

Eighty days.  Eighty days is the amount of time that passed between the earliest software version that fixed the vulnerability being released, and the advisory being published.  Eighty Days!

Other Major Release versions had interim versions released more recently, but for some versions, it was eighty days.

To reiterate – this is a critical vulnerability, with a CVSS Score of 10.  The vulnerability can result in Remote Code Execution and Denial of Service when it’s exploited.

If I were to draw a network diagram for most organizations that own Cisco ASAs, where do you think those devices are placed on that diagram?  That’s right – most of the time an ASA is at the edge of the network and accessible from the Internet.  In fact, if you have an account for Shodan (, go ahead and run a query “Set-Cookie: webvpn”, and you can find over 160,000 devices connected to the internet that certainly look like ASAs, and they might be running vulnerable versions of the webvpn / AnyConnect services.

I can understand some of the challenges that Cisco and their peers are up against.  But even with that, I’m not sure that customers should be willing to accept that an advisory like this can be withheld for eighty days after some fixes are already available.  Eighty days is a long time, and it’s a particularly long time for a vulnerability with a CVSS Score of 10 that affects devices that are usually directly connected to the internet.

I know what at least one person reading this is going to think – “But… the updates were available already, so you should have patched your systems in a timely manner regardless of an advisory like this being published.”  Yes, customers need to take responsibility for installing patches in a timely manner.  However, customers also need to have access to adequate information so that they can appropriately prioritize among myriad workloads.  The advisory that Cisco published on January 29, 2018 contains the information that is so critical for customers to have at their disposal.

Beyond the need for customers to have access to critical information, there is another problem that Cisco could help to resolve.  There are many customers and engineers at VARs that only install Cisco “Suggested” releases that receive a “gold star” icon to attest to their quality, stability, longevity, and adoption rate.  None of the interim releases that fix CVE-2018-0101 are “suggested” “gold star” releases.  That needs to change.  Cisco needs to do more to encourage customers and VARs to install software versions that include security fixes.

By the inherent nature of interim releases that contain critical security fixes, those releases are never going to have a “gold star” – they have not been available long enough for Cisco to attest to their quality, stability, longevity, and adoption rate.  The criteria for a release receiving the “gold star” icon needs to change, or there needs to be another icon next to the release that indicates a severity level.  With a fix for a critical vulnerability included in a release, all of the interim releases listed above would be identified as Critical releases if there were somewhere that Cisco shared that type of rating.

Transparency is important.  Share critical security information as soon as possible.  Help us protect our systems.  Strive to be a better technology partner.

Thoughts on the handling CVE-2018-0101 / Cisco Bug CSCvg35618

Powershell to search Domain Computers for Symantec Endpoint Protection version in response to Meltdown and Spectre patches (Updated with Reg Key search)

Real quick post –

Here’s some powershell to check the version of cceraser.dll on systems in an AD Domain and export the results to csv.

Import-Module ActiveDirectory

$computers = Get-ADComputer -SearchBase "OU=BaseOUForComputers,DC=YOUR,DC=DOMAIN" -filter * | Select -ExpandProperty Name
foreach ($computer in $computers) {
Get-ChildItem -Recurse -Force "\\$computer\c$\ProgramData\Symantec\Symantec Endpoint Protection\CurrentVersion\*" -include cceraser.dll -ErrorAction SilentlyContinue | %{ $_.VersionInfo } | Select-Object FileName,FileVersion | Export-Csv C:\PSSearch\FoundFiles-cceraser.csv -nti -append


If your environment is small enough, just look through that CSV for file versions of 117.2.0 and earlier ( and greater is good), and you’ll know that you need to update/repair their Symantec Endpoint Protection definitions before you can install Microsoft’s patches.

If your environment is larger, you might want something more significant.


Update 01/09/2018 – Here’s some more useful Powershell to look for the necessary “QualityCompat” registry key that should be set if your A/V software is up to date and your A/V vendor is playing along.  (Check this doc being maintained by Kevin Beaumont / @GossiTheDog to see if your A/V vendor is playing along


################### Search for MeltdownAVCompatibility Registry Keys ###################
 Import-Module PSRemoteRegistry
 Import-Module ActiveDirectory
 $computers = Get-ADComputer -SearchBase "DC=YOUR,DC=DOMAIN" -filter * | Select -ExpandProperty Name

foreach ($computer in $computers) {
 Get-RegValue -ComputerName $computer -Key 'SOFTWARE\Microsoft\Windows\CurrentVersion\QualityCompat' | Export-CSV c:\PSSearch\FoundRegKeys-MeltdownAVCompatibilityKey.csv -nti -append




Powershell to search Domain Computers for Symantec Endpoint Protection version in response to Meltdown and Spectre patches (Updated with Reg Key search)

SDProp and Password changes for Domain Admins (and other Protected Group members)

Problem: Domain Admins (and other Protected Group members) can’t set their own password

User accounts that are members of Protected Groups, such as Domain Admins, have their Access Controls Lists (ACLs) set to match the ACL of the AdminSDHolder object every 60 minutes (reference:

Because of this, those User accounts are not able to change their own password when either…

  • The password expires


  • They press ctrl+alt+del and select the “change password” option

…because the Security Description Propagator (SDPROP) removes those permissions every time it runs and matches the User’s ACL to AdminSDHolder.

More Details:

When SDProp Runs, a lot of things happen (Microsoft and other sources are going to be much better for info), but a couple of things happen that are directly related to the problem described above.  I’m going to do my best to explain them below.

To start, I went into ADUC, into the Properties of my Domain Admin account, and flipped the “User cannot change password” checkbox on (applied changes) and off again (applied changes).  This will put my Domain Admin user account into a state where it can change its own password in the two scenarios that are mentioned in the “Problem” section of this document.


After you do that (and before SDProp runs again at its’ regular 60min interval…timing may be tough here), if you go to the Security Tab, you will notice that “Everyone” is granted the “Change password” permissions.  Now, you may be thinking to yourself – “Everyone?  That can’t be right.”  Well, it is.  This is a default entry (for objects that aren’t protected by SDAdminHolder):

The Everyone group has Change Password permissions on all computer and user objects so that unauthenticated or “anonymous” users or computers are able to change their passwords when they expire without having to be authenticated first. If the anonymous user is denied the ability to change passwords, the user would be unable to change the password without logging on. (

Things would obviously be at a stand-still if a user with an expired password needed to log in before they were allowed to set a new password.


However, if you wait for SDProp to run (or force it as in screenshot below- reference end of article instructions using LDP.exe: ) and wait for it to complete…


…SDProp removes the “Change Password” permission that’s assigned to “Everyone”


Proving that granting “Everyone” the “Change password” permission is all it takes:

Now that my Domain Admin account has been modified by SDProp and is unable to change its own password, let’s forget about flipping the “User cannot change password” setting.  We want to confirm that granting “Everyone” the “Change password” permission is all it takes, and there’s nothing else happening that’s preventing the change.  So just go right to the Security ACLs.  Click “Advanced” and then “Add…” search for “Everyone” and click “OK”


In the new Permission Entries that apply to “Everyone”, Scroll down until you see “Change password”, check the “allow” box, and click OK.


With that set, you will see “Everyone” has been granted the “Change Password” permission on the Security Tab for this User account.  And you can successfully log on as that user, press CTRL+ALT+DEL, and choose the “Change password” option.

Now the remaining question is – what should be done, if anything, to allow a Domain Admin (or another user account that is a member of a Protect Group) the ability to change their password without asking another Domain Admin to fix the ACL so that they are (temporarily) allowed to do it?

I haven’t seen a lot of good recommendations on solving this problem (or is it a problem?  there is good reason for SDProp to protect these accounts…).  Everything I can think of is a little “hacky”.  There should be a widely accepted fix for this.  I would be hesitant to implement any fix that’s really unique to one particular environment, because I’m concerned about ongoing supportability, and the fact that, eventually, someone else will be responsible for managing the environment and they won’t be familiar with it.

SDProp and Password changes for Domain Admins (and other Protected Group members)

Web Bugs in native Excel .xlsx files

I had a previous blog post about using the Web Bug Server and Web Bug Documents from the ADHD Distro to conduct an internal phishing campaign (for reporting on who was opening attachments only…nothing really fancy).  I was toying with Excel today, and found I was able to get an Excel file to check in to the Web Bug Server using the method described below…

The following applies to Excel 2010, and the file saved as an xlsx file.  I don’t know if the same applies to newer or older versions of Excel.  I’m assuming it does, but the steps might be slightly different.

Create a new Excel file, and Click the “Data” Tab

Click the “From Web” button, and then enter your Web Bug Server URL into the Address field.  I specified “type=xls” in the url to indicate that my Excel file phoned home to the Web Bug Server.

Click Go, then the little arrow icon in the screenshot below, and then Import


This will result in a cell that looks like this, and you can right-click and Edit Query to see the full URL


Next, go to the “Data” tab, click the down-arrow on the “Refresh All” button and select “Connection Properties…”


On the Connection Properties window, check the box that says “Refresh data when opening the file”.


Now, every time you open the file you should see this message on the bottom of the Excel window, and you will get a new hit on your Web Bug Server with type “xls” every time the Excel file is opened.


Edit: there is 1 caveat here.  When you open the file on a computer for the first time, Excel is going to prompt you with a Security Warning that says “Data connections have been disabled”, and give you the option to Enable them.


Some people may not click on the “Enable Content” button.  But entering some text in the spreadsheet to try to entice them to click it might be all it takes.  Something like “Please click the “Enable Content” button above to view {whatever your target is interested in seeing}.”  As soon as that button is clicked, the file hits the Web Bug Server.


Web Bugs in native Excel .xlsx files

Scripting to solve problems (Windows DNS Debug Logs, Splunk, Powershell)

Anyone that’s worked in or been involved in IT for any substantial amount of time knows that creating scripts, even the most simple ones, can solve a lot of your problems.

A while back, I started pulling DNS Requests Debug Logs from my Windows DNS servers in to Splunk.  Having a copy of request logs, and being able to drill down into them with Splunk, can be extremely helpful for many reasons, including if your IPS alerts you to some suspicious DNS queries and you need to find the origin.

Unfortunately, Splunk occasionally causes one of my DNS servers to be unable to write to the debug log, and the server writes Event ID 3152 to the application log.  The details that are logged say:

The DNS server was unable to open file D:\DNS Requests Log\dnsrequests.txt for write.  Most likely the file is a zone file that is already open.  Close the zone file and re-initiate zone write.

I don’t know what exactly is causing the failure, but some other people have also noticed the problem.

To work around this problem, I wrote a short Powershell script and triggered it to run (via Task Scheduler) whenever Event ID 3152 is logged.  This works well for my environment because I’m not seeing Event ID 3152 logged for any other reasons than when the DNS Server service is unable to write to the debug log.  It can also be logged due to problems writing to a zone file ( so you should research your own environment before implementing this.  Actually, you should definitely, absolutely, always, research your own environment before you believe what some random guy on the Internet wrote.

I started out with a 1 line script to just restart the service, but on one occasion, the script triggered, and immediately logged 3152 again (probably while the originally triggered instance of the task was still running), and the DNS Server service was never able to create the new log file to write to.  So, I ended up with a script that stops the SplunkForwarder service, restarts the DNS Service, sleeps for 5 seconds, and then checks to make sure that the file exists and restarts the service again if it still doesn’t exist, and then starts SplunkForwarder.

$DNSLogPath = “D:\DNS Requests Log\dnsrequests.txt”

Stop-Service SplunkForwarder

Restart-Service DNS

Start-Sleep 5

if(!(Test-Path -Path $DNSLogPath))
Start-Service SplunkForwarder
Restart-Service DNS

Start-Sleep 5

Start-Service SplunkForwarder


Of all things, my scripting/programming experience is the weakest, so get in touch with me if you think this is ugly and have a better way to do it!

Also, once you’ve got the script scheduled to run based on an Event Viewer trigger, you can add a second action to the Task Scheduler job that will send you an email.  That way, you know when it happens and can manually verify that the new debug log exists.

Scripting to solve problems (Windows DNS Debug Logs, Splunk, Powershell)