Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java Instrumentation at Contrast Security

Johnathan Gilday
September 12, 2018

Java Instrumentation at Contrast Security

The powerful Java Instrumentation API, and how Contrast Security uses it to detect vulnerable code paths, in two acts

Try Contrast Community Edition for free! https://www.contrastsecurity.com/contrast-community-edition

Johnathan Gilday

September 12, 2018
Tweet

More Decks by Johnathan Gilday

Other Decks in Technology

Transcript

  1. JAVA INSTRUMENTATION AT CONTRAST SECURITY How Contrast Security uses the

    powerful and obscure Java Instrumentation API to detect vulnerable code paths, in two acts [email protected] 2018-09-09
  2. ABOUT ME Johnathan Gilday Java agent developer at Contrast Security.

    Traditionally, full-stack web application and data services developer github.com/gilday @jdgilday johnathangilday.com [email protected] 2018-09-09
  3. JAVA INSTRUMENTATION AT CONTRAST SECURITY How Contrast Security uses the

    powerful and obscure Java Instrumentation API to detect vulnerable code paths, in two acts [email protected] 2018-09-09
  4. DID YOU KNOW THE JDK SHIPS WITH AN INSTRUMENTATION PACKAGE

    THAT CONTAINS JUST 5 CLASSES WHICH ALLOWS DEVELOPERS TO TRANSFORM JVM BYTE CODE AT RUNTIME? [email protected] 2018-09-09
  5. JAVA.LANG.INSTRUMENT Provides services that allow Java programming language agents to

    instrument programs running on the JVM. The mechanism for instrumentation is modification of the byte-codes of methods. — package docs [email protected] 2018-09-09
  6. HAVE YOU INSTRUMENTED YOUR CODE WITH A JAVA AGENT? >

    ⚡ APM: App Dynamics, New Relic > ✅ Code Coverage: JaCoCo > # Security: Contrast [email protected] 2018-09-09
  7. HOW TO USE AN AGENT? $ java -javaagent:contrast.jar -jar webgoat-container-7.0.1-war-exec.jar

    [Contrast] Sun Sep 09 20:24:43 EDT 2018 Starting Contrast (build 3.5.6.582) Pat. 8,458,789 B2 [Contrast] Sun Sep 09 20:24:43 EDT 2018 Logging messages to .contrast/contrast.log [Contrast] Sun Sep 09 20:24:43 EDT 2018 Logging security messages to .contrast/security.log [Contrast] Sun Sep 09 20:24:44 EDT 2018 Loading pre-packaged configuration [Contrast] Sun Sep 09 20:24:44 EDT 2018 Using instructions from TeamServer (Assess=on, Protect=off) [Contrast] Sun Sep 09 20:24:54 EDT 2018 Starting JVM [10711ms] Sep 09, 2018 8:24:55 PM org.apache.coyote.http11.Http11Protocol init INFO: Initializing ProtocolHandler ["http-bio-8080"] Sep 09, 2018 8:24:55 PM org.apache.catalina.core.StandardService startInternal INFO: Starting service Tomcat Typically use the -javaagent JVM flag to static load the agent [email protected] 2018-09-09
  8. SHOW ME THE CODE! Emoji Agent Replaces boring string literals

    with something more exciting $ java HelloWorld hello, world! $ java \ -javaagent:emoji-agent.jar \ -cp asm-all-5.2.jar:. \ HelloWorld ! hello, world! ignore this asm-all-5.2.jar for now , we'll get there [email protected] 2018-09-09
  9. WHERE CAN I FIND A SAMPLE JAVA PROGRAM? public final

    class HelloWorld { public static void main(final String[] args) { System.out.println("hello, world!"); } } javac HelloWorld.java [email protected] 2018-09-09
  10. ENTRYPOINT The manifest of the agent JAR file must contain

    the attribute Premain-Class. The value of this attribute is the name of the agent class. The agent class must implement a public static premain method similar in principle to the main application entry point. — java.lang.instrument JavaDoc public static void premain(String agentArgs, Instrumentation inst); [email protected] 2018-09-09
  11. EMOJIAGENT.JAVA package com.johnathangilday; public final class EmojiAgent { public static

    void premain(String args, Instrumentation instrumentation) { } } MANIFEST.MF Premain-Class: com.johnathangilday.EmojiAgent Can-Redefine-Classes: true [email protected] 2018-09-09
  12. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new EmojiClassFileTransformer());

    } static final class EmojiClassFileTransformer implements ClassFileTransformer { @Override public byte[] transform( final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) { throw new RuntimeException("not yet implemented"); } } [email protected] 2018-09-09
  13. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new EmojiClassFileTransformer());

    } static final class EmojiClassFileTransformer implements ClassFileTransformer { @Override public byte[] transform( final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) { throw new RuntimeException("not yet implemented"); } } [email protected] 2018-09-09
  14. public static void premain(String args, Instrumentation instrumentation) { instrumentation.addTransformer(new EmojiClassFileTransformer());

    } static final class EmojiClassFileTransformer implements ClassFileTransformer { @Override public byte[] transform( final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) { throw new RuntimeException("not yet implemented"); } } [email protected] 2018-09-09
  15. WHAT DOES HELLOWORLD BYTECODE LOOK LIKE? HelloWorld.java public final class

    HelloWorld { public static void main(final String[] args) { System.out.println("hello, world!"); } } HelloWorld.class ? [email protected] 2018-09-09
  16. HELLO, WORLD! IN BYTECODE $ javap -verbose -c HelloWorld.class Classfile

    /Users/johnathangilday/OneDrive - Contrast Security/Documents/instrumentation-talk/emoji-agent/HelloWorld.class Last modified Sep 9, 2018; size 427 bytes MD5 checksum 88c3d00dc24442b1e38d3ee7ec52b31b Compiled from "HelloWorld.java" public final class HelloWorld minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER Constant pool: #1 = Methodref #6.#15 // java/lang/Object."<init>":()V #2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #18 // hello, world! #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #21 // HelloWorld #6 = Class #22 // java/lang/Object #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 HelloWorld.java #15 = NameAndType #7:#8 // "<init>":()V #16 = Class #23 // java/lang/System #17 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #18 = Utf8 hello, world! #19 = Class #26 // java/io/PrintStream #20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V #21 = Utf8 HelloWorld #22 = Utf8 java/lang/Object #23 = Utf8 java/lang/System #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Utf8 java/io/PrintStream #27 = Utf8 println #28 = Utf8 (Ljava/lang/String;)V { public HelloWorld(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello, world! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 } SourceFile: "HelloWorld.java" [email protected] 2018-09-09
  17. HELLO, WORLD! IN LESS BYTECODE $ javap -c HelloWorld.class Compiled

    from "HelloWorld.java" public final class HelloWorld { public HelloWorld(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello, world! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } [email protected] 2018-09-09
  18. HELLO, WORLD! IN LESS BYTECODE $ javap -c HelloWorld.class Compiled

    from "HelloWorld.java" public final class HelloWorld { public HelloWorld(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String hello, world! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } [email protected] 2018-09-09
  19. BYTECODE MANIPULATION 101 > ASM is the defacto Java library

    for reading and writing bytecode > ASM uses a low-level, visitor pattern based reading and writing API > Higher-level libraries built on ASM are typically desirable [email protected] 2018-09-09
  20. EMOJIAGENT ASM 1. ClassReader for reading bytecode from byte[] 2.

    ClassWriter a visitor that writes instructions to byte[] buffer 3. EmojiClassVisitor a class visitor that composes the EmojiMethodVisitor 4. EmojiMethodVisitor a method visitor that looks for String constants to replace [email protected] 2018-09-09
  21. ASM MethodVisitor private static final class EmojiMethodVisitor extends MethodVisitor {

    EmojiMethodVisitor(final MethodVisitor methodVisitor) { super(ASM5, methodVisitor); } @Override public void visitLdcInsn(final Object value) { final Object newValue = "hello, world!".equals(value) ? " ! hello, world!" : value; super.visitLdcInsn(newValue); } } [email protected] 2018-09-09
  22. ASM ClassVisitor private static final class EmojiClassVisitor extends ClassVisitor {

    EmojiClassVisitor(final ClassVisitor classVisitor) { super(ASM5, classVisitor); } @Override public MethodVisitor visitMethod( final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { final MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new EmojiMethodVisitor(mv); } } [email protected] 2018-09-09
  23. ADD ASM-FU TO THE EmojiClassFileTransformer static final class EmojiClassFileTransformer implements

    ClassFileTransformer { public byte[] transform( final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) { final ClassReader reader; try { reader = new ClassReader(new ByteArrayInputStream(classfileBuffer)); } catch (IOException e) { throw new IllegalArgumentException("failed to read class " + className, e); } final int flags = 0; final ClassWriter writer = new ClassWriter(flags); final ClassVisitor visitor = new EmojiClassVisitor(writer); reader.accept(visitor, flags); return writer.toByteArray(); } } [email protected] 2018-09-09
  24. SHIP IT $ javac \ > -cp ~/.m2/repository/org/ow2/asm/asm-all/5.2/asm-all-5.2.jar \ >

    com/johnathangilday/EmojiAgent.java $ jar cfm emoji-agent.jar MANIFEST.MF com/johnathangilday/EmojiAgent*.class $ java \ > -javaagent:emoji-agent.jar \ > -cp ~/.m2/repository/org/ow2/asm/asm-all/5.2/asm-all-5.2.jar:. \ HelloWorld ! hello, world! [email protected] 2018-09-09
  25. DOES THIS WORK? public final class HelloWorld { public static

    void main(String[] args) { final String audience = args.length == 1 ? args[0] : "world"; System.out.println("hello, " + audience + "!"); } } [email protected] 2018-09-09
  26. DATA FLOW ANALYSIS Many vulnerabilities, including XSS, SQL injection, command

    injection, LDAP injection, XML injection, and more happen when programmers send untrusted data to dangerous calls.1 — Jeff Williams, Contrast Co-Founder 1 https://www.contrastsecurity.com/security-influencers/why-appsec-tools-need-great-data-flow-analysis [email protected] 2018-09-09
  27. WHERE IS THE DANGEROUS FLOW? @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet

    extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  28. WHERE IS THE DANGEROUS FLOW? @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet

    extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  29. WHERE IS THE DANGEROUS FLOW? @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet

    extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  30. XSS VULNERABILITY Harmless $ curl http://localhost:8080/hello-world?audience=mars <h1>Hello,mars</h1> Pwned $ curl

    'http://localhost:8080/hello-world?audience=<script>alert(1)</script>' <h1>Hello,<script>alert(1)</script></h1> [email protected] 2018-09-09
  31. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers [email protected] 2018-09-09
  32. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers [email protected] 2018-09-09
  33. SOURCE ! HttpServletRequest.getParameter() is a source - the String it

    returns is untrusted data. Remember the object reference of that String @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  34. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers [email protected] 2018-09-09
  35. PROPAGATOR @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet extends HttpServlet { @Override

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } Where is the propagator method? [email protected] 2018-09-09
  36. RECALL, THE JAVA COMPILER IMPLEMENTS THE String CONCATENATION OPERATOR AS

    METHOD CALLS final String greeting = "<h1>Hello," + audience + "</h1>"; Becomes final String greeting = new StringBuilder() .append("<h1>Hello,") .append(audience) .append("</h1>") .toString(); [email protected] 2018-09-09
  37. PROPAGATOR ! The StringBuilder.append(String) method is a propagator - it

    propagates the untrusted data passed in as a method parameter to the StringBuilder. Remember that the String built by this StringBuilder contains untrusted data @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  38. PROPAGATORS STORE THE UNTRUSTED SUBSET final String greeting = "<h1>Hello,"

    + audience + "</h1>"; <h1>Hello,mars!</h1> ^--^ untrusted data ^--------^ ^----^ safe constants [email protected] 2018-09-09
  39. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers [email protected] 2018-09-09
  40. SINK ! The PrintWriter.println(String) method of an instance returned by

    HttpServletResponse.getWriter() is a sink - when untrusted data that has not been HTML encoded is passed to this PrintWriter, there is an XSS vulnerability @WebServlet("/hello-world") public final class VulnerableHelloWorldServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String greeting = "<h1>Hello," + audience + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  41. ! XSS VULNERABILITY DETECTED Data from the HttpServletRequest source was

    propagated to a new String which was passed to the PrintWriter.println(String) sink without being sanitized [email protected] 2018-09-09
  42. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers [email protected] 2018-09-09
  43. LET'S PATCH THE VulnerableHelloWorldServlet ! The Apache Commons StringEscapeUtils.escapeHTML method

    is a sanitizer - the String instance returned by this method is HTML encoded @WebServlet("/hello-world") public final class HelloWorldServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { final String param = "audience"; final String audience = request.getParameter(param) == null ? "world" : request.getParameter(param); response.setContentType("text/html"); try (final PrintWriter out = response.getWriter()) { final String sanitized = StringEscapeUtils.escapeHTML(audience); final String greeting = "<h1>Hello," + sanitized + "</h1>"; out.println(greeting); } } } [email protected] 2018-09-09
  44. WHERE ARE YOUR VULNERABILITIES? Contrast Community Edition is free for

    Java developers Try it out then tell us how you feel! [email protected] 2018-09-09
  45. IMAGE CREDITS > Marek Cyzio https://flic.kr/p/22MbuxK > Pål-Kristian Hamre https://flic.kr/p/mn3ScM

    > ricky montalvo https://flic.kr/p/6882RG > Porapak Apichodilok https://www.pexels.com/photo/adult-barista-beverage-cafe-373639/ > Raw Pixel https://www.pexels.com/photo/yeah-with-brown-wooden-frame-745407/ > Photo by Patrick Fore on Unsplash > Photo by Christian Wiediger on Unsplash > Photo by Magda Ehlers from Pexels > Photo by Jairo Alzate on Unsplash > Photo by Waranya Mooldee on Unsplash > Photo by Leio McLaren (@leiomclaren) on Unsplash [email protected] 2018-09-09