Method Auditing Revisited

Written by Pete Corey on Feb 15, 2016.

In the past I’ve written about how a potentially malicious user can view all of the isometrically defined methods in your Meteor application. By inspecting the Meteor.connection._methodHandlers object on the client, they can see all client-side executable instances of your methods, which in most cases are identical to your server-side executable methods. This lets an attacker identify weaknesses in your application security that may lead to an attack.

Meteor.connection._methodHandlers only poses a potential problem for methods defined in a shared location. Methods defined in a server.js file, or within a server/ folder will never be shipped to the client, and won’t appear in the _methodHandlers object.

However, defining methods in server-only locations doesn’t mean your methods are free from prying eyes! Depending on how your application is structured, it may still be possible for an attacker to find and exploit these methods.

Source Snooping

Even if a method is defined in a server-only location, it’s still accessible from the client. For example, if you have a method defined in a server.js file:

Meteor.methods({
  hiddenMethod: function(argument) {
    // Do secret things...
  }
});

That method can still be called from the client:

Meteor.call("hiddenMethod", argument);

This means that one way of discovering hidden methods within a Meteor application is to simple search the bundled application source for Meteor method calls.

When the Meteor application is minified, the Meteor object is often transformed into some other variable name, so rather than searching for /Meteor\.call("/, it’s better to search for /\.call("/:

For example, in the above screenshot we can see a call to a Meteor method called "increasePostViews". This method is taking two arguments. I wonder if both of those arguments are being checked?

Watching the Wire

An alternative to searching through the source for method calls is to simple watch all of the DDP requests that are sent over the websocket connection. Any calls to Meteor methods will be clearly marked, along with a list of arguments being sent to this method.

As you go about using the application, you can build up a list of Meteor methods, some of which may not have a corresponding handler in the Meteor.connection._methodHandlers object.

In the above screenshot, we can see a call being made to the "upvotePost" method with a single arguments.

Well Kept Secrets

These techniques will only reveal hidden methods that are being called by client-side or shared code. It’s still possible that the Meteor application may have other, completely hidden methods. These methods may be defined on the server, and only called by other server-only code.

In that case, the only way for a curious client to discover those truly hidden methods is make a call to every possible Meteor method to determine if it exists on the server. Thankfully, this kind of brute forcing is totally unfeasible, and would most likely never be worth an attacker’s time.

At the end of the day, it shouldn’t matter whether attackers know methods exist. Even your most secret of methods should be made secure. Always assume that all of your methods will be called by all users, because they can be!

Preparing for the Crater Conference

Written by Pete Corey on Feb 8, 2016.

Are you as pumped about the Crater Remote Conference as I am? There’s an amazing lineup of speakers scheduled, and I can’t wait to hear from each of them.

I’m currently in the process of putting the finishing touches on my talk. I’m planning on speaking about NoSQL Injection in modern web applications, with a heavy focus on MongoDB. I’m planning on doing most of the talk as a hands-on demonstration where we’ll attack a Meteor eCommerce application.

If you don’t have tickets yet, go buy them now!

The threat of SQL injection in modern web applications has been left by the wayside with the rise of NoSQL databases. Unfortunately, a new, but fundamentally similar threat has taken its place: NoSQL injection. Let’s take an in-depth look at this type of attack and the steps we can take to protect ourselves from it. The king is dead, long live the king!

Sending Emails Through Hidden Methods

Written by Pete Corey on Feb 1, 2016.

I was recently asked to take a look at a friend’s Meteor application. I was particularly interested in the security of the system, so I started snooping.

One of the first things I do when sleuthing my way through a new application is to dig into the client-side Meteor.connection._methodHandlers object, looking for interesting methods to play with. In this application, there was nothing interesting to be found; just the standard insert, update, and remove methods generated for every MongoDB collection.

When no methods appear in the Meteor.connection._methodHandlers object, it means that the developer either didn’t write any Meteor methods, or they wrote their methods in a server-only location which prevents them from being shipped and made visible to the client.

Finding Hidden Methods

If you’ve read my post on auditing Meteor methods, you’ll remember that even though a method may be hidden from the client, the client is still able to invoke that method. This means that while we may not see any interesting methods defined in the Meteor.connection._methodHandlers object, we might be able to find interesting method calls being done by the client.

To find these method calls, I opened up the minified Javascript source of the application, and started searching through the code for /\.call("/. Very quickly, I started seeing calls to hidden Meteor methods.

Sending Emails

While scanning through these calls to hidden methods, one method call in particular caught my eye:

...
Meteor.call("sendEmail", this.getEmail(), "Welcome...");
...

It looked as if this method took an email address and a message as arguments, and sent that message to the provided email address. Without access to the source of the method, I didn’t know if the method was doing some kind of validation on the provided email address. The only way to find out was to test it out. In my browser console, I tried the following:

Meteor.call("sendEmail", "hello@petecorey.com", "Hi Pete!");

Sure enough, a few seconds later I received an email from the application owner with a message of "Hi Pete!". Uh oh.

With a little devious thinking, it’s not hard to imagine how this functionality could easily be abused by a potential hacker. Imagine someone leveraging your server and your SMTP account to send hundreds or thousands of spam emails. Or even worse, imagine an attacker impersonating the application or application owner and convincing users to click malicious links. This is a bad thing.

Locking It Down

The fix for this issue is fairly straightforward. Because any Meteor method can be called by any Meteor client, we simply shouldn’t have a method that sends arbitrary emails to arbitrary email addresses. Instead, we should take a step back and look at what we’re trying to do.

For example, if we’re attempting to send a welcome email to a user after they sign up for our application. A better way to handle this situation may be to hook into the user creation process and send the email there.

Or maybe an admin user may want the ability to send emails to users through some kind of admin panel. This could be implemented through a method that looks very similar to the sendEmail method we saw earlier, but with a few key differences. First, we would verify that the current user has the expected permissions to send the email. We would also verify that we’re sending the email to a user of the system, not an arbitrary email address:

sendEmail: function(userId) {
  var user = Meteor.users.findOne(userId);
  if (Roles.userIsInRole(this.userId, "admin") &&
      user && user.emails && user.emails[0].address) {
    Email.send(...);
  }
}

Final Thoughts

Isomorphism, or “universal code” is still a relatively new concept for many web developers. It can be difficult to cleanly divide server and client code in our minds when the distinction is anything but clear in the real world.

When writing Meteor applications, and Meteor methods in particular, it is incredibly important to always remember where your code can be run, and who can run it.