Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

ACT ONE: JAVA AGENTS [email protected] 2018-09-09

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

WE ARE TALKING ABOUT JAVA.LANG.INSTRUMENT [email protected] 2018-09-09

Slide 7

Slide 7 text

! [email protected] 2018-09-09

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

HAVE YOU INSTRUMENTED YOUR CODE WITH A JAVA AGENT? > ⚡ APM: App Dynamics, New Relic > ✅ Code Coverage: JaCoCo > # Security: Contrast [email protected] 2018-09-09

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

READY? [email protected] 2018-09-09

Slide 14

Slide 14 text

HOW TO BUILD AN AGENT ☐ register bytecode transform function ☐ Transform JVM Bytecode [email protected] 2018-09-09

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

USE Instrumentation TO REGISTER A ClassFileTransformer [email protected] 2018-09-09

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

HALF-WAY DONE? ☑ register bytecode transform function ☐ Transform JVM Bytecode [email protected] 2018-09-09

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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."":()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 #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 // "":()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."":()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

Slide 24

Slide 24 text

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."":()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

Slide 25

Slide 25 text

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."":()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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

HOW TO BUILD AN AGENT ☑ register bytecode transform function ☑ Transform JVM Bytecode [email protected] 2018-09-09

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

ACT TWO: DATA FLOW ANALYSIS VIA INSTRUMENTATION [email protected] 2018-09-09

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 37

Slide 37 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 38

Slide 38 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 39

Slide 39 text

XSS VULNERABILITY Harmless $ curl http://localhost:8080/hello-world?audience=mars

Hello,mars

Pwned $ curl 'http://localhost:8080/hello-world?audience=alert(1)'

Hello,alert(1)

[email protected] 2018-09-09

Slide 40

Slide 40 text

[email protected] 2018-09-09

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } Where is the propagator method? [email protected] 2018-09-09

Slide 46

Slide 46 text

RECALL, THE JAVA COMPILER IMPLEMENTS THE String CONCATENATION OPERATOR AS METHOD CALLS final String greeting = "

Hello," + audience + "

"; Becomes final String greeting = new StringBuilder() .append("

Hello,") .append(audience) .append("

") .toString(); [email protected] 2018-09-09

Slide 47

Slide 47 text

StringBuilder.append(String) AND StringBuilder.toString() ARE THE PROPAGATORS [email protected] 2018-09-09

Slide 48

Slide 48 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 49

Slide 49 text

[email protected] 2018-09-09

Slide 50

Slide 50 text

PROPAGATORS STORE THE UNTRUSTED SUBSET final String greeting = "

Hello," + audience + "

";

Hello,mars!

^--^ untrusted data ^--------^ ^----^ safe constants [email protected] 2018-09-09

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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 = "

Hello," + audience + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 53

Slide 53 text

! 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

Slide 54

Slide 54 text

[email protected] 2018-09-09

Slide 55

Slide 55 text

[email protected] 2018-09-09

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

THERE ARE NO SANITIZERS IN THE VulnerableHelloWorldServlet (THAT'S WHY IT'S VULNERABLE) [email protected] 2018-09-09

Slide 58

Slide 58 text

[email protected] 2018-09-09

Slide 59

Slide 59 text

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 = "

Hello," + sanitized + "

"; out.println(greeting); } } } [email protected] 2018-09-09

Slide 60

Slide 60 text

VULNERABILITY PATCHED [email protected] 2018-09-09

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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