How Do I Script A Non-Default Dispatch?


/>

 

As
I’ve discussed previously, the script engines always talk to objects on the late-bound
IDispatch interface.  The
point of this interface is to allow a script language to call a method or reference
a property of an object by giving the name of the field and the arguments.  When
the dispatch object is invoked, the object does the work of figuring out which method
to call on which interface.

 

But
what if an object supports two interfaces
IFoo and IBar both
of which you want to be able to call late-bound?  The
most common way to solve this problem is to create two late-bound interfaces,
IFooDisp and IBarDisp.  So
when you ask the object for
IFooDisp,
you get an interface that can do late-bound invocation on
IFoo,
and similarly for
IBarDisp.

 

What
happens when you ask the object for an
IDispatch interface?  It’s
got to pick one of them!  The one it picks
is called the “default dispatch”.

 

In
JScript and VBScript, when you create an object (via
new
ActiveXObject
in
JScript or
CreateObject in
VBScript) the creation code always returns the default dispatch.  Furthermore,
in JScript, when you fetch a property on an object and it returns a dispatch object,
we ask the object to give us the default dispatch.  So
in JScript, there is no way to script a non-default dispatch.

 

What
about in VBScript?  There’s an irksome
story here, again featuring a really bad mistake made by yours truly.  At
least I meant well.

 

I
didn’t write the variant import code, and I had always assumed that VBScript did the
same thing as JScript — when an object enters the script engine from outside, we
query it for its default dispatch.  As
it turns out, that’s not true.  In VBScript,
for whatever reason, we just pass imported dispatch objects right through and call
them on whatever dispatch interface the callee gives us.

 

One
day long ago we found a security hole in IE.  The
details of the hole are not important — but what’s interesting about it was the number
of things that had to go wrong to make the hole an actual vulnerability.  Basically
the problem was that one of the built-in IE objects had two dispatch interfaces, one
designed to be used from script and one for internal purposes.  The
for-script dispatch interface was the default, and it was designed to participate
in the IE security model.  The internal-only
interface did not enforce the IE security model, and in fact, there was a way to use
the object to extract the contents of a local disk file and send it out to the internet,
which is obviously badness. Furthermore, there was a way to make another object
return the non-default interface of the broken object. 

 

JScript
did not expose this vulnerability because it always uses the default dispatch even
when given a non-default dispatch.  But
the vulnerability was exposed by VBScript.  All
these flaws had to work together to produce one vulnerability.  Now,
when you find a security hole that consists of multiple small flaws, the way you fix
it is not to just patch
one thing and hope for the best
.  You patch
everything you possibly can
.  Remember,
secure software has defense in depth.  Make
the attackers have to do twelve impossible
things, not one impossible thing, because
sometimes you’re wrong about what’s impossible. 

 

Hence,
when we fixed this hole, we fixed everything.  We
made sure that the object’s persistence code could no longer read files off the local
disk.  In case there was still a way to
make it read the disk that the patch missed, we fixed the object model so that it
never returned a non-default interface to a script.  In
case there was still a way to do both those things that we missed, we
also turned off VBScript’s ability to use non-default dispatches.

 

That
last one turned out to be a huge mistake.  The
fact that I had believed that VBScript always
talked to the default dispatch
does not logically imply that every VBScript
user read my mind and knew that successfully using a non-default dispatch was some
sort of fluke
.  People naturally assume
that if they write a program and it works, then it works because the language designers
wanted them to be able to write that program!

 

As
it turned out, there were plenty of programs
out there in the wild that used VBScript to script non-default dispatch interfaces
returned by method calls, and I broke all of
them through trying to make IE safer.  We
ended up shipping out a new build of VBScript with the default dispatch feature turned
back on a couple of days later.  (Of course
all the fixes to IE were sufficient to mitigate the vulnerability on their own, and
those were not changed back.)

 

The
morals of the story are:

 

1)      If
you want to use a non-default dispatch, you have to use VBScript.

2)      There
is no way to make VBScript give you a specific non-default dispatch.  In
VBScript, once you have a dispatch, that’s the one you’re stuck with.

3)      Defense
in depth is a good idea, but it’s not such a good idea to go so deep that backwards
compatibility is broken if you can avoid it!

 

 

 

Comments (6)

  1. Anonymous says:

    I’d be interested if you had any information on using VBScript to call Managed objects in a similar manner to CreateObject. Is this even possible without first turning my .Net objects into COM objects?

  2. Hugh says:

    Did you consider "tearoff" dispatch interfaces – that is, using properties to QI?

  3. Eric Lippert says:

    Generally speaking there is no way to call a managed object from unmanaged code without creating a COM-Callable-Wrapper. That’s why we called them COM-Callable-Wrappers!

  4. Eric Lippert says:

    Objects are free to implement tearoff interfaces if they want. But there is no support in the VBScript language for casting a given dispatch object to a different interface.

  5. Anonymous says:

    There is of course the old trick of adding a property to the default interface that returns the second interface. Aside from introducing interdependencies where non should exist, it just doesn’t work if the object is out-of-process.

    To tell the truth, I never quit got the *need* for IDispatch. This interface, and an implementation for it, should have been automatically generated by the COM infrastructure, much like marshaling. And as with marshaling, only the 1% the do want to do weird stuff would implement it by hand.

    Oh, and then there is IDispatchEx. And, as I recall, that interface does not mix well with marshaling (universal marshaling that is).

  6. I’ve talked a few times in this blog about the semantics of the equality operators in various languages….