Gửi #979788: FIT2CLOUD MeterSphere v3.6.9-lts (earlier 3.x LTS releases share the same import code path) server-side request forgery (CWE-918)thông tin

tiêu đềFIT2CLOUD MeterSphere v3.6.9-lts (earlier 3.x LTS releases share the same import code path) server-side request forgery (CWE-918)
Mô tảAPI-definition import makes the server fetch a caller-supplied URL (project member) API-definition import makes the server fetch a caller-supplied URL (project member) Data flow: HTTP multipart part request.swaggerUrl at POST /api/definition/import → Swagger3ParserApiDefinition.testUrlTimeout -> new URI(swaggerUrl).toURL().openConnection().connect() and OpenAPIParser.readLocation AFFECTED INTERFACES | Interface | |---| | POST /api/definition/import multipart request {projectId, platform=Swagger3, swaggerUrl} | | POST /api/definition/schedule/add (JSON projectId, name, swaggerUrl, enable, value) | | POST /api/definition/schedule/update (JSON id, projectId, name, swaggerUrl, enable, value) | Primary route detail: POST /api/definition/import (multipart/form-data), part request: {"projectId":"<project>","platform":"Swagger3","swaggerUrl":"http://<host>:<port>/<path>"} The optional file part is not needed. The route is registered on the API-definition controller and carries @RequiresPermissions(PROJECT_API_DEFINITION:READ+IMPORT). The default project_member role is granted exactly that permission by the application's own seed data (backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql:218), so an ordinary project member - not a system administrator - can call the route. What was tested: the two scheduled routes were exercised with an ordinary project-member account (roles project_member and org_member, no administrator role); the manual import route was exercised with the built-in administrator account in the isolated instance, and the permission it checks is the same PROJECT_API_DEFINITION:READ+IMPORT permission that the shipped seed data grants to project_member. ROOT CAUSE Source (HTTP multipart part): - ImportRequest.swaggerUrl (backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ImportRequest.java:16), populated from the request part of POST /api/definition/import (ApiDefinitionController.testCaseImport, .../api/controller/definition/ApiDefinitionController.java:295-300). Sink (server-side HTTP request to the caller-chosen URL, twice): - ApiDefinitionImportService.apiDefinitionImport(...) selects the Swagger3 parser for platform=Swagger3 and calls Swagger3ParserApiDefinition.parse(...). - Swagger3ParserApiDefinition.testUrlTimeout(String swaggerUrl) (.../parser/api/dataimport/Swagger3ParserApiDefinition.java:57-70) does new URI(swaggerUrl).toURL().openConnection().connect() with a 3 s connect timeout and no host, scheme or address validation. The local canary received this request. - Directly afterwards the same parser calls new OpenAPIParser().readLocation(request.getSwaggerUrl(), auths, null), which performs the second server-side fetch of the same URL and returns the body to the importer. Neither call consults a destination allowlist, private-address policy or DNS check. The root cause is treating a caller-supplied URL as a trusted import source: the import permission is intended to let a project member import an API description, not to grant the server-side ability to reach arbitrary internal endpoints (http://127.0.0.1:*, RFC1918, link-local, cloud metadata). PER-ROUTE DELTAS - Ordinary-member SSRF through the scheduled Swagger import - HTTP JSON field swaggerUrl (plus cron field value) at POST /api/definition/schedule/add → ApiDefinitionScheduleService.createSchedule persists the URL -> Quartz SwaggerUrlImportJob -> Swagger3ParserApiDefinition.testUrlTimeout/parse -> URI.toURL().openConnection() - Ordinary-member SSRF through the scheduled Swagger import update - HTTP JSON field swaggerUrl (with id of an api_definition_swagger row owned by the caller) at POST /api/definition/schedule/update → ApiDefinitionScheduleService.updateSchedule persists the URL -> Quartz SwaggerUrlImportJob -> Swagger3ParserApiDefinition.testUrlTimeout/parse -> URI.toURL().openConnection() PROOF OF CONCEPT Baseline: commit cf7a649a71f4db27c42547d7aa62816826d645ff, server on 127.0.0.1:18080. The destination is a test-local HTTP listener at 192.168.96.4:18081 that logs every request. 1. Sign in (the session cookie the import routes require): $ curl -s -c cookies.txt -X POST 'http://127.0.0.1:18080/login' \ -H 'Content-Type: application/json' -d '{"username":"<user>","password":"<throwaway>"}' HTTP/1.1 200 2. Import an API definition whose Swagger source is the listener: $ curl -s -b cookies.txt -X POST 'http://127.0.0.1:18080/api/definition/import' \ -F 'request={"projectId":"100001100001","platform":"Swagger3","swaggerUrl":"http://192.168.96.4:18081/ssrf-canary"};type=application/json' HTTP/1.1 500 3. What the listener logged - the server-side fetch performed by Swagger3ParserApiDefinition.testUrlTimeout: $ cat mock-requests.log GET /ssrf-canary The import itself ends in an HTTP 500 and persists nothing, but the request to the caller-chosen URL has already been sent, and the same URL is fetched a second time when OpenAPIParser.readLocation reads the document. 4. Negative control: the same call without a session returns HTTP 302 and produces no listener hit. The two scheduled variants (POST /api/definition/schedule/add and POST /api/definition/schedule/update) carry the same swaggerUrl field and are fetched by the Quartz job; those routes were exercised with an ordinary project-member account. The manual import route was exercised with the built-in administrator account in the isolated instance; the permission it checks (PROJECT_API_DEFINITION:READ+IMPORT) is granted to the default project_member role by the shipped seed data. IMPACT See the interface and root-cause sections: an actor who satisfies the stated preconditions makes the server perform a request or operation on a destination/target of the attacker's choosing, crossing the trust boundary described above. - Project repository: https://github.com/metersphere/metersphere - Security policy: `SECURITY.md` in that repository. - **Vendor contact**: MeterSphere / FIT2CLOUD security contact - [email protected] - **Status**: vendor notified 2026-09-14 by email to [email protected]; thread subject: `SSRF in MeterSphere API-definition import (swaggerUrl) reachable by an ordinary project member`; not publicly disclosed; no CVE assigned - **Duplicate check**: public advisories (NVD/OSV/GitHub) and the project issue tracker were searched before reporting; please tell us if this duplicates something already tracked internally.
Người dùng
 lucasg2g (UID 84737)
Đệ trình15/09/2026 09:21 (cách đây 29 ngày)
Kiểm duyệt12/10/2026 15:08 (27 days later)
Trạng tháiđược chấp nhận
Mục VulDB416896 [fit2cloud MeterSphere đến 3.6.9-lts Swagger3 Parser Swagger3ParserApiDefinition.java Swagger3ParserApiDefinition.testUrlTimeout swaggerUrl nâng cao đặc quyền]
điểm17

Want to know what is going to be exploited?

We predict KEV entries!