Yellowfin BI, a popular provider of business intelligence (BI) software, maintains downloadable software that allows a user to run trial or demo version locally. For user convenience, Yellowfin BI downloads the entire backend application source code locally. Prior to version 9.8.1, this downloaded source code contained a hardcoded private key. This hardcoded key was also active in production. Due to this vulnerability, an adversary can chain the hardcoded keys with authentication bypass, which could lead to remote code execution (“RCE”). This article shares lessons learned and key takeaways for organizations seeking to prevent similar security issues with hardcoded keys.
Where was Yellowfin BI leveraging hardcoded keys
The first instance of a hardcoded private key was found in YellowfinBI’s REST API implementation at /api/rest/service/StoriesApiService.java. This hardcoded key is used to verify APIs calls from the Yellowfin BI’s URI at /stories/{storyUuid}/body. The below source code illustrates an redacted private key, which was hardcoded in Yellowfin app:
try {
final PrivateKey generatePrivate = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode("MIIEvwI[..REDACTED..]uHWkYj/A== ")));
final StringBuilder sb = new StringBuilder(uuid.toString());
sb.append('\n').append(currentDateTime);
final byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
final Signature instance = Signature.getInstance("SHA512withRSA");
instance.initSign(generatePrivate);
instance.update(bytes);
encode = URLEncoder.encode(new String(Base64.getEncoder().encode(instance.sign())), "UTF-8");
}
Authentication bypass
Successful authentication will redirect the page to /StoryBody.i4. This includes the following GET parameter:
1. “storyUDID” is the GUID for story.
2. “s” is the value of the encode variable received from StoriesApiService.java. With the hardcoded value of the private key we were able to calculate the value of “s” to FeHGWgaVo[…REDACTED…]8CIej2eVq6w==.
3. “ts” is time stamp.
4. “ipPerson” is the numerical value one can even set it to 1.
5. “ipOrg” is the numerical value one can even set it to 1.
final SessionBean sessionBean = this.getSessionBean();
final StringBuilder sb2 = new StringBuilder("redirect:/StoryBody.i4?storyUUID=");
sb2.append(uuid.toString());
sb2.append("&s=").append(encode);
sb2.append("&ts=").append(currentDateTime);
sb2.append("&ipPerson=").append(sessionBean.getPrsnBean().getIpPerson());
sb2.append("&ipOrg=").append(sessionBean.getPersonSearchIpOrg());
...
}
Once the authentication bypass is successful, there are two endpoints that will allow persistent web connection:
1. /logonCheck.i4
2. /keepAlive.i4
These two URLs needs to pass POST parameter for the with Credentials key and session token.
Execution of RCE attack
After successful authentication, the next step in this chaining attack is to perform remote code execution. In order to perform this attack, there are three vulnerable endpoints that can be targeted /NewSourceCreationAjax.i4, /CreateSimpleSourceAjax.i4 or /CreateSimpleViewAjax.i4. This endpoint provides a POST parameter called action, which allows them to save content within JSON parameter. Once can force the application to call JNDISourcePlatformImplementation and call either the DB information or call other protocols such as LDAP.
{“action”:”save”,
“testConnection”:True,
“json”:
{
"restrictedSourceCreation": False,
"displaySourceIcon": "connection_type_jndi",
"sourceClassName": "com.hof.sources.JNDISourcePlatformImplementation",
"enabledForTransformations": False,
"connectionMethodCode": "JNDI",
"displaySourceLongDescription": "",
"customParameters": [
{
"displayType": 4,
"defaultValue": "",
"displayName": "",
"uniqueKey": "DATABASEURL",
"disabled": False,
"refreshOnChange": False,
"value": jndi,
"clearsAllParameters": False,
"options": None
}
],
"userCanCreateView": True,
"databaseTypeCode": "GENERICJDBC",
"displaySourceName": "JNDI",
"validationMessages": "",
"sourceName": "",
"sourceDescription": ""
}
Key Takeaways
Hardcoded keys or non-production code can lead to significant security impacts, including access into test accounts or access to non-production data or code. As security and technology leaders think about this particular risk, Certus Cybersecurity recommends focusing on these key actions:
• Ensure that all hardcoded keys are stored in the configuration, environment (.env) or YAML files. Developers should avoid pushing these files into a repo that will be used for production.
• Run code review scans to ensure that hardcoded keys and any non-production, unused code is purged before release.
• Ensure that granular authentication and authorization controls are in place for all users. A user should not be able to bypass authentication controls by changing the parameter such as ipPerson to some other numeric value. Organizations should also enforce either attribute based or role-based access controls.
About the Author
Swapnil Deshmukh is CTO & co-founder of Certus Cybersecurity. A product security thought leader and subject matter expert, Swapnil is responsible for leading the company's global team of security engineers.