Skip to content

Java: CWE-798: Query to detect hard-coded SHIRO key#5907

Merged
smowton merged 7 commits intogithub:mainfrom
x-f1v3:java/hardcoded-shiro-key
Sep 30, 2021
Merged

Java: CWE-798: Query to detect hard-coded SHIRO key#5907
smowton merged 7 commits intogithub:mainfrom
x-f1v3:java/hardcoded-shiro-key

Conversation

@x-f1v3
Copy link

@x-f1v3 x-f1v3 commented May 17, 2021

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management.

shiro allows user to set default cipher key for the “remember me” feature,
when a cipher key can be guessed , allows remote attackers to execute arbitrary code or bypass intended access restrictions via an unspecified request parameter.

The query detects a hard-coded cipher key for shiro.


override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.asExpr().getType() instanceof TypeString and
exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.asExpr().getType() instanceof TypeString and
exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) |
exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray","decode"]) |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be specific: what type's decode method are you referring to here? (the existing getBytes and toCharArray should be specific too in fairness, though no need to fiddle with that in this PR)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when i test cookieRememberMeManager.setCipherKey(Base64.decode("1234")) by the query without decode , it do not work. so i add 'decode' method as additional data flow. Other queries used to detect hard-coded may also encounter similar situations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, in that case you should check if ma.getMethod().hasQualifiedName("java.util", "Base64$Decoder", "decode")

The existing getBytes and toCharArray methods should also be qualified that way; it isn't mandatory for you to fix that here though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this work out-of-the-box?

"java.util;Base64$Encoder;false;encode;(byte[]);;Argument[0];ReturnValue;taint",
"java.util;Base64$Encoder;false;encode;(ByteBuffer);;Argument[0];ReturnValue;taint",
"java.util;Base64$Encoder;false;encodeToString;(byte[]);;Argument[0];ReturnValue;taint",
"java.util;Base64$Encoder;false;wrap;(OutputStream);;Argument[0];ReturnValue;taint",
"java.util;Base64$Decoder;false;decode;(byte[]);;Argument[0];ReturnValue;taint",
"java.util;Base64$Decoder;false;decode;(ByteBuffer);;Argument[0];ReturnValue;taint",
"java.util;Base64$Decoder;false;decode;(String);;Argument[0];ReturnValue;taint",
"java.util;Base64$Decoder;false;wrap;(InputStream);;Argument[0];ReturnValue;taint",

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot, yes it should. Can you show a test case that requires this extra decode method to be modelled this way?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. but in some case, java.util.Base64$Decoder is not be used. for instance, org.apache.shiro.codec.Base64 in https://github.com/yinna/wjproject/blob/master/wj/src/main/java/com/example/wj/config/ShiroConfiguration.java,cn.hutool.core.codec.Base64 in https://github.com/ph-sir/usermg/blob/master/src/main/java/com/pengh/usermg/shiro/ShiroConfig.java, how to detect that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either we would welcome a PR to model Shiro and/or Hutool, providing model specifications similar to those that @intrigus-lgtm linked to, or if you just want to provide enough support for this query, write something like

ma.getMethod().hasQualifiedName(["org.apache.shiro.codec", "cn.hutool.core.codec"], "Base64", "decode")

The important thing is to specify particular methods you want to model, rather than guessing that any future method encountered that is named decode will behave the same way.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.thanks for your advice

s = "sun.security.tools.keytool.Main;getKeyPasswd(String, String, char[]);2" or
s = "sun.security.x509.X509Key;decode(byte[]);0"
s = "sun.security.x509.X509Key;decode(byte[]);0" or
s = "org.apache.shiro.mgt.AbstractRememberMeManager;setCipherKey(byte[]);0"
Copy link
Contributor

@intrigus-lgtm intrigus-lgtm May 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should get moved to the otherApiCallableCredentialParam method, because the javaApiCallableCryptoKeyParam method is auto-generated.

@smowton
Copy link
Contributor

smowton commented May 18, 2021

You should also add a change note, similar to #5852 which is making very similar changes.

Copy link
Contributor

@intrigus-lgtm intrigus-lgtm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions.

ma.getQualifier() = node1.asExpr()
)
or
FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOI: Why is this needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modelled the decode methods of shiro and Hutool in ExternalFlow.qll and FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, false) is for using that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know this should work by default, so you don't have to manually call it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but it didn't work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work by default, because

"cn.hutool.core.codec;Base64;true;decode;;;Argument[0];ReturnValue;taint",
"org.apache.shiro.codec;Base64;false;decode;(String);;Argument[0];ReturnValue;taint",
only applies to TaintTracking::Configurations but not DataFlow::Configurations.

I'm not sure whether changing this to a TaintTracking::Configuration would be the right action.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would: dataflow configs track things with the same object identity, while taint tracking also allows related but different objects. Base64 decoding clearly produces a different object but is relevant to the query. Therefore taint tracking seems appropriate (cc @aschackmull to double check you're happy with this)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on whether you want the complete set of default taint steps to apply to your configuration or whether you just want object identity tracking plus a few select steps. In the former case, use a TaintTracking::Configuration, in the latter case, use a DataFlow::Configuration and add steps as needed. In either case, you shouldn't reference internal libraries directly (e.g. FlowSummaryImpl).
In this case, as we're likely mostly dealing with strings, consider whether steps like string concatenation are valid taint steps, if they are, then a TaintTracking::Configuration is likely the right choice, but if string concatenation doesn't make sense for this flow config, then a DataFlow::Configuration must be used.

@@ -0,0 +1,3 @@
lgtm,codescanning
* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`)
This query finds hard-coded cipher key for shiro. This vulnerability can lead to arbitrary code execution. No newline at end of file
Copy link
Contributor

@intrigus-lgtm intrigus-lgtm May 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this lead to arbitrary code execution?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you know the cipher key for the remember me feature,you can deserialize any class on the server environment by the key to achieve Remote Code Execution.
actually,This vulnerability is very similar to CVE-2016-4437. The difference is that CVE-2016-4437 is hard-coded by the shiro framework, this vulnerability is hard-coded by the developer,
(In order to fix CVE-2016-4437, shiro used a random key, but still retains "setCipherKey" method to set the key)

Comment on lines 2 to 3
* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`)
This query finds hard-coded cipher key for shiro. This vulnerability can lead to arbitrary code execution. No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`)
This query finds hard-coded cipher key for shiro. This vulnerability can lead to arbitrary code execution.
* The query "Hard-coded credential in API call" (`java/hardcoded-credential-api-call`) can now detect a hard-coded Apache Shiro cipher key.

ma.getQualifier() = node1.asExpr()
)
or
FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would: dataflow configs track things with the same object identity, while taint tracking also allows related but different objects. Base64 decoding clearly produces a different object but is relevant to the query. Therefore taint tracking seems appropriate (cc @aschackmull to double check you're happy with this)

@smowton smowton force-pushed the java/hardcoded-shiro-key branch from 4ac91ea to b0983cb Compare September 30, 2021 13:59
@smowton
Copy link
Contributor

smowton commented Sep 30, 2021

Updated this and will merge on green

@github-actions
Copy link
Contributor

⚠️ The head of this PR and the base branch were compared for differences in the framework coverage reports. The generated reports are available in the artifacts of this workflow run. The differences will be picked up by the nightly job after the PR gets merged. The following differences were found:

java

Generated file changes for java

  • Changes to framework-coverage-java.rst:
-    Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,26,151,,,,14,18,,
+    Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,28,151,,,,14,18,,
-    Totals,,143,4984,408,13,6,10,107,33,1,66
+    Totals,,143,4986,408,13,6,10,107,33,1,66
  • Changes to framework-coverage-java.csv:
+ cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,1,
+ org.apache.shiro.codec,,,1,,,,,,,,,,,,,,,,,,,,,1,

@smowton smowton merged commit f48c418 into github:main Sep 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants