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 johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 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? johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  6. HAVE YOU INSTRUMENTED YOUR CODE WITH A JAVA AGENT? >

    ⚡ APM: App Dynamics, New Relic > ✅ Code Coverage: JaCoCo > # Security: Contrast johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  10. HOW TO BUILD AN AGENT ☐ register bytecode transform function

    ☐ Transform JVM Bytecode johnathan.gilday@contrastsecurity.com 2018-09-09
  11. 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); johnathan.gilday@contrastsecurity.com 2018-09-09
  12. 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 johnathan.gilday@contrastsecurity.com 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"); } } johnathan.gilday@contrastsecurity.com 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"); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  15. 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"); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  16. HALF-WAY DONE? ☑ register bytecode transform function ☐ Transform JVM

    Bytecode johnathan.gilday@contrastsecurity.com 2018-09-09
  17. 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 ? johnathan.gilday@contrastsecurity.com 2018-09-09
  18. 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" johnathan.gilday@contrastsecurity.com 2018-09-09
  19. 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 } johnathan.gilday@contrastsecurity.com 2018-09-09
  20. 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 } johnathan.gilday@contrastsecurity.com 2018-09-09
  21. 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  22. 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  23. 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); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  24. 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); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  25. 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(); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  26. HOW TO BUILD AN AGENT ☑ register bytecode transform function

    ☑ Transform JVM Bytecode johnathan.gilday@contrastsecurity.com 2018-09-09
  27. 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! johnathan.gilday@contrastsecurity.com 2018-09-09
  28. 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 + "!"); } } johnathan.gilday@contrastsecurity.com 2018-09-09
  29. 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  30. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  31. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  32. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  33. 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> johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  35. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers johnathan.gilday@contrastsecurity.com 2018-09-09
  36. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  37. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers johnathan.gilday@contrastsecurity.com 2018-09-09
  38. 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? johnathan.gilday@contrastsecurity.com 2018-09-09
  39. 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(); johnathan.gilday@contrastsecurity.com 2018-09-09
  40. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  41. PROPAGATORS STORE THE UNTRUSTED SUBSET final String greeting = "<h1>Hello,"

    + audience + "</h1>"; <h1>Hello,mars!</h1> ^--^ untrusted data ^--------^ ^----^ safe constants johnathan.gilday@contrastsecurity.com 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  43. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  44. ! 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 johnathan.gilday@contrastsecurity.com 2018-09-09
  45. HOW DOES CONTRAST DISCOVER THE DATA FLOW USING INSTRUMENTATION? Instruments

    four categories of methods 1. Sources 2. Propagators 3. Sinks 4. Sanitizers johnathan.gilday@contrastsecurity.com 2018-09-09
  46. THERE ARE NO SANITIZERS IN THE VulnerableHelloWorldServlet (THAT'S WHY IT'S

    VULNERABLE) johnathan.gilday@contrastsecurity.com 2018-09-09
  47. 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); } } } johnathan.gilday@contrastsecurity.com 2018-09-09
  48. WHERE ARE YOUR VULNERABILITIES? Contrast Community Edition is free for

    Java developers Try it out then tell us how you feel! johnathan.gilday@contrastsecurity.com 2018-09-09
  49. 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 johnathan.gilday@contrastsecurity.com 2018-09-09