Random thoughts of an Exchange PFE

This blog is my effort to document and share interesting details about Exchange Server as I come across them in field, lab and community.

Ignoring SSL trust in PowerShell System.Net.WebClient

Ignoring SSL trust in PowerShell System.Net.WebClient

  • Comments 10
  • Likes

This started as a discussion on why System.Net.Webclient does not have a way to ignore SSL trust and how VBScript was easier in trying to check URL and return certain string. It’s the later part which got me going.

I wanted to prove that PowerShell is, well, more powerful and easier to use. I want on quest to find the answer and here it is!

It wasn’t well documented or easy to find but putting my bing skills to work and tweaking my findings to do what we needed to do, I was able to make it work.

Let’s see the first part of the code that started it all:

$wc = New-Object System.Net.WebClient
$src = $wc.downloadstring("https://server/abc.htm")
Very simple and benign code. All it is doing is creating a .Net WebClient object and calls a URL. Except, it is calling secure URL (notice https) and SSL it is using is not trusted. Either it is self-signed, or name does not match the hostname in URL or it is expired. Hrre’s what you get when you run it.

Exception calling "DownloadString" with "1" argument(s): "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."
Now, there is no way to specify for WebClient object to tell “ignore SSL”.

So you need to pass callback to the API. I Won’t go in gory details but simply, you need to tell the API what you override is (in our case “Ignore SSL trust and get me the page anyway”). TO do this, you need to use RemoteCertificateValidationCallBack Delegate that aPI expects. If you don’t provide it, default action takes place, which is, it it’s not trusted, close connection. MSDN has described this Delegate here. However, if you read that page and figure out PowerShell code required to ignore trust, great! I couldn’t.

So, I found Oisin Grehan’s blog which describes the code for PowerShell. And as anyone can guess, it has to be one liner…

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

Now, we set the override and told WebClient to ignore SSL trust. We then ran the 2 lines of code above and got an ERROR!!! A different one this time!

Exception calling "DownloadString" with "1" argument(s): "The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF"
Wednesday, Thusrday, Friday?! Now what? Why did it not work? Or did it?

Well, looking closer at the error, it is clear that the server is sending a string which is not formed correctly causing a protocol violation. Anyhow, it’s us on receiving end so we have to override this one too!

You can do it by using one of two methods:

1. create PowerShell.exe.config in $PSHOME location and never look back. PErsonally, I don’t like this idea as adding following lines to the file means telling PowerShell to ignore such protocol violations forever. Not a good security measure. However, if you are curious, here’s how your PowerShell.exe.config would look like (if you create a new one). it’s the “useUnsafeHeaderParsing” that is important to understand.



   
       
           
       
   

2. Now that I didn’t like the idea of leaving doors wide open, I found another way of doing it at Lee Holmes’ Blog. This sure looks ugly because we are trying to change Internal .Net system settings. The code would have been prettier otherwise. The reason I like this is, your settings of ignoring what follows are short lived and will go away when you close your PowerShell session (i.e. close current PowerShell window). This is better approach to security than opening a hole in your system every time you run PowerShell as described in option 1 above.

$netAssembly = [Reflection.Assembly]::GetAssembly([System.Net.Configuration.SettingsSection])

if($netAssembly)
{
    $bindingFlags = [Reflection.BindingFlags] "Static,GetProperty,NonPublic"
    $settingsType = $netAssembly.GetType("System.Net.Configuration.SettingsSectionInternal")

    $instance = $settingsType.InvokeMember("Section", $bindingFlags, $null, $null, @())

    if($instance)
    {
        $bindingFlags = "NonPublic","Instance"
        $useUnsafeHeaderParsingField = $settingsType.GetField("useUnsafeHeaderParsing", $bindingFlags)

        if($useUnsafeHeaderParsingField)
        {
          $useUnsafeHeaderParsingField.SetValue($instance, $true)
        }
    }
}

Now that we have all pieces of puzzle, put last section first and so on. When you call the URL, you won’t have an error now. You will be ignoring SSL trust and ignoring safe header parsing. Not a best practice but if you know and trust your sources, more PowerShell to you.

Comments
  • This is such a pain in .net compared to php - command based pulling of redirecting urls :(

  • Wow I can't believe I finally found this! I have been looking for how to do this for ages. I still don't understand the second part - why is there a protocol violation at all? And overall I don't really understand why it's so hard to do this with Powershell. But at least it works - thanks!!

  • Could someone explain how to use this code ? I couldnt understand how do we do GetRequest using this code

  • KAnus, can you provide code example so I can help?

  • Thanks for piecing this together, works great.

  • Makes me want to delete all my .net and powershell and use http

  • Dean,

    Not sure what do you mean in your comment. Do you not like disabling SSL? You don't have to. This post is just to illustrate how to do it. You have a gun, doesn't mean you have to kill someone. :)

  • Sorry for saying this, but this is all total bullshit. Just use:

    [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

    $wc = New-Object System.Net.WebClient

    $wc.DownloadString("example.org/example.php")

  • @baurmatt, I could have easily chose to delete your comment but that won't be fair. I am leaving your comment here for others to see. My intention to blog about anything is to share my findings. There are certainly many ways to achieve same goal. The one you chose could be different than mine. That doesn't make mine what you chose to call "bullshit", neither will I call yours that. Use what suits you and contribute in a way that is positive and helps others.

  • @Matthias Baur

    sorry to say, but your solution doesn't work for me

    The blog's answer does. thx !

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment