# [Security] Second-Order SSRF in jeecgboot_JeecgBoot #9553 ## Vulnerability Overview A second-order Server-Side Request Forgery (SSRF) vulnerability exists in the announcement file download functionality of `jeecgboot_JeecgBoot`. Attackers can inject malicious HTTP URLs into the `files` field of an announcement via the `POST /sys/announcement/add` interface. When a user or administrator subsequently triggers the download of announcement attachments via `GET /sys/announcement/downloadFiles`, the server uses `HttpURLConnection` to fetch the injected URL without any SSRF protection. This allows attackers to scan the internal network, access local services, and retrieve sensitive data (such as cloud metadata). ## Affected Scope - **Affected Versions**: <=v3.9.1 - **Affected Components**: - `SysAnnouncementController.java` (L139-161, L751-756) - `SysAnnouncementServiceImpl.java` (L271-326) - `FileDownloadUtils.java` (L259-279) - **Affected Functions/Methods**: - `SysAnnouncementController.add()` - `SysAnnouncementServiceImpl.downloadFiles()` - `FileDownloadUtils.getDownInputStream()` - **Entry Points**: - POST `/sys/announcement/add` (Injection) - GET `/sys/announcement/downloadFiles` (Execution) ## Remediation 1. **Input Validation**: In the `SysAnnouncementController.add()` method, perform strict URL format and protocol validation on the `files` parameter to prevent the injection of unexpected URLs. 2. **SSRF Protection**: Add SSRF protection for HTTP requests in the `FileDownloadUtils.getDownInputStream()` method. For example, restrict the request target to addresses within a whitelist, or prohibit requests to internal network addresses. 3. **Code Example**: ```java // In FileDownloadUtils.java public static InputStream getDownInputStream(String fileId, String uploadUrl) { try { // HTTP URL: NO SSRF PROTECTION if (StringUtils.isNotEmpty(uploadUrl) && uploadUrl.startsWith(CommonConstant.STR_HTTP)) { URL url = new URL(uploadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setReadTimeout(30000); return connection.getInputStream(); // SSRF HERE } else { // Local file: Protected by SsrfilePathFilter String downloadFilePath = uploadUrl + File.separator + fileId; SsrfilePathFilter.checkPathTraversal(downloadFilePath); return new BufferedInputStream(new FileInputStream(downloadFilePath)); } } catch (IOException e) { return null; } } ``` ## POC Code ```json { "title": "Important Announcement", "remarkContent": "Please review attachments", "files": "http://109.254.169.254/latest/meta-data/iam/security-credentials/http://192.168.1.1/admin" } ```