Closed Bug 1145337 Opened 7 years ago Closed 2 years ago

Avoid storing constant pointers in JS objects

Categories

(Core :: JavaScript Engine, defect)

defect
Not set
normal

Tracking

()

RESOLVED WONTFIX

People

(Reporter: mccr8, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: sec-want, Whiteboard: moderate understates the importance -- neat trick for turning a vuln into an exploit)

In addition to the security.turn_off_all_security_so_that_viruses_can_take_over_this_computer pref, another thing that has shown up in Pwn2Own exploits both last year and this year is this technique (from the writeup in bug 1145255): "Now to break ASLR. The exploit basically reads Uint32Array->type_- >clasp, which points into static TypedArrayObject::classes[]. The classes array lies in the data section of xul.dll, so the location xul.dll is found"

Is there some way we can avoid storing pointers at known offsets in xul.dll in JS objects?  I don't know how common this is.  I'm also not sure how you'd do regression testing of this.
We want to get rid of JSClass as a concept at some point, which would get rid of the pointer, certainly.  But it's going to be the case that for some objects, we want something approximating a C++ vtable of function pointers in the object -- for the meta-object protocol.  Getting rid of JSClass would not get rid of that.  So we'd still have the same thing, as far as the concern here goes.

Past that, any function that's implemented purely in C++ is going to have a function pointer inside it.  That's equally good for offset-determination, isn't it?  The only solution to that would be to self-host *every* exposed JS function in the entire standard library, and where C++ was truly unavoidable have the self-hosted method effectively as a wrapper.  While not impossible, that seems pretty horrendous, at least with our current self-hosting setup.

I don't have good ideas here.
> *every* exposed JS function in the entire standard library

Which includes the DOM, note.  And the self-hosting setup is not just horrendous from the POV of the DOM, but downright hostile.  :(
(In reply to Andrew McCreight [:mccr8] from comment #0)
> Is there some way we can avoid storing pointers at known offsets in xul.dll
> in JS objects?  I don't know how common this is.  I'm also not sure how
> you'd do regression testing of this.

One way to prevent finding any magic number (added by an attacker), would be to mix the reads/writes with a unknown number which depends on the container / runtime.

One example is to take the address of the container, thus accessing an array would be done as:

  x = arr[i] ^ arr;
  arr[i] = x ^ arr;

Note that using the address is not effective as the pointer alignment give the ability to an attacker to match the low bits of any value, which means he could make larger data structures to ensure that he has the right content.

On the other hand, what we might be able to do, it to make a static variable which contains a random number, and we use this random number to symmetrically cipher any address to a static pointer from Gecko.

This does not prevent an attacker from reading content from other compartments, but this would restrict an attacker to the only option of reading the stack to find the address of the libxul.

static const uintptr_t key = random(...);
class StaticPointer<T> {
  uintptr_t c;
  StaticPointer(T* p) : c(reinterpret_cast<uintptr_t>(p) ^ key)
  T &operator *() { return reinterpret_cast<T*>(p ^ key); }
}

Still, this does not prevent any attack which might be based on static pointers of other libraries.

And if it is possible to modify the content of other compartment, then an attacker might just force the main thread to execute some JS code which compromise the computer.
I wonder if Chrome does anything to mitigate this kind of thing.
Whiteboard: moderate understates the importance -- neat trick for turning a vuln into an exploit
(In reply to Jeff Walden [:Waldo] (remove +bmo to email) from comment #1)
> Past that, any function that's implemented purely in C++ is going to have a
> function pointer inside it.  That's equally good for offset-determination,
> isn't it?

Also, once you have an object you can trivially access its compartment (object->group->compartment, or via the shape). From the compartment you can get the runtime, and the runtime has all sorts of function pointers in it. We'd probably need a static analysis to find all these places.

But even then, once you have access to the compartment/runtime there are a ton of other things you can do with the GC heap, compartment security bits etc...
I believe ASLR is generally considered lost when the attacker has a read-anywhere to go along with their write or jump. JS objects are just the closest things around. Suggest WONTFIX+public.
Blocks: 806034
Group: core-security → javascript-core-security
While I'm always up for thinking more about something fancy that could be done to make finding offsets more difficult, I think it would wind up being much more difficult than simply trying to stamp down on this sort of thing. I think a comprehensive solution is more along the lines of XOM.
We have native functions, js::Class*, proxy handlers, compartment/runtime callbacks, empty elements, external string finalizers that are all trivially reachable from JS objects/strings. We could scramble all of them (XOR with a random value) but without some sort of static analysis we'll definitely miss stuff.

Also, I think consensus these days is that post-site-isolation, we will give up on ASLR (thanks to Spectre and similar attacks), so the question is how much time/effort we want to invest in this now.

dveditz, if you agree with comments 6-8, please open this bug.

Status: NEW → RESOLVED
Closed: 2 years ago
Flags: needinfo?(dveditz)
Resolution: --- → WONTFIX
Group: javascript-core-security
Flags: needinfo?(dveditz)
Keywords: sec-moderate
You need to log in before you can comment on or make changes to this bug.