Tutorial - Creating a Java Path checkerTutorial - Creating a Java Path checkerPrerequisite: Ant must be installed. For this tutorial, we will create a Security Vulnerability (SV) checker named JNDI.PRINT. For more information about the types of Java Path checkers you can create, see Types of Java Path checkers. The process for creating a Java path checker is the same regardless of whether you are creating an SV, RLK, or common checker. The snippet containing the issue we want to detect is in Sample1.java: import javax.naming.*; public class Sample1 { public void test(final Context context) throws Exception { final NamingEnumeration<NameClassPair> enumeration = context.list("*"); System.out.println(enumeration ); } } Run kwcreatecheckerFrom the directory in which you want your checker files to be created, run kwcreatechecker with the following options: kwcreatechecker --language java --type sv --code JNDI.PRINT Note: The use of special (and multibyte) characters is not supported with the --code option.
Result A directory is created with the name you specified with --code . This directory contains the checker stub files. In the case of the example above, the directory is named JNDI.PRINT. Add @Source and @Sink to the knowledge baseAt minimum, you must define one source and one sink for the engine to detect the issue. An issue is reported only when data created in a source is used in a sink. The .jkb file generated by kwcreatechecker contains examples of source, sink, check, and prop.
Provide a title and message for the checker
Create context-sensitive help for your checker
JNDI.PRINT help.xml <?xml version="1.0"?> <help language="java"> <defect id="JNDI.PRINT"> <description> Sensitive JNDI information leak </description> <risks> Revealing details about an JNDI storage is a security issue because it provides attackers with information they can use to further their attacks. </risks> <prevention> Filter all the data coming from the JNDI in order to prevent sensitive information leaks. </prevention> <examples> <example line="1"> <![CDATA[ import javax.naming.*; public class Sample1 { public void test(final Context context) throws Exception { final NamingEnumeration<NameClassPair> enumeration = context.list("*"); System.out.println(enumeration ); } } ]]> <description> JNDI.PRINT is reported on line 7. </description> </example> </examples> </defect> </help> Run ant to pack the checker filesGo to your checker directory and run Ant in order to pack the checker files into a JAR file. For example: cd \home\jsmith\testproject\JNDI.PRINT ant Results: Buildfile: build.xml Test the checkerPut the checker JAR in the plugins folder To test the checker, you must locally deploy the JAR file you created in the previous step. If you do not see a plugins directory in the following location, you must create it manually. The location varies depending on your operating system:
Place the JAR file in the plugins folder. Run kwjava Run kwjava to test your checker against one or more sample files. To reduce the size of the output, it's best practice to use simple source files when testing your checker. kwjava --license-host kw-test --license-port 27000 Sample1.java --xml problem.xml problem.xml output: <problem> <file>Sample1.java</file> <method>test</method> <line>7</line> <column>17</column> <message> JNDI data Parameter enumeration of call to println(...) is printed out at enumeration </message> JNDI.PRINT <params> <param>Parameter enumeration of call to println(...)</param> <param>Value returned by list(...)</param> <param>enumeration</param> <param>enumeration</param> </params> <trace> <traceBlock file="Sample1.java" class="Sample1" method="test" id="1"> <traceLine line="6" text="Value returned by list(...)"/> <traceLine line="7" text="Parameter enumeration of call to println(...)"/> </traceBlock> </trace> </problem> If your checker is not detecting the issues you expect, re-examine the knowledge base file to troubleshoot the problem. If you cannot pinpoint the problem in the knowledge base file, you can enable your checker's log to get information about the code analyzed by the checker, as well as the sources and sinks that were applied. See Optional: Advanced troubleshootingOptional: Advanced troubleshooting. Optional: Advanced troubleshootingRun kwjava with the -l option. The log contains the information about the code analyzed by the checker, as well as the sources and sinks that were applied. You can use this output to debug and improve your checker and knowledge base. kwjava --license-host kw-test --license-port 27000 Sample1.java -xml problem.xml -l <checker_dir>\logger.xml> where<checker_dir> is the checker directory generated by kwcreatechecker When you enable your checker's logger file, you see how data is collected and tracked by your checker. Add @Prop to the knowledge baseOur new snippet reference is Sample2.java: import javax.naming.*; public class Sample2 { public void test(final Context context) throws Exception { final NamingEnumeration<NameClassPair> enumeration = context.list("*"); final Object o = enumeration.next(); System.out.println(o); } } Extending the knowledge base with @Prop In the above sample, tainted data is created in final NamingEnumeration<NameClassPair> enumeration = context.list("*");, but it is not used in the sink. What is used in the sink is an element of the tainted data. This element of the tainted data is extracted in final Object o = enumeration.next();. In order to support such a situation we should extend the checker knowledge base with a prop entry, specifying that the next method returns the tainted data if the enumeration is tainted.
package javax.naming; import java.util.Hashtable; public interface Context { @Source("return") public NamingEnumeration list(String name) throws NamingException; } public interface NamingEnumeration<T> { @Prop(in="this", out="return") T next() throws NamingException; } Add @Check to the knowledge baseWe will use Sample3.java to get the information we need to create a check entry: import javax.naming.*; public class Sample3 { public void test(final Context context) throws Exception { final NamingEnumeration<NameClassPair> enumeration = context.list("*"); final Object o = enumeration.next(); verify(o); System.out.println(o); } public void verify(final Object o) throws Exception { if (o.toString().indexOf("password") >= 0) { throw new Exception("Private data is accessed"); } } } In the above snippet, the verify method is sufficient to check the data.
public class Sample3 { public void verify(@Check final Object o) throws Exception; } package javax.naming; import java.util.Hashtable; public interface Context { @Source("return") public NamingEnumeration list(String name) throws NamingException; } public interface NamingEnumeration<T> { @Prop(in="this", out="return") T next() throws NamingException; } With this knowledge-base entry, JNDI.PRINT would not be reported against Sample3. Go to : Test the checker. After re-testing the checker, we will add a check-true to the knowledge base. Add @CheckTrue to the knowledge baseIf the verify method from the check returns a boolean value, which is true (data is allowed to be printed out) and false otherwise, the data is not tainted in the positive branch if: public class Sample3 { public boolean verify(@CheckTrue final Object o) throws Exception; } package javax.naming; import java.util.Hashtable; public interface Context { @Source("return") public NamingEnumeration list(String name) throws NamingException; } public interface NamingEnumeration<T> { @Prop(in="this", out="return") T next() throws NamingException; } In that case an issue won't be reported for the following code: final NamingEnumeration<NameClassPair> enumeration = context.list("*"); final Object o = enumeration.next(); if (verify(o)) { System.out.println(o); } But an issue will be reported for this code since the check was performed incorrectly: final NamingEnumeration<NameClassPair> enumeration = context.list("*"); final Object o = enumeration.next(); if (!verify(o)) { System.out.println(o); } Go to: Test the checker. After retesting the checker, we will add a check-false to the knowledge base. Add @CheckFalse to the knowledge baseIf verify should return false for the data allowed to be printed (reversed check) then the @CheckFalse annotation should be used. public class Sample3 { public boolean verify(@CheckFalse final Object o) throws Exception; } package javax.naming; import java.util.Hashtable; public interface Context { @Source("return") public NamingEnumeration list(String name) throws NamingException; } public interface NamingEnumeration<T> { @Prop(in="this", out="return") T next() throws NamingException; } Next step: Test the checker. If after re-testing the checker, we are satisfied with the analysis results, the checker is ready for deployment. See Deploying custom checkers. |