ShareDiscreetlyWebServer v1.0.1.2

by Ryan 13. April 2013 16:47

Several improvements over the last release the past few days:

  • Some code optimizations. Pages are rendering about an order of magnitude faster now.
  • Just about all the HTML and Javascript has been exported to editable files so that an administrator can change up the code, color schemes, branding, etc., without needing to recompile the code.
  • The server can now send an S/MIME, digitally signed email to the person you want to send the URL to. Unfortunately some email clients (such as the Gmail web client) don't natively understand S/MIME, but Outlook handles it just fine. You can also get various plugins and programs to read S/MIME emails if your email client doesn't understand it. I'd rather lean toward more security-related bells and whistles than max compatibility for this project.

You can access the secret server at https://myotherpcisacloud.com.

ShareDiscreetlyWebServer v1.0.0.3

by Ryan 9. April 2013 13:17

I wrote a web service.  I call it "ShareDiscreetly".  Creative name, huh?

I wrote the server in C# .NET 4.0.  It runs as a Windows service.

ShareDiscreetlyWebServer serves a single purpose: to allow two people to share little bits of information - secrets - such as passwords, etc., in a secure, discreet manner.  The secrets are protected both in transit and at rest, using the FIPS-approved AES-256 algorithm with asymmetric keys supplied by an X.509 certificate.

Oh, and I made sure that it's thoroughly compatible with Powershell so that the server can be used in a scriptable/automatable way.

You can read a more thorough description of the server as you try it out here.

Please let me know if you find any bugs, exploits, or if you have any feature requests!

DNS over HTTP

by Ryan 31. March 2013 12:39

I was discussing with some fellow IT admins, the topic of blocking certain websites so that employees or students couldn't access them from the work or school network.  This is a pretty common topic for IT in most workplaces.  However, I personally don't want to be involved in it.  I realize that at some places, like schools for instance, filtering of some websites may be a legal or policy requirement.  But at the workplace, if an employee wants to waste company time on espn.com, that is an issue for HR and management to take up with that employee.  And again in my opinion, it's not about how much time an employee spends on ESPN or Reddit either, but simply whether that employee delivers satisfactory results.  I don't want to handle a people problem with a technical solution.  I don't want to be the IT guy that derives secret pleasure from blocking everyone from looking up their fantasy football scores.  (Or whatever it is people do on espn.com.)  I could spend my entire career until I retire working on a web proxy, blocking each and every new porn site that pops up.  If there's one thing the internet has taught me, it's that there will always be an infinite number of new porn sites.

On the other extreme of black listing, someone then suggested white listing.  Specifically, implementing "DNS white listing" in their environment for the purpose of restricting what internet sites users were allowed to access to only a handful of internet sites.  Well that is a terrible idea.  The only proper way of doing this in my opinion is to use a real web proxy, such as ISA or TMG or Squid.  But I could not help but imagine how I might implement such a system, and then how I might go about circumventing it from the perspective of a user.

OK, well for my first half-baked idea, I can imagine standing up a DNS server, disabling recursion/forwarders on that DNS server, and putting my "white list" of records on that DNS server.  Then, by way of firewall, block all port 53 access to any other IP except my special DNS server.  Congratulations, you just made your users miserable, and have done almost nothing to actually improve the security of your network or prevent people from accessing other sites.  Now the users just have to find another way of acquiring IP addresses for sites that aren't on your white list.

Well how do I get name resolution back if I can't use my DNS server?  I have an idea... DNS over HTTP!

The guys at StatDNS have already thought about this.  And what's awesome, is that they've created a web API for resolving names to IPs over HTTP.  Here's what I did in 5 minutes of Powershell:

PS C:\> Function Get-ARecordOverHTTP([string]$Query) { $($($(Invoke-WebRequest http://api.statdns.com/$Query/a).Content | ConvertFrom-Json).Answer).rdata }

PS C:\> Get-ARecordOverHTTP google.com
173.194.70.101
173.194.70.100
173.194.70.138
173.194.70.102
173.194.70.139
173.194.70.113

PS C:\> Get-ARecordOverHTTP myotherpcisacloud.com
168.61.52.184

Simple as that. How cool is Powershell, seriously?  One line to create a function that accepts a name and returns a list of IPs by interacting with an internet web service.  Pretty awesome if you ask me.

As long as you have port 80 open to StatDNS, you have internet name resolution.  Now, to wrap this into a .NET-based Windows service...

Examining Internet Explorer Tracking Protection Lists (with Powershell)

by Ryan 22. March 2013 16:12

I know that Firefox and Chrome are still the only browsers that most people will ever even consider using, but ever since I started using Windows 8, I've not yet really felt a compelling reason to stop using the built-in IE10. It's fast, it passes Acid tests, and has a nice "Developer Mode" built-in where you can change IE and rendering versions on the fly (hit F12 on your keyboard.) That, and Compatibility Mode, which I think is really under-rated in a corporate environment, where employees are forced to use antiquated corporate websites that were built for IE5.

About the only thing I miss from Chrome is my sweet, sweet AdBlock. Does IE have anything that can compare? Well, it has Tracking Protection Lists, which is a good start:

IE Tracking Protection Lists

IE Tracking Protection Lists

The Personalized List kinda' sucks because you only have two choices - either block everything that IE detects as a Javascript-type thing that pulls info from other domains and is frequently seen in similar pages, or, you have to wait for IE to detect the script 10 times or so, then you have to go back in and manually choose to block it. 

Personalized Tracking List

Of course Google is going to be the number one offender here, since that's how they make money is by shoving ads in your face around every corner and making you want to buy stuff.  Honestly, I sympathize with online advertisers to a certain point, because I believe the internet would not have as much rich, free content as it does if internet advertisers were unable to make money by providing free services.  I mean, Google doesn't keep Youtube up just because they love paying those network bandwidth bills so much. But, the ads can quickly get simply too obnoxius, and we users need a way to filter all that junk that's flashed before our eyes.  Especially the Google ones that break Internet Explorer's back button.  It's Google's code that decides to open 4 instances of some doubleclick.net resource in a way that it causes you to have to hit the Back button in your browser 5 times to get back to where you were. From legalinsurrection.com

The flip-side of the coin is that if you block too much, certain websites will stop working properly. So you need to find a happy medium. Queue Tracking Lists.  Just like AdBlock, Tracking Lists are designed to block just the web content that has been decided is more harmful or annoying than good.  That way we can keep using our Gmail, Youtube, StackExchange, etc., without interruption, while still cutting out a good chunk of the ads.

You can download new Tracking Protection Lists online, right from within the tracking lists dialog box. Just click the "Get A Tracking Protection List Online..." link. It will take you to a website.  Notice in the screenshot above, that I have already added the "EasyList" TPL, from the same people that do AdBlock.  Also notice that the Tracking Protection List is simply an HTTP URI to a *.tpl text file.  And you know what that means... that means we can play with it from Powershell!

Say you wanted to examine a TPL and see if it contained a certain domain name. Well first, let's download the TPL and store it to a variable:

PS C:\> $list = Invoke-WebRequest http://easylist-msie.adblockplus.org/easylist.tpl

Now the content of the tracking list will be in $list.Content. These are the web resources that the list will block.  You can go here to see the syntax of TPLs. Warning: There will be distasteful words in this list... as the list is designed to block distasteful content.

Alright, so what if we want to know whether this TPL will block content from the domain streamcloud.eu. First, let's break $list.Content up into lines by splitting it on newlines:

PS C:\> $list.Content.Count
1
PS C:\> $Content = $list.Content.Split("`r`n")
PS C:\> $Content.Count
10203

After splitting the content element of the web request on newlines, we can see that the TPL contains 10,203 lines. I first thought to split on [String]::NewLine, but that did not yield correct results. (Dat character encoding!) Now, keeping in mind that lines that start with a # are comments, let's see if we can find entries that contain streamcloud.eu:

PS C:\> foreach($_ in $Content) { If(!$_.StartsWith('#') -and $_.Contains("streamcloud.eu")) { $_ } }
-d streamcloud.eu /deliver.php
+d streamcloud.eu

So from this output, it appears that we are allowing streamcloud.eu, but we are specifically blocking any document named deliver.php coming from streamcloud.eu.  This could have also been written as:

PS C:\> foreach($_ in $Content) { If(!$_.StartsWith('#') -and $_ -match "streamcloud.eu") { $_ } }

But a lot of times I just naturally prefer the C# parlance. The good thing about Powershell is that you're free to mix and match.

You can certainly elaborate on the concept I've started on above, and I hope you will.  Until next time!

Encrypt-File and Decrypt-File using X.509 Certificates

by Ryan 11. March 2013 16:36

I like encrypting stuff.  Here are two Powershell functions I whipped up that can encrypt and decrypt a file using a valid key pair from an X.509 certificate. The encryption uses the public key, but only the corresponding private key can decrypt the data.

Encrypt-File:

<#
.SYNOPSIS
This Powershell function encrypts a file using a given X.509 certificate public key.
.DESCRIPTION
This Powershell function encrypts a file using a given X.509 certificate public key.
This function accepts as inputs a file to encrypt and a certificate with which to encrypt it.
This function saves the encrypted file as *.encrypted. The file can only be decrypted with the private key of the certificate that was used to encrypt it.
You must use a certificate that can be used for encryption, and not something like a code signing certificate.
.PARAMETER FileToEncrypt
Must be a System.IO.FileInfo object. $(Get-ChildItem C:\file.txt) will work.
.PARAMETER Cert
Must be a System.Security.Cryptography.X509Certificates.X509Certificate2 object. $(Get-ChildItem Cert:\CurrentUser\My\9554F368FEA619A655A1D49408FC13C3E0D60E11) will work. The public key of the certificate is used for encryption.
.EXAMPLE
PS C:\> . .\Encrypt-File.ps1
PS C:\> Encrypt-File $File $Cert
.EXAMPLE
PS C:\> . .\Encrypt-File.ps1
PS C:\> Encrypt-File $(Get-ChildItem C:\foo.txt) $(Get-ChildItem Cert:\CurrentUser\My\THUMBPRINT)
.INPUTS
Encrypt-File <System.IO.FileInfo> <System.Security.Cryptography.X509Certificates.X509Certificate2>
.OUTPUTS
A file named $FileName.encrypted
.NOTES
Written by Ryan Ries - ryan@myotherpcisacloud.com
.LINK
http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx
#>

Function Encrypt-File
{
	Param([Parameter(mandatory=$true)][System.IO.FileInfo]$FileToEncrypt,
		  [Parameter(mandatory=$true)][System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert)

	Try { [System.Reflection.Assembly]::LoadWithPartialName("System.Security.Cryptography") }
	Catch { Write-Error "Could not load required assembly."; Return }	
	
	$AesProvider                = New-Object System.Security.Cryptography.AesManaged
	$AesProvider.KeySize        = 256
	$AesProvider.BlockSize      = 128
	$AesProvider.Mode           = [System.Security.Cryptography.CipherMode]::CBC
	$KeyFormatter               = New-Object System.Security.Cryptography.RSAPKCS1KeyExchangeFormatter($Cert.PublicKey.Key)
	[Byte[]]$KeyEncrypted       = $KeyFormatter.CreateKeyExchange($AesProvider.Key, $AesProvider.GetType())
	[Byte[]]$LenKey             = $Null
	[Byte[]]$LenIV              = $Null
	[Int]$LKey                  = $KeyEncrypted.Length
	$LenKey                     = [System.BitConverter]::GetBytes($LKey)
	[Int]$LIV                   = $AesProvider.IV.Length
	$LenIV                      = [System.BitConverter]::GetBytes($LIV)
	$FileStreamWriter           
	Try { $FileStreamWriter = New-Object System.IO.FileStream("$($FileToEncrypt.FullName)`.encrypted", [System.IO.FileMode]::Create) }
	Catch { Write-Error "Unable to open output file for writing."; Return }
	$FileStreamWriter.Write($LenKey,         0, 4)
	$FileStreamWriter.Write($LenIV,          0, 4)
	$FileStreamWriter.Write($KeyEncrypted,   0, $LKey)
	$FileStreamWriter.Write($AesProvider.IV, 0, $LIV)
	$Transform                  = $AesProvider.CreateEncryptor()
	$CryptoStream               = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
	[Int]$Count                 = 0
	[Int]$Offset                = 0
	[Int]$BlockSizeBytes        = $AesProvider.BlockSize / 8
	[Byte[]]$Data               = New-Object Byte[] $BlockSizeBytes
	[Int]$BytesRead             = 0
	Try { $FileStreamReader     = New-Object System.IO.FileStream("$($FileToEncrypt.FullName)", [System.IO.FileMode]::Open)	}
	Catch { Write-Error "Unable to open input file for reading."; Return }
	Do
	{
		$Count   = $FileStreamReader.Read($Data, 0, $BlockSizeBytes)
		$Offset += $Count
		$CryptoStream.Write($Data, 0, $Count)
		$BytesRead += $BlockSizeBytes
	}
	While ($Count -gt 0)
	
	$CryptoStream.FlushFinalBlock()
	$CryptoStream.Close()
	$FileStreamReader.Close()
	$FileStreamWriter.Close()
}

Decrypt-File:

<#
.SYNOPSIS
This Powershell function decrypts a file using a given X.509 certificate private key.
.DESCRIPTION
This Powershell function decrypts a file using a given X.509 certificate private key.
This function accepts as inputs a file to decrypt and a certificate with which to decrypt it.
The file can only be decrypted with the private key of the certificate that was used to encrypt it.
.PARAMETER FileToDecrypt
Must be a System.IO.FileInfo object. $(Get-ChildItem C:\file.txt) will work.
.PARAMETER Cert
Must be a System.Security.Cryptography.X509Certificates.X509Certificate2 object. $(Get-ChildItem Cert:\CurrentUser\My\9554F368FEA619A655A1D49408FC13C3E0D60E11) will work. The public key of the certificate is used for encryption. The private key is used for decryption.
.EXAMPLE
PS C:\> . .\Decrypt-File.ps1
PS C:\> Decrypt-File $File $Cert
.EXAMPLE
PS C:\> . .\Decrypt-File.ps1
PS C:\> Decrypt-File $(Get-ChildItem C:\foo.txt) $(Get-ChildItem Cert:\CurrentUser\My\THUMBPRINT)
.INPUTS
Decrypt-File <System.IO.FileInfo> <System.Security.Cryptography.X509Certificates.X509Certificate2>
.OUTPUTS
An unencrypted file.
.NOTES
Written by Ryan Ries - ryan@myotherpcisacloud.com
.LINK
http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx
#>

Function Decrypt-File
{
	Param([Parameter(mandatory=$true)][System.IO.FileInfo]$FileToDecrypt,
		  [Parameter(mandatory=$true)][System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert)

	Try { [System.Reflection.Assembly]::LoadWithPartialName("System.Security.Cryptography") }
	Catch { Write-Error "Could not load required assembly."; Return }
	
	$AesProvider                = New-Object System.Security.Cryptography.AesManaged
	$AesProvider.KeySize        = 256
	$AesProvider.BlockSize      = 128
	$AesProvider.Mode           = [System.Security.Cryptography.CipherMode]::CBC
	[Byte[]]$LenKey             = New-Object Byte[] 4
	[Byte[]]$LenIV              = New-Object Byte[] 4
	If($FileToDecrypt.Name.Split(".")[-1] -ne "encrypted")
	{
		Write-Error "The file to decrypt must be named *.encrypted."
		Return
	}
	If($Cert.HasPrivateKey -eq $False -or $Cert.HasPrivateKey -eq $null)
	{
		Write-Error "The supplied certificate does not contain a private key, or it could not be accessed."
		Return
	}
	Try { $FileStreamReader = New-Object System.IO.FileStream("$($FileToDecrypt.FullName)", [System.IO.FileMode]::Open)	}
	Catch
	{ 
		Write-Error "Unable to open input file for reading."		
		Return 
	}	
	$FileStreamReader.Seek(0, [System.IO.SeekOrigin]::Begin)         | Out-Null
	$FileStreamReader.Seek(0, [System.IO.SeekOrigin]::Begin)         | Out-Null
	$FileStreamReader.Read($LenKey, 0, 3)                            | Out-Null
	$FileStreamReader.Seek(4, [System.IO.SeekOrigin]::Begin)         | Out-Null
	$FileStreamReader.Read($LenIV,  0, 3)                            | Out-Null
	[Int]$LKey            = [System.BitConverter]::ToInt32($LenKey, 0)
	[Int]$LIV             = [System.BitConverter]::ToInt32($LenIV,  0)
	[Int]$StartC          = $LKey + $LIV + 8
	[Int]$LenC            = [Int]$FileStreamReader.Length - $StartC
	[Byte[]]$KeyEncrypted = New-Object Byte[] $LKey
	[Byte[]]$IV           = New-Object Byte[] $LIV
	$FileStreamReader.Seek(8, [System.IO.SeekOrigin]::Begin)         | Out-Null
	$FileStreamReader.Read($KeyEncrypted, 0, $LKey)                  | Out-Null
	$FileStreamReader.Seek(8 + $LKey, [System.IO.SeekOrigin]::Begin) | Out-Null
	$FileStreamReader.Read($IV, 0, $LIV)                             | Out-Null
	[Byte[]]$KeyDecrypted = $Cert.PrivateKey.Decrypt($KeyEncrypted, $false)
	$Transform = $AesProvider.CreateDecryptor($KeyDecrypted, $IV)
	Try	{ $FileStreamWriter = New-Object System.IO.FileStream("$($FileToDecrypt.Directory)\$($FileToDecrypt.Name.Replace(".encrypted",$null))", [System.IO.FileMode]::Create) }
	Catch 
	{ 
		Write-Error "Unable to open output file for writing.`n$($_.Message)"
		$FileStreamReader.Close()
		Return
	}
	[Int]$Count  = 0
	[Int]$Offset = 0
	[Int]$BlockSizeBytes = $AesProvider.BlockSize / 8
	[Byte[]]$Data = New-Object Byte[] $BlockSizeBytes
	$CryptoStream = New-Object System.Security.Cryptography.CryptoStream($FileStreamWriter, $Transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
	Do
	{
		$Count   = $FileStreamReader.Read($Data, 0, $BlockSizeBytes)
		$Offset += $Count
		$CryptoStream.Write($Data, 0, $Count)
	}
	While ($Count -gt 0)
	$CryptoStream.FlushFinalBlock()
	$CryptoStream.Close()
	$FileStreamWriter.Close()
	$FileStreamReader.Close()
}

Signing Powershell Scripts

by Ryan 8. March 2013 13:19

Hi guys,
 
Something you might know about me is that I think Powershell is just about the best thing to ever happen to Windows Server.  And even if you don't agree... well, you don't really have a choice because there are already tasks that must be done in Powershell and there is no GUI alternative, and that trend will only continue.
 
Powershell will become part of your life if you plan on continuing to work with Windows Server. PS is very powerful, and it makes many tasks much faster and more automatable.  But with that power, comes the side-effect that PS also makes many nefarious activities faster and easier as well.  An attacker has many more tools and methods (no pun intended) available to him or her if they have unrestricted access to Powershell on your server. You could restrict all Powershell usage on your servers, but that means losing an extremely valuable tool for administrators. There must be a compromise...

One effective way to mitigate this risk is to digitally sign your Powershell scripts with a certificate. By setting the execution policy of PS scripts on your servers to AllSigned, Powershell will not run scripts unless it can successfully validate the signature that is in the script.  That also means that the script cannot be modified after being signed, as the signature will no longer match the modified script.  This is much better than the RemoteSigned execution policy, which only mandates that scripts downloaded through an Attachment Execution Service-compliant application need to be signed, which is extremely easy to bypass. (e.g. just clear the alternate NTFS data stream.)
 
Good news is that code signing, especially in Powershell, is actually pretty easy to do.
 
First, as an administrator of an Active Directory domain with an Enterprise Certificate Authority, you need to make a code signing certificate for your users to use.  On your CA, open the Certificate Templates MMC snap-in.  You can also get there in the CA snap-in by right-clicking on Certificate Templates and choosing Manage.

Cert Templates

Duplicate the Code Signing template, so that you don't modify the original template.  Configure all the properties of this template as desired, including allowing Authenticated Users (or whichever users you desire) the ability to enroll in your new certificate template.

Cert Templates

I very creatively named my template "Code Signing Template v1."  Now go back to your Certificate Authority snap-in, and choose New -> Certificate Template to Issue.  Choose your new code signing cert.

Cert Templates

Keep in mind that this information is being uploaded to Active Directory, so make sure AD replication has taken effect before wondering why the certificate isn't already available at your location as soon as you issue it.

Now, on your workstation, where you are logged in as a regular user, you want to enroll for one of those new code signing certs.  You could do this in the Certificates MMC snap-in:

Cert Enrollment

But, since we like to save time, let's go ahead and instead submit the certificate request via Powershell:

Powershell

You have to take the spaces out of the certificate template name.  But you'll know the certificate request was successful if the status is Issued.  You'll also notice that the public key of your new code signing cert is now published publicly in your Active Directory user account.  This is so everyone else in the domain can access your public key so that they can validate that you did, in fact, sign that exact Powershell script, so we can figure that it's safe to run.

Powershell

Signing a Powershell script with your new code signing certificate is simple:

Powershell script sign

Just Set-AuthenticodeSignature $script $certificate, and it's signed.  The signature is actually a block of cipher-text that appears at the bottom of your newly signed script.

 

Script Signed

Your identity is also encoded in those comments, but is not encrypted.  So Powershell can tell who signed the script right off the bat.  Then, Powershell retrieves the public key of that identity (from AD for example.)  The script was signed using your private key, which you do not share, and can only be decrypted with your public key, which you do share.  So if Powersell is able to decode the signature with your public key, it can be reasonably assured that it was signed by you.  If the script changes by even a single character, then that hashed with your private key would result in an entirely different signature, therefore modification of the script means Powershell will not run it unless it is re-signed by another authorized user, and we would be able to tell who that was.
 
Alright, that's enough out of me.  Hopefully I've been able to at least pique your interest in the potential security benefits of code signing!

(And sorry for all the cheesy pixellation.)

Azure Outage, The File Cabinet Blog, Etc.

by Ryan 23. February 2013 09:44

Got a mixed bag this weekend... I'm still busier than usual at work, so my thoughts have been more scattered lately. I'll just start typing and we'll see where it takes us.

First, Windows Azure. I just put this very blog on Azure not two days ago, and then yesterday they suffered a massive, embarrassing, world-wide secure storage outage. I say embarrassing because it was caused by an expired SSL certificate. That's right - a world-wide outage lasting hours that could have been prevented by someone taking 5 minutes to renew a certificate.

But let's get our facts straight here - Windows Azure didn't completely go down. It was specifically their secure storage services that went down, which I heard also affected provisioning of new resources, as well as a lot of unhappy customers who were running large storage and SQL operations over SSL that relied on that certificate. The outage didn't affect any HTTP or non-SSL traffic, so this blog just sat here relatively unscathed. Of course, I feel for those who put enterprise workloads up on Azure and did get hurt by the outage, but Azure is certainly not the first public cloud service to suffer a large-scale outage, and it won't be the last. But what makes this outage so particularly poignant is that it was so easily preventable with even the most basic of maintenance plans. Such as, I dunno, maybe a sticky note on someone's monitor reminding them to renew this immensely important SSL cert.

Here are a couple of screenshots, for the Schadenfreude:

 

Technet Forums

 

Azure Dashboard

 

Next thing on the list is pretty old news, but I never mentioned anything about it here. Ned Pyle, formerly of the Ask the Directory Services Team blog, has moved to Microsoft's home base and is now on the storage product team. He's still blogging though, on the File Cabinet blog. I don't know him well, but we have exchanged a few emails and blog comments about AD stuff and such. Up there with Russinovich in my opinion, he is one of my favorite tech people on the internet though, because his blogging is both entertaining and very educational. Lots of respect. Plus, check out the comments on his latest post here, where I correctly answer some AD arcana and get some validation from one of my heroes.

Now if I can just get the PFEs to make another one of those Microsoft Certified Master Active Directory posts, I'd be as happy as a civet eatin' coffee berries.

It's a Christmas Miracle!

by Ryan 25. December 2012 11:06

Well I did something quite embarrassing the other day. But then I did something pretty cool to make up for it. I'll call it a Christmas miracle, because it involves me not knowing the domain admin password, having no other normal way of logging in, despairing for a couple minutes, then getting back in anyway. And also because I'm histrionic like that. It's a real story of tribulation and triumph.

A couple days ago, the domain admin password on one of my lab domains expired unexpectedly, and so I was prompted to reset it upon RDPing to a server. So I did. As a matter of habit, I saved the new password in my password vault software. (I'm one of those people that has a different password for every single account I have, but as a result I can't remember them all.)

At least, I thought I saved the new password.

Fast forward to a week later. I wanted to do some work in that lab again, and as I began the login process, I realized that I had no idea what I had set the password to. The domain admin password. The only Administrative account in that domain. "No problem," I thought. It's in my password vault...

It wasn't.

I went through several backup copies of the password file, thinking that it must have been overwritten. Nope. Dreadful thoughts of spending the whole weekend rebuilding the lab flashed before my eyes.

Then I had an idea.

I switched my monitor over to the physical domain controller, and plugged in a keyboard.

Server 2012 Login

It's a very calm and soothing login screen, isn't it? I like it.

Anyway, there's the ever-present "Accessibility" or "Ease of Access" icon down there in the corner. (I know many of you already know where I'm going with this, and you're right. But if you don't know, read on. Otherwise, you may want to skip to the "Afterthoughts" section below.) This domain controller is running Server 2012 Core. That accessibility button in the corner doesn't do anything. I can click it, but it doesn't do anything. Maybe it's because this is a Server Core installation with no GUI elements. Hm.

I had a Windows installation image on a bootable USB thumb drive handy, so I plugged that in to the domain controller and rebooted the machine (using the power button on the chassis obviously, since I wasn't going to be logging in.)

Once the machine had booted off the installation media, instead of choosing to install Windows, I chose "Repair your computer." From there, I chose the option to launch a Command Prompt. Voila. Sweet. I now had a shell on the computer.

Next I navigated on over to where everything interesting happens, \Windows\System32. (The drives had different drive letters than normal, but who cares.) Now, under regular circumstances, that little Accessibility button on the logon screen launches a program called Utilman.exe. From there you can get all the gizmos like the on-screen keyboard and magnifier and things like that to help you log in, in case your eyesight's not so good or you have no hands or something. But there was no Utilman.exe in the System32 folder. I guess that's why the Accessibility icon didn't do anything when I clicked it. My domain controllers are very inconsiderate of the handicapped.

So I just decided to try the command:

copy X:\Windows\System32\cmd.exe X:\Windows\System32\Utilman.exe

Then I rebooted. Once I was back at the Windows login screen, I hit the Accessibility icon one last time. Command Prompt. And not just any Command Prompt, but a Command Prompt running as Local System! Since this was a normal boot of the domain controller and therefore Active Directory was online, I just did:

net user DomainAdmin *

And I was greeted with a prompt to reset the domain administrator password to whatever I wanted.

This time I made damn sure that the new password was saved and backed up in my password vault.

Afterthoughts

So it's great that I was able to reset a domain administrator password with nothing but a bootable image and physical access to the machine, but, one can't help but wonder about the glaring security implications of this. Once a person has a Local System shell on a domain controller, they own the domain.

Furthermore, today's enterprise environments are more complex than ever. Having physical access to the console of the machine doesn't necessarily mean you have to actually be able to touch the machine, like I was able to do here. These days, "physical access" can mean logging in to an ILO or accessing a virtual machine management console from a thousand miles away. And are we, as enterprise administrators and IT directors and CTOs, sure that everyone in our organization with access to those things are people we also trust as potential domain admins? It's not just about the threat of outside attackers. We may be unwittingly exposing a massive attack surface to all of our front-line support personnel should they ever decide to go rogue.

Alright, so what should be done about this little "feature" of Windows? First off, Microsoft needs to not launch Utilman.exe as Local System. Launch it as an unprivileged account that doesn't have the ability to do things like reset Administrator passwords. I'm sure there's a technical reason why they think they "have" to run the Ease of Access stuff as Local System, so that it can simulate the secure attention sequence, etc., but I'm not buying it.  Just because it's hard doesn't mean they should get away with letting this gaping security hole go unpatched.  They could also include a hash check in the code behind that little icon so it would at least be a little more difficult to replace the real Utilman.exe with, say, a copy of Cmd.exe that was renamed to Utilman.exe.

To make matters worse, there is no "official" Group Policy setting to disable this button. You might be able to mitigate the risk somewhat by using Group Policy to do something like run a script that cacls Utilman.exe at startup and denies Everyone all access, or having Group Policy push out your own version of Utilman.exe at every boot of the computer... neither of which are ideal solutions nor are they likely to be bulletproof.

I'm rarely critical of Microsoft, but these are the sorts of things that have to be fixed ASAP, if they want to continue to be taken seriously as an enterprise-worthy operating system.

Antivirus and .NET-ception

by Ryan 24. October 2012 14:15

So I was playing around the other day with a couple of antivirus products for Windows, trying to see if I could figure out ways to evade detection, (it was a slow day.) I feel very ambivalent pretty much hate antivirus software. As an IT guy, I know that it's a necessary evil, and every once in a while it does legitimately catch something that would have infected your system, but:

  • AV software often negatively impacts the performance of the entire system, unless you make so many concessions on what it doesn't scan that it practically defeats the purpose. I mean, yeah, your website probably would perform better if I excluded the entire inetpub folder, but then who's going to slow down that e-mail spam bot that you went and got yourself infected with? 
  • You might as well consider the installation of AV software on your machine a permanent modification. Just try and cleanly uninstall that crap I dare you.
  • False sense of security. Some AV products might be slightly better or worse than others, but none of them are approaching perfect. And none of them are a replacement for bad habits, such as clicking on all the links on that popup window that didn't get blocked by your web browser for some reason when you went to that one website with farm animals on it... I mean... I wouldn't know anything about that.

So the European Institute for Computer Antivirus Research (EICAR) made up this string of ASCII characters that antivirus product vendors and users can use to test their AV products. You can put it in a text file or in an executable file or whatever, and it's supposed to trigger the AV on your machine. Depending on your AV settings, even copying the EICAR string to your clipboard might trigger your antivirus. The "on-access" scanning of antivirus products uses a file system filter driver to intercept all disk I/O, or whenever an IRP_MJ_CREATE is issued for a file, but unless the AV product is also configured to actively scan memory and network activity, then you can evade it as long as your "evil code" stays in memory and doesn't touch the disk.

So I was playing around in Powershell and C#, and I wrote this rather silly bit of script... er, code... or scrode? It's a Powershell script that uses the Add-Type cmdlet to load in a C# class on the fly. That C# class in turn, contains code to compile some more code on the fly, and turn it into an executable. (Finally the Inception reference starts to make sense.) So just for fun I download the EICAR test file from the internet (which triggers some AV products, but not others, depending on how they're configured.) Now that I have the evil EICAR string in memory, I compile my executable, which does not itself contain any malicious code, so it won't trigger AV. Then I run that new process and pass the EICAR data to it as a parameter. In this way, I feel like I was able to get "malicious" data onto the computer and into a process that could then do whatever it wants with it - all without triggering the antivirus on the system because the EICAR data never touched the disk.

$SourceCode = @"
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;

public class EvilClass
{
    public static void Main(string[] args)
    {
        var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
        var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, Environment.GetEnvironmentVariable("temp") + "\\evil.exe", true);
        parameters.GenerateExecutable = true;
        CompilerResults results = csc.CompileAssemblyFromSource(parameters,
        @"using System;
		  using System.Reflection;
          class AnotherEvilClassInsideAnotherEvilClass 
			{				
				public static void Main(string[] args)
				{					
					string msg = ""Data in evil.exe Main(): "" + args[0];
					System.Console.WriteLine(msg);					
				}
            }");
        results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));		
    }
}
"@

Add-Type -TypeDefinition $SourceCode -Language CSharp

Write-Host "Downloading some evil code from the internet and displaying it in the console..."
ForEach($_ in $(New-Object System.Net.Webclient).DownloadData('http://eicar.org/download/eicar.com'))
{
	$evilString += [char]$_
	Write-Host $([char]$_) -NoNewLine -ForegroundColor Red
}
Write-Host "`nGenerating evil.exe..."
[EvilClass]::Main([String]::Empty)
Write-Host "Executing evil.exe..."
& $Env:TEMP\evil.exe $evilString
Write-Host "Deleting evil.exe..."
Remove-Item $Env:TEMP\evil.exe

And here is the scrode in action:

Powershell power

AliceAndBob v1.0: My Diffie-Hellman Tribute

by Ryan 25. September 2012 13:53

Sorry I haven't posted in a couple weeks. Busy as usual. However, I watched this video on Youtube the other day, which inspired me to write another C# application. It is of almost no actual use, but it is entertaining and maybe even a little educational.

It is my ode to the Diffie-Hellman key exchange concept, where two actors (Alice and Bob) are able to confidently exchange messages back and forth, even though Eve is eavesdropping on their line of communication and will intercept anything they send across the wire. This is because each Alice and Bob retain a secret key that is never shared publicly, but is an essential piece of information in coming to an agreement on a shared key. Alice and Bob are able to both agree on a shared key that can then be used as the seed in a symmetric encryption algorithm to hide their messages. Eve's only recourse is to calculate all possible discrete logarithms in attempt to arrive at the same solution as Alice and Bob did. With sufficiently large numbers, this can take a very, very long time. I highly recommend the Youtube video I posted above for a much nicer explanation.

I developed the application on Windows 7 x64, with .NET 4.0. (To make it work on Windows 8, I needed to put Microsoft.VisualBasic.PowerPacks.Vs.dll in the same directory since it wasn't on my Windows 8 machine.) Since this isn't a serious, production application, I didn't put any effort into error handling or threading, but I didn't encounter any crashes. I used the System.Numerics.BigInteger class to handle large numbers, but be careful when trying to use large numbers, as the calculations quickly become astronomical and it'll peg the processor it's running on. 

Without further ado, I present AliceAndBob v1.0:

AliceAndBobHelp

 

Here's the executable: AliceAndBob.exe (354.50 kb)

And here is the entire Visual Studio 2010 project (source code): AliceAndBob.zip (746.71 kb)

As always, bug reports and enhancement requests are welcome. Take it easy on me as I never claimed to be a mathematician or a cryptography guru, but I welcome your constructive criticisms.

About

Name: Ryan Ries
Location: Texas, USA
Occupation: Systems Engineer 

I am a Windows engineer and Microsoft advocate, but I can run with pretty much any system that uses electricity.  I'm all about getting closer to the cutting edge of technology while using the right tool for the job.

This blog is about exploring IT and documenting the journey.


Blog Posts (or Vids) You Must Read (or See):

Pushing the Limits of Windows by Mark Russinovich
Mysteries of Windows Memory Management by Mark Russinovich
Accelerating Your IT Career by Ned Pyle
Post-Graduate AD Studies by Ned Pyle
MCM: Active Directory Series by PFE Platforms Team
Encodings And Character Sets by David C. Zentgraf
Active Directory Maximum Limits by Microsoft
How Active Directory Replication Topology Works by Microsoft


MCITP: Enterprise Administrator

Profile for Ryan Ries at Server Fault, Q&A for system administrators

Twitter

LOPSA

 

I do not discuss my employers on this blog and all opinions expressed are mine and do not reflect the opinions of my employers.