AlvaroNeovia opened a new issue, #2541:
URL: https://github.com/apache/shardingsphere-elasticjob/issues/2541

   Hi,
   
   The following is the result of a read-only security review; all reproduction 
was done in a DISPOSABLE environment. Happy to provide PoCs / verification 
details on request.
   
   ---
   
   ## A. 上游通知(`apache/shardingsphere-elasticjob-ui`)
   
   **主题**:ElasticJob UI(lite + cloud 双侧)的 `registry-center` 配置面**对 
`connectString` 零校验**,导致已认证会话(含出厂默认凭据)可令服务端 **ZooKeeper 客户端向任意 `host:port` 发起 
TCP 连接并外发 ZK 协议握手**(协议级 SSRF)
   
   ### A.1 根因(双层零校验 → 建连)
   
   **第 1 层 · 入口零校验**(两子模块同构):
   
   ```java
   // 
shardingsphere-elasticjob-lite-ui-backend/.../service/impl/RegistryCenterConfigurationServiceImpl.java:86-94(cloud
 侧同构)
   @Override
   public boolean add(final RegistryCenterConfiguration config) {
       GlobalConfiguration configs = loadGlobal();
       boolean result = 
configs.getRegistryCenterConfigurations().getRegistryCenterConfiguration().add(config);
       if (result) {
           configurationsXmlRepository.save(configs);      // ← 任意 
zkAddressList/namespace/digest 直落 XML,无任何校验
       }
       return result;
   }
   ```
   
   REST 入口:`RegistryCenterController` 
`@RequestMapping("/api/registry-center")`(:43):
   
   | 端点 | 行 | 作用 |
   |---|---|---|
   | `POST /api/registry-center/add` | :83-86 | 写入任意配置(零校验) |
   | `POST /api/registry-center/connect` | :106-113 → :118 | **触发建连** |
   | `GET /api/registry-center/load` | :71-75 → :73 | 对 activated 配置**同样触发建连** |
   
   **第 2 层 · sink 零校验 → 建连**:
   
   ```java
   // lite 侧(依赖 elasticjob-lite-lifecycle,见 B 
版本矩阵)internal/reg/RegistryCenterFactory.java:51-72
   public static CoordinatorRegistryCenter 
createCoordinatorRegistryCenter(final String connectString, ...) {
       ...
       return REG_CENTER_REGISTRY.computeIfAbsent(hashCode, unused -> {
           CoordinatorRegistryCenter result = 
newCoordinatorRegistryCenter(connectString, namespace, digest);
           result.init();                                                  // ← 
建连
           return result;
       });
   }
   private static CoordinatorRegistryCenter newCoordinatorRegistryCenter(final 
String connectString, ...) {
       final ZookeeperConfiguration zkConfig = new 
ZookeeperConfiguration(connectString, namespace);   // ← 直取,零校验
       ...
       return new ZookeeperRegistryCenter(zkConfig);
   }
   
   // cloud 
侧自带同类(cloud-ui-backend/.../service/RegistryCenterFactory.java:51-69,同构)
   
   // 建连落地:elasticjob-registry-center-zookeeper-curator 
reg/zookeeper/ZookeeperRegistryCenter.java:96-135
   public void init() {
       ...
       CuratorFrameworkFactory.Builder builder = 
CuratorFrameworkFactory.builder()
               .connectString(zkConfig.getServerLists())                   // 
:99  ← 攻击者可控值直入 Curator
               ...;
       client = builder.build();                                           // 
:123
       client.start();                                                     // 
:124  ← 真实 TCP 连接发起
       if (!client.blockUntilConnected(...)) { ... }                       // 
:126
   }
   ```
   
   **独立复核(本次审计取得)**:
   - 编译产物字节码同判:`javap -c` 显示 `new ZookeeperConfiguration` 
`<init>(String,String)` → `invokestatic newCoordinatorRegistryCenter` → 
`invokeinterface CoordinatorRegistryCenter.init:()V`(排除 sources 与二进制不同步风险);
   - **3.0.2(release)与该 sink 文件逐字节相同**:`diff` 空输出且两版文件 sha256 同为 
`c0c93264196fd3ad04999f639e1ec4d7d979ed0c4d52919d1812a557d75458dd`(见 B 版本矩阵);
   - 与同类已修复面(CVE-2022-31764,事件追踪 JDBC URL 注入,修于 3.0.2)对照:**该修复引入了 driver 
类名白名单,但 `registry-center` 路径连该级校验都没有** ⇒ 属"修复只覆盖被点名端点"形态。
   
   ### A.2 影响端点(认证后 / 出厂凭据即可)
   
   1. `POST /api/registry-center/add`(body 含 `zkAddressList:"<任意 host:port>"`)→ 
零校验落盘;
   2. `POST /api/registry-center/connect`(body `{name}`)→ 对任意地址发起 Curator 建连;
   3. `GET /api/registry-center/load`(对 activated 配置)→ 等价触发。
   
   **盲 oracle**:`/connect` 返回 `Boolean`(`Controller:120-122` 捕获 `RegException` 
→ `false`)——对存活 ZK 与会话可达目标返回快、对非 ZK/关端口在重试窗口后返回 `false`(本地实测 **18 s**)⇒ 
可作**内网端口/服务存活探测原语**。
   
   **加固背景**:该面在任何认证配置下成立——出厂默认凭据为 
`root/root`(`application.properties:20-21`,lite/cloud 两侧同值),且认证载体仅为可离线持有的 
`Access-Token` JWT。UI 默认监听 `8088`(`application.properties:18`)。
   
   ### A.3 运行时验证(本地 lab,已端到端复现)
   
   从源码构建 lite-ui 3.1.0-SNAPSHOT(`BUILD SUCCESS`),JRE8 运行实例上完成六步链:
   `root/root 登录` → `add(zkAddressList=127.0.0.1:<本地监听端口>)` → `connect` → 
**本地监听器捕获 2 次 TCP accept,各收到 49 字节标准 ZooKeeper ConnectRequest**:
   
   ```
   00 00 00 2d                          ← 长度前缀 = 45
   00 00 00 00                          ← protocolVersion = 0
   00 00 00 00 00 00 00 00              ← lastZxidSeen = 0
   00 00 ea 60                          ← timeOut = 60000 ms(与客户端 
sessionTimeout 一致)
   00 00 00 00 00 00 00 00              ← sessionId = 0(新会话)
   00 00 00 10                          ← passwd 长度 = 16
   00 00 00 00 …(16B 全零)            ← passwd
   00                                   ← readOnly = false
   ```
   
   三源交叉闭合:应用日志 `Socket connection established, client: /127.0.0.1:45690 / 
:51224`(端口 ×2)与监听器记录逐一对上;`/connect` 返回 `false` 且阻塞 18 s(Curator 
`ExponentialBackoffRetry(1000,3,3000)` 窗口)。测试条目已回滚(`DELETE` 后 `load=[]`)。
   
   **⇒ 建议**:
   ① 在 `add`/`connect` 路径对 `zkAddressList` 做格式与范围校验(最小:正则 
`^[A-Za-z0-9_.-]+:[0-9]{1,5}(,[A-Za-z0-9_.-]+:[0-9]{1,5})*$`;推荐:叠加部署方白名单/私网策略,拒绝指向非预期网段的地址);
   ② 在 `RegistryCenterFactory`(含 cloud 自带实现)与 `ZookeeperRegistryCenter` 
建连前做统一校验点,避免"每入口各修一遍";
   ③ 文档化声明 `registry-center` 面仅为管理端使用,建议默认绑定回环/内网并强制修改出厂凭据;
   ④ 复核 `elasticjob-lite-lifecycle` 中其它 `connectString`/JDBC URL 
系调用点是否存在同类零校验(CVE-2022-31764 的邻域)。
   
   ---
   
   
   ---
   
   *(Security hardening request; full technical annex available on request.)*
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: 
[email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to