Tech Editorials #Advisory #CVE #Vulnerability

Sandstorm Security Review

Shaolin 2018-01-26

Sandstorm Security Review (English Version)
一次在 Sandstorm 跳脫沙箱的滲透經驗 (中文版本)

Introduction

In early 2017, we had a pentesting target protected with Sandstorm. Sandstorm is a web-based platform which allows users to install their web apps, such as WordPress, GitLab, etc. The main feature of Sandstorm is that it containerizes every app in its own sandbox. Therefore, even though we had found several vulnerabilities of the apps, we still could not put a threat to the server.

In order to leverage the vulnerabilities, we put part of efforts into review of Sandstorm’s source codes, and tried to escape the sandbox to impact the whole server. Finally, we found a number of uncommon and interesting vulnerabilities, and received CVE IDs as follows:

  • CVE-2017-6198 (Denial of Service)
  • CVE-2017-6199 (Bypassing Authorization Schema)
  • CVE-2017-6200 (Insecure Direct Object References)
  • CVE-2017-6201 (Server-Side Request Forgery)

Exploitation Details

CVE-2017-6198

This is a DoS created by system resource exhaustion. The root cause is that Sandstorm does not have a comprehensive policy to limit the amount of resource used by every apps run on it. In src/sandstorm/supervisor.c++ only the maximum number of files opened by each process was limited. See the codes below:

void SupervisorMain::setResourceLimits() {
  struct rlimit limit;
  memset(&limit, 0, sizeof(limit));
  limit.rlim_cur = 1024;
  limit.rlim_max = 4096;
  KJ_SYSCALL(setrlimit(RLIMIT_NOFILE, &limit));
}

Ref: https://github.com/sandstorm-io/sandstorm/blob/v0.202/src/sandstorm/supervisor.c++#L824

Since supervisor does not restrict the amount of subprocesses and storage usage, attackers can raise a resource exhaustion attack to crash the server by simply uploading a malicious app which keeps calling fork() (aka the “fork bomb”) or consumes huge storage space.

CVE-2017-6199

Usually Sandstorm will designate unique permissions to the specific members of a certain organization, and the default membership validation method is to check user’s email address and see whether the string after @ exists in their white list. See the codes below:

if (identity.services.email.email.toLowerCase().split("@").pop() === emailDomain) {
    return true;
}

Ref: https://github.com/sandstorm-io/sandstorm/blob/v0.202/shell/packages/sandstorm-db/db.js#L1112

Therefore, when an attacker fills in an email like [email protected],[email protected] and the system will automatically consider the attacker a member of the aaa.bbb organization.

Another key factor that contributes to the successful attack lies in one of the features when users log on Sandstorm. Users does not need to set up passwords for Sandstorm. Each time when the users need to log onto the service, they only need to fill in their email address, and they’ll receive a set of random unique password for login. The reason why the example above works is because the system treats [email protected],[email protected] as a user from aaa.bbb domain, and the random password will be sent to the two email addresses, [email protected] and [email protected] As long as one can receive the password, they can log in to use the service.

Below is a quick demonstration:

  1. On Sandstorm, restrict access to users from domain aaa.bbb only.

  2. On login page, fill in [email protected],[email protected] for the email field. (Note: at the front end, the email field is checked with HTML5 validation, but it is not further checked for validity at the back end)

  3. Retrieve random password in [email protected] mailbox.

  4. Login successful. [email protected],[email protected] is considered as a user and member of aaa.bbb organization!

In our pentesting, the target website allowed users from validated domains to install their own apps. Therefore, through this bypass exploit, further attacks could be accomplished by combining other vulnerabilities described in this blog post (CVE-2017-6198, CVE-2017-6200, CVE-2017-6201).

CVE-2017-6200

This is an interesting vulnerability. Totally two little validation flaws were exploited to initiate this attack! On Sandstorm, owners of each Grain (Sandstorm container, in short, an app sandbox) can download their backup data for the app. But because of the two vulnerabilities in the packing process, an attacker can pack the files under the /etc and /run directories located on the server outside the sandbox. The security issues were as follows:

  1. The packing process has hid /var, /proc, /etc and other sensitive directories, but did not hide /etc.host and /run.host these two directories. These directories are the aliases for the directories /etc and /run on the server respectively, which are relatively newer features.

  2. The system will pack the legitimate files, have them sorted out, and create zip packages through the standard input interface. The separation between files are determined by line-breaks (\n). As a result, when a line-break string appears in the file name, illegal path file names can be injected and packed with zip. Although the app checks whether there is a line-break in the file name, but the directory name was not checked.

Ref: https://github.com/sandstorm-io/sandstorm/blob/v0.202/src/sandstorm/backup.c%2B%2B#L271

By using these two vulnerabilities together, the attacker simply has to create a directory in the sandbox /var/exp\n/etc.host/passwd\n , then backup files containing /etc/passwd on the server can be retrieved through backup downloading function.

Screenshot of a real-world scenario:

  1. First, create a new directory in Grain /var/exp\n/etc.host/passwd\n, and use the Grain Backup function to download the backup file.

  2. After unzipping the backup file, from etc.host we’ll see /etc/passwd of the server outside the sandbox.

CVE-2017-6201

This is a classic SSRF (Server-Side Request Forgery) issue. Sandstorm allow installation of apps from arbitrary sources, and an attacker can simply let the server access a certain location by providing an installation URL. The problem was identified on https://[target]/install/xxxChangeItEveryTimexxx?url=http://127.0.0.1:22/ This sample link confirms whether the server’s port 22 is open.

(Parse Error, which implies server’s port 22 is open)

Follow-up Updates

After we reported the vulnerabilities, Sandstorm fixed it immediately and then published an article: https://sandstorm.io/news/2017-03-02-security-review

Through this pentesting experience, we consider Sandstorm a safe platform with outstanding security mechanisms. This is mainly attributed to its fundamental design rationale: to assume that every app installed is malicious. With this vigilant assumption, Sandstorm’s defence mechanisms for the core system become comprehensive and watertight. Apart from the server-side protection, some common client-side attacks (such as XSS, CSRF) are handled properly by Sandstorm’s unique countermeasures, such as host name randomization. That is, it is very difficult for attackers to sabotage the server by simply manipulating the apps, and so does privilege escalation through attacking at the client-side.

Nevertheless, such an impressive platform still had some minor mistakes which led to security issues. Most of the vulnerabilities found this time are improper usages of libraries or negligence of existing defence architecture while introducing new features. These types of vulnerability are also common in our other projects. We would like to take the opportunity to remind developers, always present a comprehensive security review especially when developing new features to avoid vulnerabilities caused by the gaps between defence mechanisms.