Impact
The do_accept function in erts/epmd/src/epmd_srv.c calls epmd_cleanup_exit() when accept(2) returns EMFILE (per-process file descriptor limit reached) or ENFILE (system-wide file descriptor limit reached), rather than treating these as recoverable conditions. An attacker can exhaust epmd's file descriptor slots by holding many TCP connections open, periodically sending a single byte per connection to reset the idle timeout, and then triggering accept(2) to return EMFILE, which kills the daemon. epmd has no per-source-IP connection cap, making the attack feasible from a single source.
This is normally not an issue as epmd should never be exposed on untrusted networks. However, the Debian/Ubuntu packages set it up by default as a systemd service listening on all interfaces. Furthermore, the systemd unit inherits a low file descriptor soft limit, and repeated daemon deaths trigger systemd's start-rate-limit, permanently failing both epmd.service and epmd.socket and requiring manual operator intervention to recover.
Workarounds
- Restrict epmd to loopback only by setting
ListenStream=127.0.0.1:4369 and ListenStream=[::1]:4369 in a systemd socket override (/etc/systemd/system/epmd.socket.d/override.conf), clearing the default wildcard binding first with an empty ListenStream=.
- On systemd-managed systems, raise the file descriptor limit and configure automatic restart by adding
LimitNOFILE=65536 and Restart=always with a suitable RestartSec in /etc/systemd/system/epmd.service.d/override.conf.
- Restrict network access to TCP port 4369 via firewall rules to trusted hosts only.
Affected/Unaffected Versions
A version larger than or equal to one of the listed patched versions is unaffected; otherwise, a version that satisfies an expression listed under affected versions is affected, and if it does not, it is unaffected.
The documentation of the new OTP version scheme describes how versions should be compared. Note that versions used prior to OTP 17.0, when the new OTP version scheme was introduced, are never listed since it is not well defined how to compare those versions.
In the case of this vulnerability, it is known to have been present since at least OTP R11.
Credits
Thanks to Ryan Moore for finding and responsibly disclosing this vulnerability to the Erlang/OTP project.
Impact
The
do_acceptfunction inerts/epmd/src/epmd_srv.ccallsepmd_cleanup_exit()whenaccept(2)returnsEMFILE(per-process file descriptor limit reached) orENFILE(system-wide file descriptor limit reached), rather than treating these as recoverable conditions. An attacker can exhaust epmd's file descriptor slots by holding many TCP connections open, periodically sending a single byte per connection to reset the idle timeout, and then triggeringaccept(2)to returnEMFILE, which kills the daemon. epmd has no per-source-IP connection cap, making the attack feasible from a single source.This is normally not an issue as
epmdshould never be exposed on untrusted networks. However, the Debian/Ubuntu packages set it up by default as asystemdservice listening on all interfaces. Furthermore, thesystemdunit inherits a low file descriptor soft limit, and repeated daemon deaths trigger systemd's start-rate-limit, permanently failing bothepmd.serviceandepmd.socketand requiring manual operator intervention to recover.Workarounds
ListenStream=127.0.0.1:4369andListenStream=[::1]:4369in a systemd socket override (/etc/systemd/system/epmd.socket.d/override.conf), clearing the default wildcard binding first with an emptyListenStream=.LimitNOFILE=65536andRestart=alwayswith a suitableRestartSecin/etc/systemd/system/epmd.service.d/override.conf.Affected/Unaffected Versions
A version larger than or equal to one of the listed patched versions is unaffected; otherwise, a version that satisfies an expression listed under affected versions is affected, and if it does not, it is unaffected.
The documentation of the new OTP version scheme describes how versions should be compared. Note that versions used prior to OTP 17.0, when the new OTP version scheme was introduced, are never listed since it is not well defined how to compare those versions.
In the case of this vulnerability, it is known to have been present since at least OTP R11.
Credits
Thanks to Ryan Moore for finding and responsibly disclosing this vulnerability to the Erlang/OTP project.