Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLIME is insecure and easily allows local attackers to execute arbitrary code #286

Closed
DemiMarie opened this issue Dec 18, 2015 · 39 comments
Closed

Comments

@DemiMarie
Copy link

@DemiMarie DemiMarie commented Dec 18, 2015

SLIME is extremely insecure and allows local attackers to easily execute arbitrary code by connecting to the Swank server process.

Fortunately, the fix is simple, at least on Unix: use a Unix domain socket (AF_UNIX) instead of a TCP socket on localhost. On Windows, use a simple, once-only secret key obtained from RtlGenRandom() (the basis of the better-known, and harder to use, CryptGenRandom()) to authenticate Emacs to the Common Lisp program.

The secret should be passed via a secure temporary file. Specifically, Emacs should set the file contents to the authentication key, and the Common Lisp implementation should overwrite the file with its port number. One MUST use a means of comparison that resists timing attacks (attackers can hammer localhost very quickly).

One could also use the Windows solution on Unix, with /dev/urandom as the random number source.

@luismbo

This comment has been minimized.

Copy link
Member

@luismbo luismbo commented Dec 18, 2015

I suppose the vast majority of SLIME users are on single-user machines. Definitely something we should do at some point, though. #270 is a first stab at implementing your AF_UNIX suggestion, but it might be a better idea to use the same solution on Windows and UNIX for better maintainability.

@DemiMarie

This comment has been minimized.

Copy link
Author

@DemiMarie DemiMarie commented Dec 23, 2015

I have come up with a plan of attack.

Part 1 is to enable use of Unix domain sockets. Much of this code can be taken from the Emacs server code within Emacs itself. This fixes the problems forever on must Unix systems, unless ABCL or LispWorks is used. ABCL, being a JVM language, does not support Unix domain sockets without a JNI library. I couldn't figure out how to change the LispWorks code since it uses undocumented functions and I don't have LispWorks.

Part 2 is securing transport over TCP sockets. My plan here is, essentially, to make the existing .slime-secret support much more secure. Instead of one .slime-secret file, there will be one .slime/secret_keys/pid file for each instance of Swank. Swank and Emacs will both check permissions on these files, which will contain both the password and the port number Swank is listening on. A password will be generated by Swank at server startup using a cryptographically secure random number generator, and will be 32 bytes long. The password will never be reused. Also, Swank and Emacs will both be hard-coded to listen to localhost — Swank has no business listening on a non-localhost network connection. Remote connections can be taken care of via SSH and socat.

@zellerin

This comment has been minimized.

Copy link
Contributor

@zellerin zellerin commented Mar 8, 2016

Just to have clear understanding of the security/convenience tradeoff, does proposed solution support these scenarios arouns slime-connect?

  • (possibly repeated) connection to a running Lisp process - I understand it would work for both solutions in previous article (but the "password never will be reused" part is not clear to me), not for solution in the first article (file overwritten at first use)
  • connection to a Lisp process running under different (service) user - I understand it should work if the socket priviledges are set up to allow other users (additional action), not work easily for secret files scenario
  • connection to a Lisp process running on a remote host, forwarding port via ssh, same user - I understand that it would not work at all with the AF_UNIX (does ssh support forwarding that?) and re-using the generated password files would be complicated
  • connection to a Lisp process running on a remote host, forwarding port via ssh, different (service) user - it should not work, and the fact that it does not work is in fact security feature

If the things above do not work or do not have a good alternative, please consider leaving "old" way as a (possibly opt-in) option. The .slime-secret would be apparently good enough for my use cases, except that it appears to be communicated as encoded and apparently anyone can shut down the communication by not following the protocol (I am not sure if #. can be also somehow sneaked in). Maybe I should investigate and file separate issue for this.

Disclaimer: I am not a slime maintainer, just a (possibly affected) happy user. So do not feel bound by my opinions.

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Oct 11, 2016

Note that this vulnerability is probably more severe than obvious: we just published a fix for this in Guile.

https://lists.gnu.org/archive/html/guile-user/2016-10/msg00007.html

Basically, using DNS rebinding attacks, you can even attack a "localhost-only" connection via users' web browsers. So yes, it should currently be possible to have users visit web pages that allow attackers to execute any lisp code on a user's machine.

I'd highly recommend adding support for unix domain sockets ASAP.

@dkochmanski

This comment has been minimized.

Copy link
Member

@dkochmanski dkochmanski commented Oct 11, 2016

Swank has no business listening on a non-localhost network connection. Remote connections can be taken care of via SSH and socat.

I think that there are a few uses for that though. I develop on remote Android ECL process from my Emacs on Linux box. Another scenario is debugging remote client application (never tried this one though - this should be secured obviously). Remote connections shouldn't be wiped out entirely (but I think that making it opt-in would be reasonable).

@attila-lendvai

This comment has been minimized.

Copy link

@attila-lendvai attila-lendvai commented Oct 11, 2016

to clarify the attack: if you have a swank listening on localhost, and you are browsing using a browser on the same machine, then a specially crafted website can get access to your swank.

from the linked email:

the presumption that "localhost" is only accessible by local users can't be guaranteed by modern operating system environments

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Oct 11, 2016

@attila-lendvai Yes that's exactly right.

I see that there's a pull request at #291 to add support for unix domain sockets. Glad to hear it... I think that's the right solution.

@DemiMarie

This comment has been minimized.

Copy link
Author

@DemiMarie DemiMarie commented Oct 11, 2016

I think that the ultimate solution is cryptographic authentication of all
connections, combined with using explicit IP addresses (as opposed to
hostnames). The problem is that GNU Emacs is not likely to get Windows
named pipe support anytime soon – and even then you need to authenticate
the server.

The solution is threefold:

  • Authenticate all communication
  • Use explicit loopback IP addresses
  • When remote access is needed, piggyback on existing remote login systems
    such as SSH

On Oct 11, 2016 5:41 PM, "Attila Lendvai" notifications@github.com wrote:

to clarify the attack: if you have a swank listening on localhost, and
you are browsing using a browser on the same machine, then a specially
crafted website can get access to your swank.

from the linked email:

the presumption that "localhost" is only accessible by local users can't
be guaranteed by modern operating system environments


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#286 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AGGWB9kxjimNmTki-Rr3W4K6JNwN-DT5ks5qzAKYgaJpZM4G4CVn
.

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Oct 12, 2016

Personally, if a Windows solution is not yet ready, I think that the right option is to make the :unix option the default with unix domain sockets and leave the old tcp code in place, signaling a warning. (You could likewise try to add a guard that closes HTTP connections when discovered, the way the Guile code has.) This way users have a secure option by default.

Currently users are known to just be left vulnerable. If there's a solution now, let's use it. If an authentication system, which needs to be figured out, becomes available in the future, great. But maybe let's worry about that then?

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Mar 17, 2017

Hi! It's been a while and I haven't seen any movement on this. Meanwhile, users are still vulnerable. I've considered using common lisp for some projects but have stayed away from it because of this issue for now. I still think doing the quick solution of using unix domain sockets is best for now; at least provide it as an option?

I'd love to help but I'm not familiar with SLIME's code at all, and am already swamped with other tasks. Sorry for the mostly-ping message... I just don't think this one should be dropped on the floor!

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Aug 13, 2017

For local slime sessions, I don't see an issue. When slime launches a process it connects to a random port almost immediately, and no new connections are accepted. So it's all theoretical.

Now, for remote sessions using an ssh tunnel the window when it's still listening is larger, but domain sockets won't help there.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Aug 13, 2017

And if you want to reduce the local issue from theoretical to impossible, create ~/.slime-secret with a password in it.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Aug 13, 2017

and .slime-secret can be used with remote connections as well.

@jasom

This comment has been minimized.

Copy link

@jasom jasom commented Aug 13, 2017

Can we make generating ~/.slime-secret from /dev/urandom the default behavior if it doesn't exist? That would make it reasonably secure by default.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Aug 13, 2017

That would work for local connections initiated by slime, but not for remote connections.

@jasom

This comment has been minimized.

Copy link

@jasom jasom commented Aug 13, 2017

But for remote connections, it will fail, hopefully with a message at least as helpful as: "mismatch in slime secret" and the user can either duplicate the secret between machines, or disable the use of it? As an example of other projects that have made the switch, X11 switched to requiring a secret by default at some point (since I recall when it was still using xhost based authentication)

@zellerin

This comment has been minimized.

Copy link
Contributor

@zellerin zellerin commented Aug 13, 2017

And if you want to reduce the local issue from theoretical to impossible, create ~/.slime-secret with a password in it.

... unless there is some other bug there (such as was #302). I am not sure that the code was written with security in really adverse conditions in mind.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Aug 13, 2017

Bugs should be fixed, nobody will argue with that.

@stassats stassats changed the title SLIME is extremely insecure and easily allows local attackers to execute arbitrary code SLIME is insecure and easily allows local attackers to execute arbitrary code Aug 13, 2017
@benkreuter

This comment has been minimized.

Copy link

@benkreuter benkreuter commented Nov 19, 2017

Using .slime-secret does not make an attack "impossible," it just makes it a bit more difficult. It only means that the attacker is forced to do a man-in-the-middle attack. Other than making unix domain sockets the default, the only real fix would be to use a protocol between the Lisp process and Emacs that authenticates every message cryptographically. It would have to authenticate that every message is received, that messages are received in the correct order, etc. In other words, it would probably be easier and more secure to just start using TLS than to try to design and implement something Slime-specific.

So can we please switch to unix sockets as the default, and at least reduce the scope of this bug to Windows (which I am sure has something similar, but I am not a Windows user or developer)?

@dkochmanski

This comment has been minimized.

Copy link
Member

@dkochmanski dkochmanski commented Nov 19, 2017

fwiw unless you accept connections from the outside world (slime accepts connections only from 127.0.0.1 by default) it would be rather hard to pull MITM attack.

@benkreuter

This comment has been minimized.

Copy link

@benkreuter benkreuter commented Nov 19, 2017

I do not think it is so hard: A local attacker could connect to the swank process first, then immediately start listening on the same port until emacs connects.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Nov 19, 2017

And domain sockets prevent local connections?

So can we please switch to unix sockets as the default, and at least reduce the scope of this bug to Windows (which I am sure has something similar, but I am not a Windows user or developer)?

And what about connections to remote servers?

We can switch to whatever you propose, ssl, domain sockets, but you'll have to provide the code.

@benkreuter

This comment has been minimized.

Copy link

@benkreuter benkreuter commented Nov 19, 2017

Unix domain sockets can be restricted to a particular user; the problem with TCP is that the attacker can be a different user. Unix sockets would also be more difficult for a web browser to connect to.

I am happy to help, but there seems to already be a pull request for unix domain sockets, so I am not sure what more I can do.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Nov 19, 2017

You have other users on your machine attacking you?

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Nov 19, 2017

Indeed there is #291 that's a lot of changes, beyond my attention span.

@benkreuter

This comment has been minimized.

Copy link

@benkreuter benkreuter commented Nov 19, 2017

I do not think other users on my machine are attacking me, but that does not mean that I should e.g. give all users on the machine read access to my SSH private keys. Also keep in mind that a user is not necessarily a person; users can also be accounts created for system services, some of which may be network facing and which are run as separate users to mitigate the risk of compromise (and there has been no shortage of bugs over the years).

Let's not be dismissive of security concerns, especially when the solution is not terribly difficult.

If the pull request is too large, I can pitch in and help with reviewing it, or I can help to break it down into more manageable changes. Just let me know what you need (feel free to email me if you would prefer not to add more comments to this bug).

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented Nov 19, 2017

I don't think I'll be handling this at all, unless it were a simple change changing a couple of functions. But something like that pull request goes beyond the amount of time I'm willing to allot to working on slime. I'm not against it, just no interest.

@Ambrevar

This comment has been minimized.

Copy link

@Ambrevar Ambrevar commented May 10, 2019

Any update on this?
If not, I'd be interested in working on #291 to make Unix sockets the default without disabling TCP. Would that work? @stassats ?

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented May 10, 2019

You are free to do anything, I just don't find this issue important.

@Ambrevar

This comment has been minimized.

Copy link

@Ambrevar Ambrevar commented May 10, 2019

To be sure I understood correctly: the above comment #286 (comment) explains that when starting a Swank server on a local Lisp process, the user is vulnerable to attacks when using a web browser on the side.

Is this correct? If so, it seems to be critical.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented May 10, 2019

It's overblown and theoretical, not critical. And won't be solved with unix domain sockets.

@Ambrevar

This comment has been minimized.

Copy link

@Ambrevar Ambrevar commented May 10, 2019

Why theoretical? It's quite easy for a website to send to localhost if I'm not mistaken.
Web browsers typically don't have access to Unix domain sockets.

@stassats

This comment has been minimized.

Copy link
Member

@stassats stassats commented May 10, 2019

I replaced this with #511 so that there are no alarmist comments talking over each other with the actual information spread out over the place.

@stassats stassats closed this May 10, 2019
@dacodas

This comment has been minimized.

Copy link
Contributor

@dacodas dacodas commented Jul 9, 2019

Perhaps an interesting first step would be to try using TLS by including some of the appropriate parameters to (proc (open-network-stream "SLIME Lisp" nil host port)) on line 1424 of slime.el

https://www.gnu.org/software/emacs/manual/html_node/elisp/Network.html

In this case, two possible improvements would be made, assuming a reverse proxy providing SSL termination to the swank server running on the remote Lisp.

  1. x509 authentication by specifying a client certificate signed by some trusted CA
  2. Encrypted traffic between the slime.el client and the reverse proxy server

Since the reverse proxy would provide SSL termination, I think this first step would require no changes to the swank server, just add support for passing parameters to the open-network-stream command.

I will try this out and submit a pull request, I think.

@dacodas

This comment has been minimized.

Copy link
Contributor

@dacodas dacodas commented Jul 9, 2019

I realize now that this doesn't address the problem raised here of not using Unix sockets and attackers sharing the same machine. All it really does is provide another form of authentication.

In any case, I've linked it here

#519

@jasom

This comment has been minimized.

Copy link

@jasom jasom commented Jul 10, 2019

@dacodas

For point-to-point security with symmetric keys, consider spiped which can establish a secure connection between two hosts. It even supports domain sockets, in the event that SLIME ever supports them.

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Jul 10, 2019

The attack isn't theoretical; the Guile post I linked to talks about the actual details. We demonstrated a real attack there and had to fix it.

Unix domain sockets aren't available to browsers. If they ever became so, it is true that this would no longer fix that problem. Since they aren't, I'm unsure why you think that's not a viable fix.

@mohe2015

This comment has been minimized.

Copy link

@mohe2015 mohe2015 commented Jul 10, 2019

@cwebber The guile post references a paper about an html form protocol attack. But according to my research and also #511

swank doesn't understand HTTP.

I checked that the following way:
server:

(load "$HOME/.roswell/lisp/slime/2019.05.21/swank-loader.lisp")
(swank-loader:init)
(swank:create-server :port 4005 :style :spawn :dont-close t)

client:

nc localhost 4005
GET / HTTP/1.1

and this closes the connection with the error that this can't be parsed as an integer. So I think slime should not be vulnerable.

But I still agree that using unix domain sockets where possible is a good idea.

@cwebber

This comment has been minimized.

Copy link

@cwebber cwebber commented Jul 10, 2019

Greatly appreciate the followup, @mohe2015! I assumed wank used a line-oriented protocol like Guile's did and admittedly did not particularly test. So it seems SLIME is probably not as at risk as I thought it was for HTTP, anyway. (There may be other protocols that also have clients with confused deputy problems, I don't know.)

I think it still would be important to add but I'll admit that this decreases the urgency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.