diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb
new file mode 100644
index 000000000..2bc4bf842
Binary files /dev/null and b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb differ
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/start_ssh.escript b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/start_ssh.escript
new file mode 100644
index 000000000..ee42662f1
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/files/start_ssh.escript
@@ -0,0 +1,31 @@
+#!/usr/bin/env escript
+%%! -sname ssh_runner -noinput
+
+main([PortStr]) ->
+ Port = list_to_integer(PortStr),
+
+ io:format("Starting Erlang SSH daemon on port ~p~n", [Port]),
+
+ case application:ensure_all_started(ssh) of
+ {ok, _StartedApps} ->
+ ok;
+ {error, StartReason} ->
+ io:format("Failed to start ssh application dependencies: ~p~n", [StartReason]),
+ halt(1)
+ end,
+
+ KeyDir = "/opt/erlang_ssh/ssh_keys",
+
+ case ssh:daemon(Port, [
+ {system_dir, KeyDir},
+ {idle_time, infinity}
+ ]) of
+ {ok, Pid} ->
+ io:format("SSH daemon started successfully on port ~p, pid: ~p~n", [Port, Pid]),
+ receive
+ stop -> ok
+ end;
+ {error, DaemonReason} ->
+ io:format("Failed to start SSH daemon: ~p~n", [DaemonReason]),
+ halt(1)
+ end.
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/config.pp b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/config.pp
new file mode 100644
index 000000000..444d5a9b3
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/config.pp
@@ -0,0 +1,39 @@
+class ssh_erlangotp_rce::config {
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $leaked_filenames = $secgen_parameters['leaked_filenames']
+ $strings_to_leak = $secgen_parameters['strings_to_leak']
+ $ssh_username = $secgen_parameters['unix_username'][0]
+ $ssh_home = "/home/${ssh_username}"
+
+ file { '/opt/erlang_ssh':
+ ensure => directory,
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ }
+
+ file { '/opt/erlang_ssh/ssh_keys':
+ ensure => directory,
+ owner => $ssh_username,
+ group => $ssh_username,
+ mode => '0700',
+ }
+
+ file { '/opt/erlang_ssh/start_ssh.escript':
+ ensure => present,
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ source => 'puppet:///modules/ssh_erlangotp_rce/start_ssh.escript',
+ require => File['/opt/erlang_ssh'],
+ }
+
+ ::secgen_functions::leak_files { 'erlang_ssh-flag':
+ storage_directory => $ssh_home,
+ leaked_filenames => $leaked_filenames,
+ strings_to_leak => $strings_to_leak,
+ owner => $ssh_username,
+ mode => '0600',
+ leaked_from => 'ssh_erlangotp_rce',
+ }
+}
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/install.pp b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/install.pp
new file mode 100644
index 000000000..ab460213c
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/install.pp
@@ -0,0 +1,53 @@
+class ssh_erlangotp_rce::install {
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $ssh_username = $secgen_parameters['unix_username'][0]
+ $ssh_home = "/home/${ssh_username}"
+ $status_log = '/var/log/ssh_erlangotp_rce/stage_status.log'
+
+ ensure_packages([
+ 'openssh-client',
+ ])
+
+ file { '/var/log/ssh_erlangotp_rce':
+ ensure => directory,
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ }
+
+ file { '/tmp/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb':
+ ensure => file,
+ source => 'puppet:///modules/ssh_erlangotp_rce/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb',
+ mode => '0644',
+ }
+
+ exec { 'install-erlang-otp-prebuilt':
+ command => "/bin/sh -c 'echo \"$(date -Is) install:start\" >> ${status_log}; if /usr/bin/dpkg -i /tmp/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb > /var/log/ssh_erlangotp_rce/install.log 2>&1; then echo \"$(date -Is) install:ok\" >> ${status_log}; touch /var/log/ssh_erlangotp_rce/.install_ok; else rc=$?; echo \"$(date -Is) install:fail rc=\$rc\" >> ${status_log}; touch /var/log/ssh_erlangotp_rce/.install_fail; tail -n 120 /var/log/ssh_erlangotp_rce/install.log >> ${status_log}; exit \$rc; fi'",
+ creates => '/usr/local/bin/erl',
+ path => ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
+ logoutput => true,
+ require => [
+ File['/var/log/ssh_erlangotp_rce'],
+ File['/tmp/erlang-otp-prebuilt_26.2.5.10-1_amd64.deb'],
+ ],
+ }
+
+ user { $ssh_username:
+ ensure => present,
+ home => $ssh_home,
+ managehome => true,
+ shell => '/bin/bash',
+ }
+
+ group { $ssh_username:
+ ensure => present,
+ }
+
+ file { $status_log:
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ require => File['/var/log/ssh_erlangotp_rce'],
+ }
+}
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/service.pp b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/service.pp
new file mode 100644
index 000000000..685a76c53
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/manifests/service.pp
@@ -0,0 +1,81 @@
+class ssh_erlangotp_rce::service {
+ $secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
+ $ssh_port = pick($secgen_parameters['ssh_port'], ['2222'])
+ $port = $ssh_port[0]
+ $ssh_username = $secgen_parameters['unix_username'][0]
+
+ exec { 'generate-ssh-host-rsa-key':
+ command => "/bin/sh -c '/usr/sbin/runuser -u ${ssh_username} -- ssh-keygen -t rsa -b 2048 -f /opt/erlang_ssh/ssh_keys/ssh_host_rsa_key -N \"\" > /var/log/ssh_erlangotp_rce/ssh_keygen_rsa.log 2>&1'",
+ path => ['/usr/sbin', '/usr/bin', '/sbin', '/bin'],
+ creates => '/opt/erlang_ssh/ssh_keys/ssh_host_rsa_key',
+ logoutput => true,
+ require => [
+ File['/var/log/ssh_erlangotp_rce'],
+ File['/opt/erlang_ssh/ssh_keys'],
+ User[$ssh_username],
+ ],
+ }
+
+ exec { 'generate-ssh-host-ecdsa-key':
+ command => "/bin/sh -c '/usr/sbin/runuser -u ${ssh_username} -- ssh-keygen -t ecdsa -b 256 -f /opt/erlang_ssh/ssh_keys/ssh_host_ecdsa_key -N \"\" > /var/log/ssh_erlangotp_rce/ssh_keygen_ecdsa.log 2>&1'",
+ path => ['/usr/sbin', '/usr/bin', '/sbin', '/bin'],
+ creates => '/opt/erlang_ssh/ssh_keys/ssh_host_ecdsa_key',
+ logoutput => true,
+ require => Exec['generate-ssh-host-rsa-key'],
+ }
+
+ file { '/etc/systemd/system/erlang-otp-ssh-rce.service':
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ content => @("UNIT"),
+[Unit]
+Description=Erlang OTP vulnerable SSH daemon
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+Type=simple
+User=${ssh_username}
+Group=${ssh_username}
+WorkingDirectory=/opt/erlang_ssh
+ExecStart=/usr/local/bin/escript /opt/erlang_ssh/start_ssh.escript ${port}
+Restart=on-failure
+RestartSec=3
+StandardOutput=append:/var/log/ssh_erlangotp_rce/ssh_daemon_start.log
+StandardError=append:/var/log/ssh_erlangotp_rce/ssh_daemon_start.log
+
+[Install]
+WantedBy=multi-user.target
+| UNIT
+ notify => Exec['systemd-daemon-reload'],
+ require => [
+ File['/var/log/ssh_erlangotp_rce'],
+ Exec['generate-ssh-host-ecdsa-key'],
+ File['/opt/erlang_ssh/start_ssh.escript'],
+ Exec['install-erlang-otp-prebuilt'],
+ User[$ssh_username],
+ ],
+ }
+
+ exec { 'systemd-daemon-reload':
+ command => '/bin/systemctl daemon-reload',
+ path => ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
+ refreshonly => true,
+ }
+
+ service { 'erlang-otp-ssh-rce':
+ ensure => running,
+ enable => true,
+ provider => 'systemd',
+ subscribe => [
+ File['/etc/systemd/system/erlang-otp-ssh-rce.service'],
+ File['/opt/erlang_ssh/start_ssh.escript'],
+ ],
+ require => [
+ Exec['systemd-daemon-reload'],
+ Exec['generate-ssh-host-ecdsa-key'],
+ ],
+ }
+}
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/secgen_metadata.xml b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/secgen_metadata.xml
new file mode 100644
index 000000000..03c763587
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/secgen_metadata.xml
@@ -0,0 +1,59 @@
+
+
+ Erlang/OTP SSH Unauthenticated RCE
+ Rosie Fletcher
+ MIT
+ Erlang/OTP SSH server allows unauthenticated remote code execution via crafted SSH protocol messages. CVE-2025-32433 affects versions prior to OTP-27.3.3, OTP-26.2.5.11, and OTP-25.3.2.20. The vulnerability exists in the SSH handshake message handling where message types >= 80 are incorrectly processed before authentication.
+
+ erlang_ssh
+ user_rwx
+ remote
+ linux
+ low
+
+ strings_to_leak
+ leaked_filenames
+ unix_username
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CVE-2025-32433
+ https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2
+ https://nvd.nist.gov/vuln/detail/CVE-2025-32433
+ erlang
+ Apache-2.0
+
+ Erlang/OTP SSH server running on 2222 port
+ Erlang/OTP SSH is vulnerable to pre-authentication RCE. Exploit via metasploit module (linux/ssh/ssh_erlangotp_rce).
+
+
+ erlang
+
+
+ update
+
+
+
+ EXPLOITATION
+ EXPLOITATION FRAMEWORKS
+
+
+ CVEs and CWEs
+
+
+ PENETRATION TESTING - SOFTWARE TOOLS
+ PENETRATION TESTING - ACTIVE PENETRATION
+
+
\ No newline at end of file
diff --git a/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/ssh_erlangotp_rce.pp b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/ssh_erlangotp_rce.pp
new file mode 100644
index 000000000..48ea0a419
--- /dev/null
+++ b/modules/vulnerabilities/unix/ssh/ssh_erlangotp_rce/ssh_erlangotp_rce.pp
@@ -0,0 +1,3 @@
+include ssh_erlangotp_rce::install
+include ssh_erlangotp_rce::config
+include ssh_erlangotp_rce::service
\ No newline at end of file
diff --git a/scenarios/ctf/SSH_it_happens.xml b/scenarios/ctf/SSH_it_happens.xml
new file mode 100644
index 000000000..ddeec712c
--- /dev/null
+++ b/scenarios/ctf/SSH_it_happens.xml
@@ -0,0 +1,109 @@
+
+
+
+ SSH-it happens
+ Rosie Fletcher
+
+ A server is running an Erlang/OTP SSH daemon vulnerable to CVE-2025-32433.
+ Exploit the pre-authentication RCE to gain root access and capture the flag.
+
+
+ ctf
+ attack-ctf
+ intermediate
+
+
+ server-side misconfiguration and vulnerable components
+ Missing Authentication
+
+
+ EXPLOITATION
+ EXPLOITATION FRAMEWORKS
+
+
+ CVEs and CWEs
+
+
+ PENETRATION TESTING - SOFTWARE TOOLS
+ PENETRATION TESTING - ACTIVE PENETRATION
+
+
+
+ attack_vm
+
+
+
+ 172.16.0.2
+ 172.16.0.3
+
+
+
+
+ {"username":"kali","password":"kali","super_user":"true","strings_to_leak":[],"leaked_filenames":[]}
+
+
+
+
+
+
+ {"username":"kali","password":"kali","super_user":"true","strings_to_leak":[],"leaked_filenames":[]}
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ IP_addresses
+
+
+
+
+
+
+
+ spoiler_admin_pass
+
+
+
+
+
+ server
+
+
+
+
+
+
+
+ 2222
+
+
+
+
+
+
+
+
+
+
+
+ IP_addresses
+
+
+
+
+ spoiler_admin_pass
+
+
+
+
+
\ No newline at end of file