[HN Gopher] Unauthenticated Remote Code Execution in Erlang/OTP SSH
___________________________________________________________________
Unauthenticated Remote Code Execution in Erlang/OTP SSH
Author : kimi
Score : 142 points
Date : 2025-04-17 13:29 UTC (9 hours ago)
(HTM) web link (nvd.nist.gov)
(TXT) w3m dump (nvd.nist.gov)
| aftbit wrote:
| As I understand it, this is talking about an SSH server built
| into Erlang/OTP, not e.g. OpenSSH on a server with Erlang
| installed.
|
| >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 wrote:
| Yes - one of the many things that you can find in OTP is a
| programmable SSH/SCP client and server. The vulnerability is in
| the server component.
|
| See for example
| https://blog.differentpla.net/blog/2022/11/01/erlang-ssh/
| davidw wrote:
| Erlang, because of its architecture, has something of a habit
| of people rewriting various protocols in Erlang itself,
| rather than calling out to some C library.
|
| This has pros and cons.
| innocentoldguy wrote:
| This is probably because C NIFs run in the same process as
| the Erlang scheduler. If you have a long-running or
| blocking NIF, it can starve the scheduler and cause
| significant performance degradation across the system.
| natrys wrote:
| I think they now have "dirty" NIFs that use a separate
| scheduler for this.
| throwawaymaths wrote:
| yes, but there is a finite number of them, by default
| equal to the number of available cores. If your
| connection stays in c-land for too long you might run
| into trouble, if more than one connection are desired.
| hinkley wrote:
| I wonder if there's space for a libuv inspired solution
| now.
| toast0 wrote:
| libuv is more or less abstraction around an event loop
| for async i/o right?
|
| 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.
|
| [1] https://www.erlang.org/doc/apps/erts/erl_nif.html#eni
| f_selec... [2] https://www.erlang.org/doc/apps/erts/erl_d
| river.html#driver_...
| toast0 wrote:
| Writing protocol code in Erlang is nice, because the
| parsing is so easy and clear. And if you want to do
| something that's not so easy by spawning a command, then
| you may as well build it in Erlang. And it's fun and
| symmetric to build both a server and a client... I've not
| looked at OTP SSH code, but I'd assume the ciphering is
| still calls to external c libraries, as it is in the OTP
| TLS code.
|
| Of course, easy protocol parsing doesn't do the whole job;
| state management is required too (and was missed here,
| clearly).
| rollcat wrote:
| This is why I generally do not rely on SSH servers other than
| OpenSSH. It's (by far) the most widely deployed implementation,
| thoroughly battle-tested, etc. It's also hard to actually get
| pwned; the OpenBSD[1] guys believe in security as the default.
|
| 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 wrote:
| There's a huge difference here, historically that was because
| many C codebases were vulnerable due to inherent C flaws and
| ssh daemons due to their age was C based. OpenBSD folks
| stances on coding and system design avoids pitfalls.
|
| 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 wrote:
| I also have this principle, although I make an exception for
| https://tinyssh.org
| VWWHFSfQ wrote:
| OpenSSH has actually been "pwned" numerous times though. It's
| a very desirable target.
| rramadass wrote:
| For folks interested in the Security aspects of Erlang/BEAM
| languages the guidelines from _Security Working Group of the
| Erlang Ecosystem Foundation_ are a good resource -
| https://security.erlef.org/ and https://erlef.org/wg/security
| formerly_proven wrote:
| If I interpret the patch correctly the issue seems to be that you
| could just ask for a channel and do a request_exec before
| authenticating. The regression test is: {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.
| phoe-krk wrote:
| CWE-306, Missing Authentication for Critical Function, linked
| in the report seems to suggest the same. The score of 10.0 is
| damn spicy, too - you just ask the server to execute something
| for you, and it does so, no questions asked.
| tialaramex wrote:
| Design bug here: Clearly we need to run code _as_ somebody,
| so, there 's no reason to have infrastructure which just
| executes user code with the current context or server
| (presumably? or maybe an actual zero, ie root) context.
|
| 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.
| toast0 wrote:
| Afaik, if you were running the Erlang SSH daemon, when you
| connect as an authorized user (or just ask!), it drops you
| into the Erlang debug shell. There's no concept of
| different users in the debug shell. Erlang dist is kind of
| an almost single system image cluster, with no security
| boundaries once you're connected to the cluster. (Well, not
| exactly no boundaries, you can mark a process sensitive,
| and ets tables private, and do some other things to make
| introspection difficult [1], but you can likely still spawn
| an os process debugger and get to everything; you would
| need to modify the OTP sources to disable os process
| spawning)
|
| 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...
| chc4 wrote:
| I'm vaguely surprised that https://www.runzero.com/sshamble/
| didn't find this. They did a scan over the entire internet
| trying invalid SSH state machine transitions, which I guess
| didn't cover this sequence.
| hdmoore wrote:
| I was too! The reason is that the Go x/crypto/ssh library was
| bailing out on the lack of reply to the channel open request,
| which prevented it from reaching the auth bypass check via
| exec. I should have an update out soon with this fixed and a
| RCE check for this issue.
|
| 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
| ziddoap wrote:
| > _RUB_
|
| 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.
| password4321 wrote:
| > _The issue is caused by a flaw in the SSH protocol message
| handling which allows an attacker to send connection protocol
| messages prior to authentication._
|
| per https://www.openwall.com/lists/oss-security/2025/04/16/2
| throwawaymaths wrote:
| most Elixir deployments are probably unaffected (obviously,
| please please check to be sure), as SSH is turned off by default.
|
| https://paraxial.io/blog/erlang-ssh
| giraffe_lady wrote:
| This is also true of erlang right? This module is part of the
| stdlib but if you haven't implemented access using it it's
| "turned off" in the same way any other libraries you aren't
| using are.
| __jonas wrote:
| I'm assuming the most likely affected Elixir projects would be
| those using Nerves with SSH enabled and exposed to the public
| internet, as nerves_ssh wraps the OTP SSH daemon.
|
| 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.
| qwertox wrote:
| How does this affect servers like ejabberd? I just noticed that
| they upgraded their server yesterday [0] and am wondering if it
| could contain some kind of fix for this, or would this be
| unrelated?
|
| [0] https://github.com/processone/ejabberd/releases
| toast0 wrote:
| ejabberd doesn't start the Erlang SSH daemon; or at least
| codesearch on their github doesn't have any reference to ssh
| other than something unrelated trying to figure out if a url is
| a git repo.
|
| 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.
| codetrotter wrote:
| > 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?
| toast0 wrote:
| Yes, the Erlang debug shell is just the default option; you
| can replace the interface with whatever, if you wanted to
| build a chat/mud/ascii cinema/etc, it should all be
| possible (but you'd be advised to review the OTP ssh code
| to confirm it does what you want and nothing extra)
| bilekas wrote:
| There's something really strange and upsetting reading this on an
| archive site that wont be around for much longer..
| WillPostForFood wrote:
| It isn't going away!
| aposm wrote:
| Oops..... we are currently trying to sell an elixir-based
| greenfield project internally. This doesn't affect elixir by
| default as other commenters pointed out, but still might make our
| project a bit harder to pitch to management...
| jerf wrote:
| If your organization is looking for "the language ecosystem
| that never has any security vulnerabilities", pack it in and
| close up shop because you're not going to find one. How many,
| how often, and how they are handled is far more important.
|
| 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.
| toast0 wrote:
| > overall the Erlang/Elixir ecosystem is very strong and
| lacks most of the footguns like an "eval" statement that get
| people
|
| 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
| joshribakoff wrote:
| I've seen more horrendous code using macros in elixir even
| despite by brief foray than I have seen ever in decades of
| working in languages with eval. Like using them when normal
| functions would suffice.
| MisterTea wrote:
| > most obvious way to run a shell command [1]
|
| I think you forgot a link to your [1] reference.
| Hikikomori wrote:
| Just create your own.
| victorbjorklund wrote:
| Honestly, no language is totally safe.
| r3tr0 wrote:
| you could probably write a custom XDP program to parse and check
| for this payload using a tool like yeet and XDP_DROP it.
|
| https://yeet.cx
|
| you can try our sandbox at https://yeet.cx/play
| marioflach wrote:
| I wrote a GitHub ,,clone" a while ago. Implementing Git's wire
| and transfer protocol directly in Elixir.
|
| 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...
___________________________________________________________________
(page generated 2025-04-17 23:00 UTC)