Coordinated Disclosure Timeline

Summary

A retest of GHSL-2023-239/CVE-2024-28212 uncovered that the endpoint /script/api/github/validate of ngrinder remained susceptible to unsafe YAML deserialization. However, it seemed not to be exploitable in an impactful manner.

Project

ngrinder

Tested Version

v3.5.9

Details

Unsafe Deserialization (GHSL-2024-069)

The endpoint /script/api/github/validate remains susceptible to unsafe YAML deserialization. This is due to the fact that user-controlled YAML is allowed to flow into a vulnerable-by-default YamlReader#read sink of YamlBeans.

The user-controlled data is passed down from the unauthenticated validateGithubConfig endpoint:

@PostMapping("/github/validate")
public void validateGithubConfig(@RequestBody FileEntry fileEntry) {
	gitHubFileEntryService.validate(fileEntry);
}

Then passed down to the validate method inside the GitHubFileEntryService class.

public boolean validate(FileEntry gitConfigYaml) {
	for (GitHubConfig config : getAllGithubConfig(gitConfigYaml)) {

The validate method ultimately calls the getAllGithubConfig, which ultimately calls the YamlReader#read sink of YamlBeans:

private Set<GitHubConfig> getAllGithubConfig(FileEntry gitConfigYaml) {
	Set<GitHubConfig> gitHubConfig = new HashSet<>();
	try (YamlReader reader = new YamlReader(gitConfigYaml.getContent())) {
		Map<String, Object> gitConfigMap = cast(reader.read());

This vulnerability was discovered with the help of CodeQL’s Deserialization of user-controlled data query.

Since YamlBeans allows to instantiate classes by user-controlled classnames and populate the resulting objects with data (using setters or public fields) an attacker aware of a so-called gadget chain might use this vulnerability to execute code. E.g. if ngrinder would use the c3p0 library an attacker could use the WrapperConnectionPoolDataSource class to execute code by making a request such as:

POST /script/api/github/validate HTTP/1.1
Host: <ngrinder-host>:8080
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
Connection: close
Content-Length: xx

{
"content":  "!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\nuserOverridesAsString: \n \"HexAsciiSerializedMap:aced0005737[..];\""
}

In this hypothetical sample YamlBeans would instantiate the class WrapperConnectionPoolDataSource and then call the setter setUserOverridesAsString on it with the abbreviated HexAsciiSerializedMap:[..] data. This data itself is serialized using Java Serialization. For more information see WrapperConnectionPoolDataSource inside of the Marshalsec paper. Theoretically, there could be one or more gadget classes on the ngrinder’s classpath that could have various impacts up to remote code execution.

Impact

This issue may lead up to remote code execution in case an attacker is able to find appropriate gadgets on ngrinder’s classpath.

Resources

Credit

This issue was discovered and reported by GHSL team member @p- (Peter Stöckli).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-069 in any communication regarding this issue.