Mobility4 Public Safety Flashcards
AWS Identity and Access Management (IAM)
provides fine-grained access control across all of AWS.
With IAM, you can specify who can access which services and resources, and under which conditions. With IAM policies, you manage permissions to your workforce and systems to ensure least-privilege permissions.
IAM Use cases
With IAM, you can manage AWS permissions for workforce users and workloads. For workforce users, we recommend that you use AWS Single Sign-On (AWS SSO) to manage access to AWS accounts and permissions within those accounts. AWS SSO makes it easier to provision and manage IAM roles and policies across your AWS organization. For workload permissions, use IAM roles and policies, and grant only the required access for your workloads.
Attribute-based access control (ABAC)
is an authorization strategy for creating fine-grained permissions based on user attributes, such as department, job role, and team name. With ABAC, you can reduce the number of distinct permissions you need for creating fine-grained controls in your AWS account.
Amazon CloudWatch
MongoDB
What is RocketChat?
Rocket. Chat is a communication hub that facilitates team collaboration and organizes conversations.
It allows its users to choose how they will communicate, either by chat, video conference, audio messaging, file sharing or other omnichannel tools.
Rocket.Chat is one of the most popular open source solutions for team communication, written in JavaScript and TypeScript.
Rocket Chat - CVE-2019-17220
XSS 2019-10-21 2019-10-23 4.3 None Remote Medium Not required None Partial None Rocket.Chat before 2.1.0 allows XSS via a URL on a ![title] line.
What are the 3 vulnerabilities?
But when they are misused, abused, or otherwise implemented incorrectly—or just ignored—they become application vulnerabilities. Three of these vulnerabilities point to a basic lack of good housekeeping: Missing Authentication, Missing Authorization, and Missing Encryption.
Is Rocket chat end to end encrypted?
You can use Rocket. Chat’s end-to-end encryption feature for a chat and Jitsi’s peer 2 peer or video bridge encryption for your video /audio calls. By doing so, you guarantee a reliable and robust group video chat, audio chat, and screen sharing experience. For even more control, you can run a private Jitsi server.
Impact
We discovered critical vulnerabilities in its source code that could have been used by an attacker to take complete control over a server, starting with as little as any user’s email address.
During the analysis of Rocket.Chat 3.12.1 we found two NoSQL Injection vulnerabilities.
These can allow attackers to escalate their privileges, to execute arbitrary system commands on the host server, and to steal confidential user data and chat messages. Both vulnerabilities are fixed in version 3.13.2 and backported to older branches in versions 3.12.4 and 3.11.4.
To attack a Rocket.Chat instance, an attacker either needs an account or has to know the email address of any user that has 2-factor authentication (2FA) disabled. Some open source communities use public Rocket.Chat instances with open registration, which would be vulnerable. In other scenarios it can be easy to guess or find email addresses of users.
Technical Details
We found two NoSQL Injection vulnerabilities in two separate components. Each one can be used on its own to take over an admin account but they use different injection approaches, making it interesting to see both. Combining them into a chain makes an attack less likely to be detected.
MongoDB Injection Primer
MongoDB is a popular document-oriented database and falls into the category of NoSQL databases. It consists of collections and documents, which are the respective equivalents of tables and rows in a relational database. Each document has a JSON-like structure with keys and values on multiple hierarchical levels. A document that represents a user could look like this:
Queries also have such a JSON-like structure and describe which fields of a document have to have certain values in order to be contained in the result set. The query supports literal values but also operators. There are field-level operators that can be used to e.g. specify a numeric range, and there are top-level operators that can be used to build more complex queries. A query that returns all users that are over 18 years old and have the admin role would look like this:
A classic injection in this scenario occurs when a program expects a certain user-provided value to be a string, but it can also be an object.
This happens often when user input comes in JSON format. In such a case, an attacker can for example bypass a login by specifying an object as the password parameter which contains an operator expression that is always true, like {“$ne”:1}.
When exploiting SQL Injections, joins and subqueries are often used to leak data from different tables. There are equivalents of this in MongoDB, but they cannot be used in every scenario. Attackers have to get creative when they find a NoSQL Injection, because it usually does not give them the same capabilities that an SQL Injection would.
NoSQL Injection #1: Taking Over a Regular User
During our research we managed to execute arbitrary code on the server, but we had to take small steps to get there. The first step for an attacker is to take over an unprivileged user account. One of the NoSQL Injections can be used without authentication, but it requires us to know the email address of a user that does not have two-factor authentication (2FA) enabled.
The first vulnerability is CVE-2021-22911: a Blind NoSQL Injection that allows to leak a user’s password reset token. The vulnerable part of the code is located in the getPasswordPolicy() method. This method can be called without being authenticated, which makes sense because the frontend needs to know the password policy when users are registering. Its parameter params is coming from a user-controlled JSON value but is not validated in any way:
server/methods/getPasswordPolicy.js
7 getPasswordPolicy(params) { 8 const user = Users.findOne({ 'services.password.reset.token': params.token }); 9 if (!user && !Meteor.userId()) { 10 throw new Meteor.Error('error-invalid-user', 'Invalid user', { 11 method: 'getPasswordPolicy', 12 }); 13 } 14 return passwordPolicy.getPasswordPolicy(); 15 }
The Users.findOne() method in line 8 queries the users collection with the provided query object and returns the first match. Since an attacker can provide params.token as an object, they can use MongoDB’s $regex operator to check if a token begins with a certain character. To check if a token begins with an uppercase A, the query would look like this:
Users.findOne({ 'services.password.reset.token': { $regex: '^A' } });
This can be used to create an oracle that can tell whether a token begins with a certain sequence of characters or not. When the query matches a user, the server’s password policy is returned, but when it does not return any result the method returns an error. An attacker can repeatedly make guesses and observe the oracle’s response to see if our guess was correct, until the whole token is known.
Exploitation of this vulnerability works like this: an attacker requests a password reset for a user using their email address, uses the oracle to leak the newly created token, and finally uses that token to change the user’s password. This enables access to more attack surface because authenticated users, while having no special privileges, can use a lot more of Rocket.Chat’s features.
NoSQL Injection #2: Elevating Privileges
Taking over a user account with the previously described NoSQL injection was noisy: the user got a password reset email, was logged out, and cannot log in because the password was changed. If that happens to an admin they would likely investigate and detect the attack. Also, admin accounts are probably more likely to be protected by two-factor authentication (2FA).
So in order to elevate privileges, an attacker can use a second vulnerability: Rocket.Chat Security Issue 0025. It requires authentication, but has more impact: it can not only be used to leak a user’s password reset token, but any field of any user in the database. Here is how it works:
The users.list API endpoint takes a query parameter from the URL which is then used to query the users collection. Documents in that collection contain fields that should not be accessible by everyone, which is why the query is filtered by using a blocklist that removes certain fields from the query and the result.
app/api/server/v1/users.js
223 API.v1.addRoute(‘users.list’, { authRequired: true }, {
224 get() {
… // …
230 const { sort, fields, query } = this.parseJsonQuery();
232 const users = Users.find(query, { /* … */}).fetch();
239 return API.v1.success({
240 users,
… // …
244 });
245 },
246 });
The filtering only considers actual fields that could be queried, but not top level MongoDB operators. The general drawback of using a blocklist approach for validation is that it is easy to miss something. Using an allowlist to explicitly permit known values is more effective at preventing such issues.
To bypass the filter, the $where top-level operator can be used which takes a JavaScript expression and executes it for each document in a collection to decide if the document should be contained in the result set or not.
This sounds like Remote Code Execution (RCE) but the code is executed inside the MongoDB process and is very restricted, it can only access the fields of the current document and there are no APIs that allow interaction with the outside world. But it still allows for more flexibility when exploiting this injection.
At first we thought this would be another case of a Blind NoSQL Injection where we would make incremental guesses to observe responses, because the result set is always stripped of any sensitive fields. But then we realized that we could leak values by throwing an error inside the $where operator’s JavaScript expression! The error is then passed back to the user in the API response with the full error message. An example of this is the following query that leaks an admin user’s secret:
{“$where”:”this.username===’admin’ && (()=>{ throw this.secret })()”}
The API response would then include the secret:
{
“success”: false,
“error”: “uncaught exception: aHR0cHM6Ly9iaXQubHkvM3VQclgwUA==”
}
With this technique, an attacker can find an admin account and leak their email, password hash, and 2FA secret. They then request a password reset, leak the reset token, and perform the reset just like before. After that, the attacker can log in with the new password and the 2FA codes that can be generated with the secret. After achieving RCE (which we will cover in the next section) the attacker can restore the admin’s original password hash so that the admin can still log in and is less likely to notice the attack.
From Admin to Remote Code Execution
At this point, the attacker has access to an admin user, which already has a huge impact. To determine the severity of this we wanted to know if it is possible to gain Remote Code Execution capabilities, so we spent a little more time researching.
Rocket.Chat has a feature called Integrations that allows creating incoming and outgoing web hooks. These web hooks can have scripts associated with them that are executed when the web hook is triggered. They are run using the vm module of Node.js which might sound safe to use but is explicitly declared to not be a security mechanism. A script that runs inside a VM context has no access to system resources per default, but there are easy ways to break out.
To escape a VM context, the attacker has to get access to objects from the parent context. In this case there are multiple objects and functions passed to the script as arguments which can be used to access the parent context’s function constructor to create a new function. Any functions created with that constructor will inherit its context, regardless of the context they are executed in.
In order to execute system commands on the server the attacker creates a script that will get the require() function of the parent context and uses it to load the child_process module which contains an exec() function:
payload.js:
1 const require = console.log.constructor('return process.mainModule.require')(); 2 const { exec } = require('child_process'); 3 exec('echo pwned > /tmp/proof.txt');
This concludes the exploit chain, starting with just the email address of a regular user, ending with the capabilities to execute arbitrary commands on the server. It shows the dangers of NoSQL Injection vulnerabilities and how important it is to validate all user inputs. SonarQube and SonarCloud can help you to identify different types of injection vulnerabilities in your code automatically.