Written by Pete Corey on May 22, 2017.

Not long ago, the Meteor Development Group purchased Kadira after the Kadira team announced it would be shutting down. Shortly after, MDG open sourced the entire project on Github. 🎉

Being the curious developer that I am, I decided to sleuth through Kadira’s source looking for trouble.

I found several security issues which I reported to Meteor’s security team and were promptly fixed. Of those issues, the most notable was a NoSQL Injection vulnerability in Kadira’s user-facing kadira-ui Meteor application.

Let’s dive into the vulnerability and take a look at why it exists, how it could have been exploited, and how to prevent it.

Finding the Vulnerability

The best way to hit the ground running when looking for NoSQL Injection vulnerabilities in a Meteor application is to grep for calls to Meteor.methods, or Meteor.publish. This gives you a very quick idea of the entry points available to you as a user (or an attacker).

After finding all methods and publications, your next step is to peruse through every argument being passed into each method and publication and make sure that they’re being thoroughly checked.

In the kadira-ui project, I managed to find a Meteor method that wasn’t thoroughly checking its arguments. The "alerts.create" method was checking that the alertInfo argument matched Match.Any. Unfortunately, checking that an argument matches Match.Any is roughly equivalent to not checking it at all.

This is looking promising.

Tracing out the path of this method showed that alertInfo is passed into a function called setAppName. An appId field is pulled out of the alertInfo object and is ultimately passed directly into a MongoDB query:


var appId = alertsInfo.meta.appId;
var app = Apps.findOne(appId);

Passing an unchecked argument into a MongoDB query is the perfect recipe for a NoSQL Injection vulnerability.

Exploiting the Vulnerability

Because we, as intrepid explorers/malicious attackers, have complete control over the data passed into the Apps.findOne query, we’re presented with a few different choices in how we could exploit this vulnerability.

We could try to target a random application in the database by passing in a special MongoDB query operator when calling "alerts.create" from the client, like {_id: {$gte: ""}}:


Meteor.call("alerts.create", { meta: { appId: { _id: {$gte: ""} } } });

Or we could try something more impactful.

Because we have control over the entire query, we can pass in a $where query operator. The $where query operator is special in that it lets us pass in and execute raw Javascript in the MongoDB database process.

We can use this to our advantage to initiate a Denial of Service attack against the database and the Meteor application itself:


Meteor.call("alerts.create", {
    meta:{
        appId:{
            $where: "d = new Date; do {c = new Date;} while (c - d < 100000);"
        }
    }
});

In this example, we’re providing a query that will run a tight, unyielding loop for one hundred seconds per document in the Apps collection. It’s important to realize that this one hundred second time limit can be extended indefinitely or removed altogether.

We’ve essentially thrown the database into an infinite loop.

Pegging the CPU of the MongoDB host in this way drastically reduces the availability of the database and in many situations renders the Meteor application completely unusable.

With a single query, we’ve taken down the entire application.

Read more about the dangers of the $where query, and watch me exploit a similar vulnerability in my “NoSQL Injection in Modern Web Applications” talk given at CraterConf!

Fixing the Vulnerability

The fix for this specific instance of NoSQL Injection is to more thoroughly check the alertsInfo argument passed into the "alerts.create" method.

We’re expecting appId to be a String, so let’s check that it is:


check(alertInfo, Match.ObjectIncluding({
    meta: Match.ObjectIncluding({
        appId: String
    })
}));

If a malicious user provides anything other than a String (like a {$where: ...} query operator) in the appId field, our method will throw an exception.

Ideally, it would be better to fully flesh out the expected schema of alertInfo and avoid using Match.ObjectIncluding, but I’m not familiar enough with the application to make that change. In your application, you should check every field of every argument down to its primitive fields.

This change is enough to prevent a NoSQL Injection attack through the alertInfo.meta.appId field.

Even though Meteor isn’t actively maintaining their open sourced version of Kadira, I submitted a pull request to the project for posterity and to warn future users of the vulnerability.

Thanks to the Meteor Team

I’d like to give a huge shout out to the Meteor team for their speedy and professional response to this situation.

Nick Martin responded to my report within ten minutes and confirmed and patched my findings in their internal version of Kadira within twenty four hours.

Not only that, but they sent me a killer collection of swag!

I’d also like to thank MDG for swooping in during a time of crisis within the community, purchasing the Kadira platform, and open sourcing the entire product to the world. That act should be seen as no small gift to the Meteor community.

Inject Detect

If you’ve been following along for the past few months, you’ll know that I’m deep into the development of my security-focused project, Inject Detect.

Inject Detect is being developed to fight the exact problem I laid out in this article: NoSQL Injection. NoSQL Injection is an incredibly prevalent vulnerability in Meteor applications, and I’ve been fighting a war against it for years now.

Inject Detect is my newest and most powerful weapon to combat the threat of NoSQL Injection.

While Inject Detect is still under development, I’m fast approaching a releasable version. Be sure to sign up for the Inject Detect newsletter to stay up to date on its upcoming release!