├── .gitignore
├── LICENSE
├── README.md
├── config
├── blacklist-default
├── unit_test_config_1
├── unit_test_config_2
└── unit_test_config_3
├── mf
├── contrast.mf
├── safeois.mf
└── test.mf
├── pom.xml
├── run_tests.sh
└── src
├── main
└── java
│ └── com
│ ├── akamai
│ └── security
│ │ ├── SafeObjectInputStream.java
│ │ └── TestSafeObjectInputStream.java
│ └── contrastsecurity
│ └── rO0
│ ├── ObjectInputStreamVisitor.java
│ ├── RO0Agent.java
│ ├── RO0Config.java
│ ├── RO0Transformer.java
│ ├── ResolveClassController.java
│ ├── ResolveClassMethodVisitor.java
│ └── TestCases
│ ├── BlacklistElement.java
│ ├── IgnoreClassListElement.java
│ ├── Test.java
│ ├── TestMain.java
│ ├── TestRO0Config.java
│ ├── TestResolveClassController.java
│ ├── UnlistedElement.java
│ └── WhitelistElement.java
├── scripts
└── stats.pl
└── test
└── java
└── com
└── contrastsecurity
└── rO0
└── DeserializationTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | /.settings/
10 | /.classpath
11 | /.project
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Contrast Security OSS
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of rO0 nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | contrast-rO0
2 | ========
3 |
4 | A lightweight Java agent for preventing attacks against object deserialization
5 | like those discussed by [@breenmachine](http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#websphere)
6 | and the original researchers [@frohoff and @gebl](http://www.slideshare.net/frohoff1/appseccali-2015-marshalling-pickles), affecting WebLogic, JBoss, Jenkins and
7 | more.
8 |
9 | ## Why did you make this?
10 | This is the only way to hotpatch your application against this vulnerability.
11 | Patching the code is possible (and this package contains SafeObjectInputStream to help),
12 | but for many applications, patches will not be available for a long time. Because of
13 | the nature of the vulnerability, some applications will have to re-architect their
14 | messaging completely.
15 |
16 | ## How do I use the library?
17 |
18 |
19 | ### JVM-wide fix
20 |
21 | Build the agent, first:
22 | ```
23 | git clone https://github.com/Contrast-Security-OSS/contrast-rO0.git
24 | cd contrast-rO0
25 | mvn clean package test
26 | ```
27 | The agent, contrast-rO0.jar, is now in the /target directory. Now you need to copy the contrast-rO0.jar into your classpath, and copy the configuration file somewhere you'll remember.
28 |
29 | The final step is to add the following JVM options to your server or application:
30 | ```
31 | -javaagent:/path/to/contrast-rO0.jar -DrO0.reporting=false -DrO0.blacklist=true -DrOo.lists=filename
32 | ```
33 | where 'filename' is the path to the default config file.
34 |
35 | Now you're safe serializing from known dangerous classes!
36 |
37 |
38 | ### Spot Fix
39 |
40 | If you'd prefer to spot fix your code, possibly because you need to use dangerous classes somewhere, or because you need to minimize stability risk, you can use the SafeObjectInputStream class. To use this class, you'll need to replace calls to ObjectInputStream in your code with calls to SafeObjectInputStream.
41 |
42 | When you use SafeObjectInputStream, you can either whitelist or blacklist classes - whitelisting is safer, but blacklisting has lower stability risk. If you want to use this, you don't need the java command line options listed above. Instead, construct your SafeObejctInputStream, tell it if you want to blacklist or whitelist, and add the whitelisted/blacklisted classes to the stream. NOTE: you'll have to repeat this everywhere in your code where you need to implement safe deserialization.
43 |
44 | ```
45 | SafeObjectInputStream in
46 | = new SafeObjectInputStream(inputStream, true); // whitelisting mode
47 | // or
48 | // = new SafeObjectInputStream(inputStream, false); // blacklisting mode
49 | in.addToWhitelist(ClassThatIsSafeToDeserialize.getName());
50 | in.addToWhitelist("com.my.SafeDeserializable");
51 | // or
52 | // in.addToBlacklist(ClassThatIsDangerous.class);
53 |
54 | // then just use like normal
55 | in.readObject();
56 | ```
57 |
58 | ### Reporting on Serialization Usage
59 | The shim can be instructed to report when serialization occurs. This allows you to determine where in your application deserialization is actually occurring - assuming that you exercise the relevant functionality.
60 |
61 | To use this, built it just as described in "JVM-wide fix" but, use the following command line options instead:
62 |
63 | ```
64 | -javaagent:/path/to/contrast-rO0.jar
65 | ```
66 |
67 | ## What does it do?
68 | When protecting your application, the JVM-wide shim or the SafeObjectInputStream spot fix will throw a SecurityException if there is an attempt to deserialize a dangerous object.
69 |
70 | This represents the "last mile" of the exploit chain. The default blacklist contains the only publicly known proofs-of-concept, which are extremely unlikely to be used for legitimate purposes during deserialization.
71 |
72 | * org.apache.commons.collections.functors.InvokerTransformer
73 | * org.apache.commons.collections4.functors.InvokerTransformer
74 | * org.apache.commons.collections.functors.InstantiateTransformer
75 | * org.apache.commons.collections4.functors.InstantiateTransformer
76 | * org.codehaus.groovy.runtime.ConvertedClosure
77 | * org.codehaus.groovy.runtime.MethodClosure
78 | * org.springframework.beans.factory.ObjectFactory
79 |
80 | Though there likely exist other exploitable classes, they are difficult to find, and likely won't be part of any mass exploitation tool for a while.
81 |
82 | However, when they do become available, you can update your configuration file to include these classes in your blacklist. Or, if you want to be more secure, you can use this tool in "reporting" mode to learn what you're deserializing, and then specify a whitelist of the classes that you want to allow. If you go with this approach, and you don't happen to include a dangerous class in your whitelist, then new research finding additional dangerous classes won't affect you.
83 |
84 | ## What's the synopsis of all configuration options and usage modes?
85 |
86 | ```
87 | -DrO0.reporting=true Enable reporting (). Reporting is enabled by
88 | default. Intended for use if we ever use this
89 | package for protecting instead of just reporting.
90 | -DrOo.lists=filename Specify a configuration file. File specifies
91 | classes to include in the whitelist, blacklist,
92 | ignore classes list, or ignore stack trace list.
93 | FIRST MEANING
94 | CHAR
95 | + If the line starts with +, it’s included in the whitelist.
96 | Attempts to deserialize any classes not whitelisted will
97 | throw a SecurityException. Only has effect when
98 | -DrO0.whitelist is enabled.
99 |
100 | - If the line starts with -, it’s included in the blacklist.
101 | Attempts to deserialize these classes will throw a
102 | SecurityException. Only has effect when -DrO0.blacklist is
103 | enabled.
104 |
105 | $ If the line starts with $, it’s included in the “ignore
106 | class list”. Classes in this list will not be reported.
107 | Only has effect when -DrO0.ignoreClasses is enabled
108 |
109 | @ If the line starts with @, it’s included in the “ignore
110 | in stack list”. When a class is deserialized and the class
111 | specified by this line is in the stack, don’t report. Only
112 | has effect when -DrO0.ignoreStack is enabled.
113 |
114 | -DrO0.whitelist=true Enable whitelisting. Classes not included in the
115 | config file as whitelisted will not be allowed to
116 | deserialize. You can enable this at the same time as
117 | blacklisting. See the section on "enabling both
118 | blacklisting and whitelisting at the same time" for
119 | details.
120 |
121 | -DrO0.blacklist=true Enable blacklisting. Classes included in the config
122 | file as blacklisted will not be allowed to
123 | deserialized; all other classes will deserialize
124 | normally. You can enable this at the same time as
125 | blacklisting. See the section on "enabling both
126 | blacklisting and whitelisting at the same time" for
127 | details.
128 |
129 | -DrO0.ignoreClasses=true Used to quiet down the tool when it’s doing
130 | reporting. Classes or packages listed as ignored
131 | will not be reported upon. Only useful if reporting
132 | is enabled. Can be used in combination with
133 | -DrO0.ignoreStack.
134 |
135 | -DrO0.ignoreStack=true Used to quiet down the tool when it’s doing
136 | reporting. If the specified class or package is
137 | in the stack during deserialization, that
138 | deserialize attempt will not be reported upon.
139 | For example, used to stop logging all memcached
140 | events if @com.danga.Memcached is included in the
141 | config file and ignoreStack is enabled., memcached
142 | should be much less pronounced in the logs. Likely
143 | to have noticeable performance impact. Can be
144 | specified in combination with -DrO0.gnoreClasses.
145 | Only useful if reporting is enabled.
146 |
147 | -DrO0.outfile=filename Used to control where output from this utility is sent.
148 | If no output is specified, it will go to System.out. If
149 | filename is specified, rO0 will try to write
150 | output to the specified location; if it cannot (for example
151 | if permissions disallow this) then rO0 will instead
152 | fall back to System.out.
153 | ```
154 |
155 | ### Enabling both blacklisting and whitelisting at the same time
156 | This tool allows you to enable both blacklisting and whitelisting at the same time.
157 | If you do so, a class will only be allowed to deserialize if it's *both* on the
158 | whitelist and not on the blacklist. This is useful if you want to maintain a list
159 | of known dangerous classes that you never want to deserialize as backup in case
160 | someone accidentally or unknowingly adds that same class to the white list.
161 |
162 | ## Supported systems
163 | Although it's not tested on them all, the agent should work well on the following platforms:
164 | * Java 5-8
165 | * OpenJDK/HotSpot, JRockit, IBM
166 |
167 | ## Who made this?
168 | This project is sponsored by [Contrast Security](http://www.contrastsecurity.com/) and released under the BSD license for developers. It includes contributions from Akamai Technologies.
169 |
170 | 
171 |
--------------------------------------------------------------------------------
/config/blacklist-default:
--------------------------------------------------------------------------------
1 | # Default blacklist that contains all currently known dangerous
2 | # classes.
3 | -org.apache.commons.collections.functors.InvokerTransformer
4 | -org.apache.commons.collections.functors.InstantiateTransformer
5 | -org.apache.commons.collections4.functors.InvokerTransformer
6 | -org.apache.commons.collections4.functors.InstantiateTransformer
7 | -org.codehaus.groovy.runtime.ConvertedClosure
8 | -org.codehaus.groovy.runtime.MethodClosure
9 | -org.springframework.beans.factory.ObjectFactory
10 |
--------------------------------------------------------------------------------
/config/unit_test_config_1:
--------------------------------------------------------------------------------
1 | ############
2 | # HELP
3 | #
4 | # First character Meaning
5 | # -------------- -------
6 | # # comment
7 | # - blacklist
8 | # + whitelist
9 | # $ ignore during reporting if CLASS matches
10 | # @ ignore during reporting if on stack
11 | +com.contrastsecurity.rO0.TestCases.WhitelistElement
12 | -com.contrastsecurity.rO0.TestCases.BlacklistElement
13 | $com.contrastsecurity.rO0.TestCases.IgnoreClassListElement
14 |
--------------------------------------------------------------------------------
/config/unit_test_config_2:
--------------------------------------------------------------------------------
1 | ############
2 | # HELP
3 | #
4 | # First character Meaning
5 | # -------------- -------
6 | # # comment
7 | # - blacklist
8 | # + whitelist
9 | # $ ignore during reporting if CLASS matches
10 | # @ ignore during reporting if on stack
11 | @com.contrastsecurity.rO0.TestRO0Agent
12 | @com.contrastsecurity.Ro).TestResolveClassController
13 |
--------------------------------------------------------------------------------
/config/unit_test_config_3:
--------------------------------------------------------------------------------
1 | ############
2 | # HELP
3 | #
4 | # First character Meaning
5 | # -------------- -------
6 | # # comment
7 | # - blacklist
8 | # + whitelist
9 | # $ ignore during reporting if CLASS matches
10 | # @ ignore during reporting if on stack
11 | @com.contrastsecurity.rO0.TestRO0Config
12 |
--------------------------------------------------------------------------------
/mf/contrast.mf:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Created-By: Aaron Katz
3 | Agent-Class: com.contrastsecurity.rO0.RO0Agent
4 | Premain-Class: com.contrastsecurity.rO0.RO0Agent
5 | Boot-Class-Path: /Users/akatz/git/rO0/contrast-ro0/bin/contrast-rO0.jar
6 | Can-Redefine-Classes: true
7 | Can-Retransform-Classes: true
8 | Can-Set-Native-Method-Prefix: true
9 |
10 |
--------------------------------------------------------------------------------
/mf/safeois.mf:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Created-By: Aaron Katz
3 | Main-Class: com.akamai.security.TestSafeObjectInputStream
4 |
5 |
--------------------------------------------------------------------------------
/mf/test.mf:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Created-By: Aaron Katz
3 | Main-Class: com.contrastsecurity.rO0.TestCases.TestMain
4 |
5 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.contrastsecurity
5 | contrast-rO0
6 | 1.0.0-SNAPSHOT
7 |
8 |
9 |
10 | org.ow2.asm
11 | asm-debug-all
12 | 5.0.3
13 |
14 |
15 | junit
16 | junit
17 | 4.8.2
18 | test
19 |
20 |
21 | commons-collections
22 | commons-collections
23 | 3.2.1
24 |
25 |
26 | org.springframework
27 | spring-core
28 | 4.2.3.RELEASE
29 | test
30 |
31 |
32 |
33 |
34 |
35 | contrast-rO0
36 |
37 |
38 | org.apache.maven.plugins
39 | maven-shade-plugin
40 | 1.6
41 |
42 |
43 | package
44 |
45 | shade
46 |
47 |
48 |
49 |
50 |
51 |
52 | *:*
53 |
54 | com/contrastsecurity/rO0/**
55 | org/objectweb/**
56 | META-INF/MANIFEST.MF
57 |
58 |
59 |
60 |
61 |
62 | org.objectweb
63 | com.contrastsecurity.rO0.thirdparty.org.objectweb
64 |
65 |
66 |
67 |
68 |
69 | org.apache.maven.plugins
70 | maven-jar-plugin
71 | 2.3.1
72 |
73 |
74 |
75 |
76 | com.contrastsecurity.rO0.RO0Agent
77 |
78 |
79 |
80 | com.contrastsecurity.rO0.RO0Agent
81 | com.contrastsecurity.rO0.RO0Agent
82 | ${build.finalName}.jar
83 | true
84 | true
85 | true
86 | ${maven.build.timestamp}
87 |
88 |
89 |
90 |
91 |
92 | maven-surefire-plugin
93 | 2.9
94 |
95 | false
96 | true
97 |
98 | -javaagent:${project.build.directory}/${build.finalName}.jar=rO0.reporting:false,rO0.blacklist:true,rO0.lists:${basedir}/config/blacklist-default
99 |
100 |
101 |
102 |
103 | verify-serialization-security
104 | integration-test
105 |
106 | test
107 |
108 |
109 | alphabetical
110 | false
111 | once
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/run_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "moving to ./bin"
4 | pushd bin
5 |
6 | echo "recompiling"
7 | javac ../src/main/java/com/contrastsecurity/rO0/*.java ../src/main/java/com/contrastsecurity/rO0/TestCases/*.java ../src/main/java/com/akamai/security/*.java -classpath ../lib/asm-5.0.4.jar:../lib/asm-commons-5.0.4.jar -d .
8 |
9 | echo "creating jar files"
10 | jar cfm contrast-test.jar ../mf/test.mf com/contrastsecurity/rO0/TestCases/*.class
11 | jar cfm contrast-ro0.jar ../mf/contrast.mf com/contrastsecurity/rO0/*.class
12 | jar cfm contrast-ro0-spotfix.jar ../mf/safeois.mf com/akamai/security/*.class
13 |
14 | echo "copying dependencies"
15 | cp ../lib/* .
16 |
17 | echo "executing tests"
18 | export JARDIR=`pwd`
19 | java -javaagent:${JARDIR}/contrast-ro0.jar -Xbootclasspath/p:"${JARDIR}/contrast-rO0.jar:${JARDIR}/asm-5.0.4.jar:${JARDIR}/asm-commons-5.0.4.jar" -jar contrast-test.jar -Dfile.encoding=UTF-8 -classpath ${JARDIR}/asm-5.0.4.jar:${JARDIR}/asm-commons-5.0.4.jar
20 |
21 | java com/akamai/security/TestSafeObjectInputStream
22 |
23 | # clean up
24 | popd
25 |
--------------------------------------------------------------------------------
/src/main/java/com/akamai/security/SafeObjectInputStream.java:
--------------------------------------------------------------------------------
1 | package com.akamai.security;
2 |
3 | import com.contrastsecurity.rO0.RO0Config;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.ObjectInputStream;
7 | import java.io.ObjectStreamClass;
8 | import java.util.Hashtable;
9 |
10 | public class SafeObjectInputStream extends ObjectInputStream {
11 |
12 | RO0Config config = new RO0Config();
13 |
14 | Hashtable classList = new Hashtable();
15 |
16 | /**
17 | * Construct a new SafeObjectInputStream, specifying whether it is
18 | * in whitelisting mode or blacklisting mode. If whitelisting,
19 | * only classes that are added to the whitelist (via addToWhitelist())
20 | * will be allowed to deserialize. If it's in blacklist mode, only
21 | * classes not on the blacklist will be allowed to deserialize.
22 | *
23 | * @param isWhitelist true if in whiteslist mode; false if in blacklist
24 | * mode.
25 | */
26 | public SafeObjectInputStream(boolean isWhitelist) throws IOException {
27 | super();
28 | this.config.setWhitelisting(isWhitelist);
29 | this.config.setBlacklisting(!isWhitelist);
30 | }
31 |
32 | public SafeObjectInputStream(InputStream in, boolean isWhitelist) throws IOException {
33 | super(in);
34 | this.config.setWhitelisting(isWhitelist);
35 | this.config.setBlacklisting(!isWhitelist);
36 | }
37 |
38 | /* By calling this function and adding a class to the whitelist,
39 | * you are attesting that the class you have whitelisted is
40 | * completely safe, in that it and its parent constructors do
41 | * NOTHING other than intiialize the class - no business logic, no
42 | * threads or processes are started, and nothing that is automatically
43 | * operated upon by other classes. DTOs that contain no business logic
44 | * are generally safe. Other classes may or may not be safe.
45 | * As a quick hint, don't do System.exec(), reflection, or reserve
46 | * large resources during initialization.
47 | */
48 | public void addToWhitelist(Class> klass) {
49 | config.addToWhitelist(klass);
50 | }
51 |
52 | public void addToBlacklist(Class> klass) {
53 | config.addToBlacklist(klass);
54 | }
55 |
56 | public RO0Config getConfig() { return config; }
57 |
58 | public void setConfig(RO0Config config){ this.config = config; }
59 |
60 | protected Class> resolveClass(ObjectStreamClass desc)
61 | throws IOException, ClassNotFoundException
62 | {
63 | String name = desc.getName();
64 |
65 | if( config.isBlacklisted(name) ) {
66 | String message = "Attempt to deserialize blacklisted class:" + name;
67 | throw new SecurityException(message);
68 | }
69 |
70 | if( ! config.isWhitelisted(name) ) {
71 | String message = "Attempt to deserialize non-whitelisted class: " + name;
72 | throw new SecurityException(message);
73 | }
74 |
75 | return super.resolveClass(desc);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/com/akamai/security/TestSafeObjectInputStream.java:
--------------------------------------------------------------------------------
1 | package com.akamai.security;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.IOException;
6 | import java.io.ObjectOutputStream;
7 | import java.io.Serializable;
8 | import java.util.Arrays;
9 |
10 | import com.contrastsecurity.rO0.TestCases.Test;
11 | import com.contrastsecurity.rO0.TestCases.BlacklistElement;
12 | import com.contrastsecurity.rO0.TestCases.UnlistedElement;
13 | import com.contrastsecurity.rO0.TestCases.WhitelistElement;
14 |
15 | public class TestSafeObjectInputStream
16 | extends Test
17 | {
18 | public static void main(String[] args)
19 | {
20 | TestSafeObjectInputStream test = new TestSafeObjectInputStream();
21 | try {
22 | test.run();
23 | } catch (Throwable t) {
24 | out("FAIL! FAIL! FAIL: Unexpected exception");
25 | out(t);
26 | }
27 | }
28 |
29 | public void run() throws IOException {
30 | out("--------------------------------------------------");
31 | out("----- Beginning SafeObjectInputStream tests -----");
32 |
33 | testWhitelist();
34 | testBlacklist();
35 | }
36 |
37 | private void testWhitelist() throws IOException
38 | {
39 | out("----- Test in whitelisting mode -----");
40 | WhitelistElement whitelistElement = new WhitelistElement();
41 | BlacklistElement blacklistElement = new BlacklistElement();
42 | UnlistedElement unlistedElement = new UnlistedElement();
43 |
44 | byte[] w = serialize(whitelistElement);
45 | byte[] b = serialize(blacklistElement);
46 | byte[] u = serialize(unlistedElement);
47 |
48 | ByteArrayInputStream bin_w = new ByteArrayInputStream(w);
49 | ByteArrayInputStream bin_b = new ByteArrayInputStream(b);
50 | ByteArrayInputStream bin_u = new ByteArrayInputStream(u);
51 |
52 | SafeObjectInputStream in_w = new SafeObjectInputStream(bin_w, true);
53 | SafeObjectInputStream in_b = new SafeObjectInputStream(bin_b, true);
54 | SafeObjectInputStream in_u = new SafeObjectInputStream(bin_u, true);
55 |
56 | in_w.addToWhitelist(WhitelistElement.class);
57 | in_b.addToWhitelist(WhitelistElement.class);
58 | in_u.addToWhitelist(WhitelistElement.class);
59 |
60 | test("verifying whitelisted object deserializes with whitelist",
61 | tryToDeserialize(w, in_w),
62 | true);
63 | test("verifying blacklisted object doesn't deserialize with whitelist",
64 | tryToDeserialize(b, in_b),
65 | false);
66 | test("verifying unlisted object doesn't deserialize with whitelist",
67 | tryToDeserialize(u, in_u),
68 | false);
69 | }
70 |
71 | private void testBlacklist() throws IOException
72 | {
73 | out("----- Test in blacklisting mode -----");
74 | WhitelistElement whitelistElement = new WhitelistElement();
75 | BlacklistElement blacklistElement = new BlacklistElement();
76 | UnlistedElement unlistedElement = new UnlistedElement();
77 |
78 | byte[] w = serialize(whitelistElement);
79 | byte[] b = serialize(blacklistElement);
80 | byte[] u = serialize(unlistedElement);
81 |
82 | ByteArrayInputStream bin_w = new ByteArrayInputStream(w);
83 | ByteArrayInputStream bin_b = new ByteArrayInputStream(b);
84 | ByteArrayInputStream bin_u = new ByteArrayInputStream(u);
85 |
86 | SafeObjectInputStream in_w = new SafeObjectInputStream(bin_w, false);
87 | SafeObjectInputStream in_b = new SafeObjectInputStream(bin_b, false);
88 | SafeObjectInputStream in_u = new SafeObjectInputStream(bin_u, false);
89 |
90 | in_w.addToBlacklist(BlacklistElement.class);
91 | in_b.addToBlacklist(BlacklistElement.class);
92 | in_u.addToBlacklist(BlacklistElement.class);
93 |
94 | test("verifying whitelisted object deserializes with blacklist",
95 | tryToDeserialize(w, in_w),
96 | true);
97 | test("verifying blacklisted object doesn't deserialize with blacklist",
98 | tryToDeserialize(b, in_b),
99 | false);
100 | test("verifying unlisted object doesn't deserializes with blacklist",
101 | tryToDeserialize(u, in_u),
102 | true);
103 | }
104 |
105 |
106 | private boolean tryToDeserialize(byte[] orig_bytes, SafeObjectInputStream in)
107 | {
108 | try {
109 | // this first line will throw an exception if deserialization is
110 | // not allowed
111 | Serializable object = (Serializable)in.readObject();
112 |
113 | // these lines ensure that the object really deserialized correctly
114 | // and our test is valid.
115 | byte[] new_bytes = serialize(object);
116 | if(Arrays.equals(orig_bytes, new_bytes)) return true;
117 | } catch ( SecurityException se ) {
118 | return false;
119 | } catch ( IOException ioe ) {
120 | throw new RuntimeException(ioe);
121 | } catch (ClassNotFoundException cnfe) {
122 | throw new RuntimeException(cnfe);
123 | }
124 | return false;
125 | }
126 |
127 | private byte[] serialize(Serializable object) throws IOException
128 | {
129 | ByteArrayOutputStream bytes = new ByteArrayOutputStream();
130 | ObjectOutputStream out = new ObjectOutputStream(bytes);
131 | out.writeObject(object);
132 | return bytes.toByteArray();
133 | }
134 |
135 | private static void out(String s)
136 | {
137 | System.out.println(s);
138 | System.out.flush();
139 | }
140 |
141 | private static void out(Throwable t)
142 | {
143 | t.printStackTrace();
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/ObjectInputStreamVisitor.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import org.objectweb.asm.ClassVisitor;
4 | import org.objectweb.asm.MethodVisitor;
5 | import org.objectweb.asm.Opcodes;
6 |
7 | public class ObjectInputStreamVisitor extends ClassVisitor {
8 |
9 | public ObjectInputStreamVisitor(ClassVisitor cv) {
10 | super(Opcodes.ASM5, cv);
11 | }
12 |
13 | @Override
14 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
15 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
16 | if("resolveClass".equals(name) && "(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;".equals(desc)) {
17 | mv = new ResolveClassMethodVisitor(mv, access, name, desc);
18 | }
19 | return mv;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/RO0Agent.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.PrintStream;
7 | import java.lang.instrument.ClassFileTransformer;
8 | import java.lang.instrument.Instrumentation;
9 | import java.util.Properties;
10 |
11 | /**
12 | * Install a ClassFileTransformer to instrument ObjectInputStream.
13 | */
14 | public class RO0Agent {
15 |
16 | public static RO0Config config = new RO0Config();
17 |
18 | // added to make unit testing / prerequisite verification easier
19 | public static boolean loaded = false;
20 |
21 | /**
22 | * The location where we'll write our output. Defaults to System.out, but
23 | * can be configured to go elsewhere.
24 | */
25 | private static PrintStream out = System.out;
26 |
27 | /**
28 | * configuration properties as passed in to premain and/or agentmain
29 | */
30 | static private Properties properties = null;
31 |
32 |
33 | public static void premain(String args, Instrumentation inst) throws IOException {
34 | setup(args, inst);
35 | }
36 |
37 | public static void agentmain(String args, Instrumentation inst) throws IOException {
38 | setup(args, inst);
39 | }
40 |
41 | private static void setup(String args, Instrumentation inst) {
42 | properties = parseCommandLine(args);
43 |
44 | ClassFileTransformer xform = new RO0Transformer();
45 | inst.addTransformer(xform);
46 | readConfig(args);
47 | }
48 |
49 | private static void readConfig(String args) {
50 |
51 |
52 | String outfile = getProperty("rO0.outfile");
53 |
54 | if( outfile != null && !outfile.equals("") ) {
55 | out("redirecting output to " + outfile);
56 | try {
57 | out = new PrintStream(new FileOutputStream(outfile));
58 | } catch (FileNotFoundException e) {
59 | out("failed to redirect output. Sending output to System.out.");
60 | e.printStackTrace();
61 | }
62 | }
63 |
64 | try {
65 | String listfile = getProperty("rO0.lists");
66 | config.readConfig(listfile);
67 | } catch (FileNotFoundException e) {
68 | out("failed to read config file");
69 | out(e);
70 | }
71 |
72 | String _reporting = getProperty("rO0.reporting");
73 | String _ignoreClasses = getProperty("rO0.ignoreClasses");
74 | String _ignoreStack = getProperty("rO0.ignoreStack");
75 | String _whitelist = getProperty("rO0.whitelist");
76 | String _blacklist = getProperty("rO0.blacklist");
77 | debug("blacklist = " + _blacklist);
78 |
79 | /* Read configuration from the environment (see the README for details about how to set this
80 | * stuff). Default is that nothing is enabled.
81 | *
82 | * If it's null, it's not set, so it's disabled. If it's not "true" it's not set as we expect
83 | * so it's disabled.
84 | */
85 |
86 | // This one is enabled by default, so the "null" is opposite. If it's present
87 | // it must be "true" to be enabled, but if it's not present, it's still true.
88 | boolean reportingEnabled = ( _reporting == null || _reporting.equals("true") );
89 |
90 | // These are normal
91 | boolean classIgnoreEnabled = ( _ignoreClasses != null && _ignoreClasses.equals("true") );
92 | boolean stackIgnoreEnabled = ( _ignoreStack != null && _ignoreStack.equals("true") );
93 | boolean whitelistEnabled = ( _whitelist != null && _whitelist.equals("true") );
94 | boolean blacklistEnabled = ( _blacklist != null && _blacklist.equals("true") );
95 |
96 | config.setReporting(reportingEnabled);
97 | config.setClassFiltering(classIgnoreEnabled);
98 | config.setStackFiltering(stackIgnoreEnabled);
99 | config.setWhitelisting(whitelistEnabled);
100 | config.setBlacklisting(blacklistEnabled);
101 |
102 | debug("Configuration = " + config.toString());
103 |
104 | loaded = true;
105 | }
106 |
107 |
108 | /**
109 | * Reads the specified property from the properties object; if not found,
110 | * checks the environment.
111 | *
112 | * @param string the property to read
113 | * @param properties the properties to check
114 | *
115 | * @return the value as specified by the properties object; if it does not exist
116 | * in the properties object, the value as specified in the environment;
117 | * null if not found anywhere.
118 | */
119 | private static String getProperty(String string) {
120 | if( properties == null ) {
121 | return null;
122 | }
123 |
124 | return properties.getProperty(string, System.getProperty(string));
125 | }
126 |
127 | private static Properties parseCommandLine(String args) {
128 | debug("parsing command line:" + args);
129 | Properties properties = new Properties();
130 | if( args == null ) return properties;
131 |
132 | // key value pairs separated by commas
133 | String[] pairs = args.split(",");
134 | for( String pair : pairs) {
135 | debug("kvpair = " + pair);
136 | if ( pair.length() == 0 ) continue;
137 |
138 | String[] key_value = pair.split(":");
139 | String key = key_value[0];
140 | String value = (key_value.length > 1) ? key_value[1] : "";
141 | properties.setProperty(key, value);
142 | debug("Added key="+key+" value="+value);
143 | }
144 |
145 | debug("properties = " + properties);
146 | return properties;
147 | }
148 |
149 | public static void out(String msg) {
150 | String quiet = getProperty("rO0.quiet");
151 | if(quiet == null || !"true".equalsIgnoreCase(quiet)) {
152 | out.println("[contrast-rO0] " + msg);
153 | }
154 | }
155 |
156 |
157 | public static void out(Throwable t) {
158 | String veryQuiet = getProperty("rO0.quiet");
159 | String quiet = getProperty("rO0.noStackTraces");
160 |
161 | if( veryQuiet != null && "true".equalsIgnoreCase(veryQuiet) ) return;
162 | if( quiet != null && "true".equalsIgnoreCase(quiet) ) return;
163 |
164 | t.printStackTrace(out);
165 | }
166 |
167 | public static void debug(String s) {
168 | String debug = getProperty("rO0.debug");
169 | if( debug != null && debug.equals("true") )
170 | {
171 | out.println("[contrast.rO0] DEBUG: " + s);
172 | }
173 | }
174 |
175 | public static void main(String[] args) {
176 | out.println("********************************************************************");
177 | out.println("* contrast-rO0 - the hotpatching agent for deserialization attacks *");
178 | out.println("********************************************************************");
179 | out.println();
180 | out.println("To use contrast-rO0, add it as a Java agent with the following flag: ");
181 | out.println(" -javaagent:/path/to/contrast-rO0.jar");
182 | out.println();
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/RO0Config.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.FileNotFoundException;
5 | import java.io.FileReader;
6 | import java.io.IOException;
7 | import java.util.Vector;
8 |
9 | public class RO0Config {
10 |
11 | /** Flag indicating if reporting is enalbed or disabled. Enabled by default. **/
12 | private boolean reportingEnabled = true;
13 |
14 | /**
15 | * Flag indicating whether we're supposed to ignore any classes during reporting.
16 | * Enabled if the config file specified ignore classes.
17 | */
18 | private boolean classIgnoreEnabled = false;
19 |
20 | /**
21 | * Flag indicating whether we're supposed to ignore any stack traces during
22 | * blacklisting. This can be used to indicate trusted code, but USE WITH CARE.
23 | */
24 | private boolean stackWhitelistEnabled = false;
25 |
26 | /**
27 | * List of classes to ignore during reporting. That is, don't report if there are
28 | * deserialization attempts for these classes. This is to reduce the noise in the log
29 | * file for known safe stuff.
30 | */
31 | private Vector classIgnoreList = new Vector();
32 |
33 |
34 | /**
35 | * Flag indicating that we should ignore deserialization attempts for classes with any of
36 | * the listed superclasses in their stack. This is intended to allow us to ignroe things like
37 | * serialization via memcache. This can be helpful when trying to figure out whwere in the
38 | * application deserialization occurs, as it allows us to ignore the stuff we know about and
39 | * keep the output log limited to the stuff we want to investigate.
40 | */
41 | private boolean stackIgnoreEnabled = false;
42 |
43 | /**
44 | * List of classes that, if found on the stack while in reporting mode, should result in NOT logging
45 | * the deserialization attempt.
46 | */
47 | private Vector stackIgnoreList = new Vector();
48 |
49 | /**
50 | * Flag to indicate if we're in whitelisting mode. Deserailziation of expected classes will be
51 | * allowed. Deserialization of non-whitelisted classes will result in a SecurityException.
52 | * Note that whitelisting is prone to usability/funcitonal failure if it's incomplete. If you
53 | * don't whitelist a class you need, you will not have access to that functionality.
54 | */
55 | private boolean whitelistEnabled = false;
56 |
57 | /**
58 | * If we're in whitelisting mode, this array will contain all whitelisted classes. Deserialization
59 | * of these classes will be allowed; deserialization of anything unlisted willr esult in a SecurityException
60 | */
61 | private Vector whitelist = new Vector();
62 |
63 | /**
64 | * Flag to indicate if blacklisting is enabled. If it is, deserialization attempts on listed classes
65 | * will result in a SecurityException; deserialization attempts on unlisted classes will be allowed.
66 | * Note that blacklisting is inherently prone to security failure - if you fail to blacklist a class
67 | * that is dangerous, you'll have a vulnerability.
68 | */
69 | private boolean blacklistEnabled = false;
70 |
71 | /**
72 | * List of classes that are blacklisted. If blacklisting is enalbed, attempts to load these
73 | * classes will result in a SecurityException.
74 | */
75 | private Vector blacklist = new Vector();
76 |
77 | /**
78 | * Read the specified file and parse the configuration.
79 | * @param filename
80 | * @throws FileNotFoundException
81 | */
82 | public void readConfig(String filename) throws FileNotFoundException {
83 | if( filename == null || filename.equals("") ) return;
84 | readConfig(new FileReader(filename));
85 | }
86 |
87 | /**
88 | * See readConfig(String)
89 | *
90 | * @param file
91 | */
92 | public void readConfig(FileReader file) {
93 | if(file == null) return;
94 |
95 | BufferedReader in = null;
96 | try {
97 | in = new BufferedReader(file);
98 |
99 | String line = null;
100 | do {
101 | line = in.readLine();
102 | if( line == null ) continue;
103 | line = line.trim();
104 |
105 | if(line.startsWith("#")) continue; // # is comment character if 1st char in line
106 | if(line.startsWith("-")) addToBlacklist(line.substring(1,line.length()).trim());
107 | if(line.startsWith("+")) addToWhitelist(line.substring(1,line.length()).trim());
108 | if(line.startsWith("$")) addToClassIgnoreList(line.substring(1,line.length()).trim());
109 | if(line.startsWith("@")) addToStackIgnoreList(line.substring(1,line.length()).trim());
110 | } while(line != null );
111 | } catch (FileNotFoundException fnfe) {
112 | RO0Agent.out("Unable to set up ignore list");
113 | fnfe.printStackTrace();
114 | } catch (IOException ioe) {
115 | RO0Agent.out("Error reading ignorelist config file");
116 | ioe.printStackTrace();
117 | } finally {
118 | try { if( in != null ) { in.close(); } } catch ( Exception e) { /* do nothing*/ }
119 | }
120 | }
121 |
122 | /**
123 | * Called during reporting to check if the current class is filtered out of the
124 | * report. It's safe to call this without first checking if reporting is enabled;
125 | * this method returns false if reporting is disabled.
126 | *
127 | * @param className
128 | * @return true if the event should be included in the output report. False if it should NOT be
129 | * included in the report. "false" can be the case if reporting is disabled, of if the class
130 | * or an element in the current call stack is filtered out via the config file.
131 | */
132 | public boolean includeInReport(String className) {
133 | if( ! getReportingEnabled() )
134 | {
135 | return false;
136 | }
137 |
138 | if( this.getClassIgnoreEnabled() && isOnClassIgnoreList(className) )
139 | {
140 | return false;
141 | }
142 |
143 | if( getStackIgnoreEnabled() && isOnStackIgnoreList(Thread.currentThread().getStackTrace()))
144 | {
145 | return false;
146 | }
147 |
148 | return true;
149 | }
150 |
151 | public void setStackFiltering(boolean enabled)
152 | {
153 | this.stackIgnoreEnabled = enabled;
154 | }
155 |
156 | public void setClassFiltering(boolean enabled)
157 | {
158 | this.classIgnoreEnabled = enabled;
159 | }
160 |
161 | /**
162 | * Internal helper method to determine if a class name is on the list of classes to ignore.
163 | *
164 | * @param name the class name to check
165 | *
166 | * @return ture if it's on the list; false if it's not on the list
167 | */
168 | private boolean isOnClassIgnoreList(String name){
169 | return isOnList(classIgnoreList, name);
170 | }
171 |
172 | /**
173 | * Internal helper method to determine if the specified stack trace contains any element
174 | * that is on the list of stack trace elements to ignore. A match indicates that the
175 | * current stack trace matches something from the configuration file.
176 | *
177 | * @param stackTrace the stack trace to check
178 | * @return true if there's a match; false if there's no match.
179 | */
180 | private boolean isOnStackIgnoreList(StackTraceElement[] stackTrace) {
181 | if( !getStackIgnoreEnabled() || getStackWhitelistEnabled() ) {
182 | return false;
183 | }
184 |
185 | if( stackIgnoreList.isEmpty() ) return false;
186 |
187 | for( int i=0; iNOTE: This method DOES NOT check the call stack, just the specific class. This is because
228 | * checking the call stack could have a performance hit in production, which we are not (yet?) willing
229 | * to risk.
230 | *
231 | * @param name the class to check if it's whitelisted.
232 | * @return true if the class is whitelisted or if whitelisting is disabled. False if whitelisting is
233 | * enabled but the class is not whitelisted.
234 | */
235 | public boolean isWhitelisted(String name) {
236 | if( ! ( getWhitelistEnabled() || getStackWhitelistEnabled() ) ) {
237 | return true;
238 | }
239 |
240 | return isOnList(whitelist, name) || isOnStackIgnoreList( Thread.currentThread().getStackTrace() );
241 | }
242 |
243 | @SuppressWarnings("rawtypes")
244 | public boolean isWhitelisted(Class klass) {
245 | return isWhitelisted(klass.getName());
246 | }
247 |
248 |
249 | /**
250 | * Internal helper method for checking if an entry is on either a Vector or a Hashtable.
251 | * If it's in the Vector but not the Hashtable, then the Hashtable will be updated to include
252 | * the item, so that future lookups are faster.
253 | *
254 | * @param list
255 | * @param cache
256 | * @param name
257 | * @return true if the named class is listed either in the vector or the hashtable
258 | */
259 | private boolean isOnList(Vector list, String name) {
260 |
261 | if(list.size() == 0 ) return false;
262 |
263 | for(int i=0; i list, String line)
282 | {
283 | list.addElement(line);
284 | }
285 |
286 | /**
287 | * Adds the specified line to the list of classes that we should filter out of reporting
288 | * results if the deserialization event occurred with the specified class in the current
289 | * call stack. For example, to help ignore if serialization is happening through memcache.
290 | * @param line
291 | */
292 | public void addToStackIgnoreList(String line) {
293 | addToList(stackIgnoreList, line);
294 | }
295 |
296 | /**
297 | * Adds the specified class name to the list of classes that we should filter out of reporting
298 | * results if it is the class being deserialized.
299 | *
300 | * @param className
301 | */
302 | public void addToClassIgnoreList(String className){
303 | addToList(classIgnoreList, className);
304 | }
305 |
306 |
307 | /**
308 | * Add the specified class to the whitelist. If whitelisting is enabled, this class will be
309 | * allowed to deserialize, but non-whitelisted classes won't be allowed to deserialize.
310 | * @param line
311 | */
312 | public void addToWhitelist(String line) {
313 | addToList(whitelist, line);
314 | }
315 |
316 | @SuppressWarnings("rawtypes")
317 | public void addToWhitelist(Class klass){
318 | addToWhitelist(klass.getName());
319 | }
320 |
321 | /**
322 | * Add the specified class to the blacklist. If blacklisting is enabled, this class NOT will be
323 | * allowed to desereialize, but non-listed classes will be allowed to deserialize.
324 | *
325 | * @param line
326 | */
327 | public void addToBlacklist(String line) {
328 | addToList(blacklist, line);
329 | }
330 |
331 | @SuppressWarnings("rawtypes")
332 | public void addToBlacklist(Class klass) {
333 | addToBlacklist(klass.getName());
334 | }
335 |
336 | /**
337 | * Turns on or off whitelisting.
338 | *
339 | * @param isWhitelist
340 | */
341 | public void setWhitelisting(boolean enabled) {
342 | this.whitelistEnabled = enabled;
343 | }
344 |
345 | /**
346 | * Turns on or off blacklisting.
347 | *
348 | * @param isBlacklist
349 | */
350 | public void setBlacklisting(boolean enabled) {
351 | this.blacklistEnabled = enabled;
352 | }
353 |
354 | /**
355 | * Turn reporting on or off.
356 | *
357 | * @param enabled
358 | */
359 | public void setReporting(boolean enabled) {
360 | this.reportingEnabled = enabled;
361 | }
362 |
363 | @SuppressWarnings("rawtypes")
364 | public void addToClassIgnoreList(Class class1) {
365 | addToClassIgnoreList(class1.getName());
366 | }
367 |
368 | @SuppressWarnings("rawtypes")
369 | public boolean includeInReport(Class class1) {
370 | return this.includeInReport(class1.getName());
371 | }
372 |
373 | public String toString() {
374 | String string = "rO0Config:{";
375 | string += " BLACKLIST:(" + this.blacklistEnabled + this.blacklist + ")";
376 | string += " WHITELIST(" + this.whitelistEnabled + this.whitelist + ")";
377 | string += " REPORTING(" + this.reportingEnabled;
378 | string += "CLASSIGNORE(" + this.classIgnoreEnabled + this.classIgnoreList + ")";
379 | string += "STACKIGNORE(" + this.stackIgnoreEnabled + this.stackIgnoreList + ")";
380 | string += ")";
381 | string += "}";
382 |
383 | return string;
384 | }
385 |
386 | }
387 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/RO0Transformer.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import java.lang.instrument.ClassFileTransformer;
4 | import java.lang.instrument.IllegalClassFormatException;
5 | import java.security.ProtectionDomain;
6 |
7 | import org.objectweb.asm.ClassReader;
8 | import org.objectweb.asm.ClassVisitor;
9 | import org.objectweb.asm.ClassWriter;
10 |
11 | /**
12 | * Just transform a single class - java.io.ObjectInputStream. Let
13 | * the user know if there are any problems doing that via sysout.
14 | */
15 | public class RO0Transformer implements ClassFileTransformer {
16 |
17 | public byte[] transform(ClassLoader cl, String className, Class> parentClass, ProtectionDomain pd, byte[] originalBytecode) throws IllegalClassFormatException {
18 | byte[] transformedBytecode = null;
19 | if("java/io/ObjectInputStream".equals(className)) {
20 | transformedBytecode = weavePatch(originalBytecode);
21 | }
22 | return transformedBytecode;
23 | }
24 |
25 | private byte[] weavePatch(byte[] originalBytecode) {
26 | byte[] transformedBytecode = null;
27 | try {
28 | ClassReader reader = new ClassReader(originalBytecode);
29 | ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
30 | ClassVisitor visitor = new ObjectInputStreamVisitor(writer);
31 | reader.accept(visitor, ClassReader.EXPAND_FRAMES);
32 | transformedBytecode = writer.toByteArray();
33 | RO0Agent.out("Protection against deserialization attacks added to java.io.ObjectInputStream");
34 | } catch (Throwable t) {
35 | RO0Agent.out("Problem instrumenting java.io.ObjectInputStream -- no deserialization protection in place");
36 | t.printStackTrace();
37 | }
38 | return transformedBytecode;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/ResolveClassController.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import java.io.ObjectStreamClass;
4 |
5 | public class ResolveClassController {
6 |
7 | /**
8 | * This is invoked at the beginning of java.io.ObjectInputStream#resolveClass().
9 | * @param streamClass the parameter passed to resolveClass()
10 | */
11 | public static void onResolveClass(ObjectStreamClass streamClass) {
12 | String name = streamClass.getName();
13 |
14 | if( RO0Agent.config.isBlacklisted(name) ) {
15 | String message = "Likely exploit gadget encoutered during deserialization: " + name;
16 | RO0Agent.out(message);
17 | throw new SecurityException(message);
18 | }
19 |
20 | if( ! RO0Agent.config.isWhitelisted(name)) {
21 | String message = "Non-whitelisted class found during deserialization: " + name;
22 | RO0Agent.out(message);
23 | throw new SecurityException(message);
24 | }
25 |
26 | // LAST thing to do is report. If something else failed, an exception would have been
27 | // thrown, and an error reported; we'd never get here; or if something was found on
28 | // an ignore list, we'd never get here. Note that there are two ways to ignore stuff,
29 | // so that we don't get really noisy logging. First is ignoring classes to be deserialized.
30 | // second is ignoring deserialization attempts with entries on the stack.
31 | if( ! RO0Agent.config.includeInReport(name)) {
32 | return;
33 | }
34 |
35 | RO0Agent.out("Deserializing " + name + ": " + Thread.currentThread().getStackTrace().toString());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/ResolveClassMethodVisitor.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import org.objectweb.asm.MethodVisitor;
4 | import org.objectweb.asm.Opcodes;
5 | import org.objectweb.asm.Type;
6 | import org.objectweb.asm.commons.AdviceAdapter;
7 | import org.objectweb.asm.commons.Method;
8 |
9 | public class ResolveClassMethodVisitor extends AdviceAdapter {
10 |
11 | protected ResolveClassMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
12 | super(Opcodes.ASM5, mv, access, name, desc);
13 | }
14 |
15 | /**
16 | * Fire off our sensor at the beginning of ObjectInputStream#resolveClass(java.io.ObjectStreamClass).
17 | */
18 | @Override
19 | protected void onMethodEnter() {
20 | Type type = Type.getType(ResolveClassController.class);
21 | Method method = new Method("onResolveClass", "(Ljava/io/ObjectStreamClass;)V");
22 | loadArg(0);
23 | invokeStatic(type,method);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/BlacklistElement.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.Serializable;
4 |
5 | public class BlacklistElement implements Serializable {
6 |
7 | /**
8 | *
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/IgnoreClassListElement.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.Serializable;
4 |
5 | public class IgnoreClassListElement implements Serializable {
6 |
7 | /**
8 | *
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/Test.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import com.contrastsecurity.rO0.RO0Agent;
4 |
5 | public class Test {
6 | public void test(String test, boolean result, boolean expected){
7 | RO0Agent.out((result==expected?"PASS: ":"FAIL: ") + test);
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/TestMain.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | public class TestMain {
4 |
5 | public static void main(String[] args) {
6 | TestRO0Config test1 = new TestRO0Config();
7 | TestResolveClassController test2 = new TestResolveClassController();
8 |
9 | test1.run();
10 | test2.run();
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/TestRO0Config.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.FileNotFoundException;
4 |
5 | import com.contrastsecurity.rO0.RO0Config;
6 | import com.contrastsecurity.rO0.RO0Agent;
7 | import com.contrastsecurity.rO0.TestCases.BlacklistElement;
8 | import com.contrastsecurity.rO0.TestCases.IgnoreClassListElement;
9 | import com.contrastsecurity.rO0.TestCases.UnlistedElement;
10 | import com.contrastsecurity.rO0.TestCases.WhitelistElement;
11 |
12 | public class TestRO0Config
13 | extends Test
14 | {
15 |
16 | public void run() {
17 | RO0Agent.out("----- Beginning RO0Config tests -----");
18 | RO0Config config = new RO0Config();
19 |
20 | RO0Agent.out("----- Testing All-Disabled Config -----");
21 | config.setBlacklisting(false);;
22 | config.setReporting(false);
23 | config.setWhitelisting(false);
24 | config.setBlacklisting(true);
25 | test("check disabled blacklist disallows nothing",
26 | config.isBlacklisted(Object.class),
27 | false);
28 | test("check disabled whitelist allows everything",
29 | config.isWhitelisted(Object.class),
30 | true);
31 | test("check disabled report filters filter everything",
32 | config.includeInReport(Object.class),
33 | false);
34 |
35 |
36 | RO0Agent.out("----- Testing Empty Config, each enabled in turn -----");
37 | config.setBlacklisting(true);
38 | test("check empty, enabled blacklist disallows nothing",
39 | config.isBlacklisted(Object.class),
40 | false);
41 | config.setWhitelisting(true);
42 | test("check empty, enabled whitelist allows nothing",
43 | config.isWhitelisted(Object.class),
44 | false);
45 | config.setReporting(true);
46 | test("check empty, enalbed report still filters filter nothing",
47 | config.includeInReport(Object.class),
48 | true);
49 |
50 |
51 | RO0Agent.out("----- Test 1 blacklisted element -----");
52 | config.setBlacklisting(true);
53 | config.setWhitelisting(false);;
54 | config.addToBlacklist(Object.class);
55 | test("check blacklisted element is found on blacklist",
56 | config.isBlacklisted(Object.class),
57 | true);
58 | test("check NON-blacklisted element is not found on blacklist",
59 | config.isBlacklisted(Integer.class),
60 | false);
61 | test("check whitelist pretends everything is whitelisted when blacklist is enabled",
62 | config.isWhitelisted(Object.class),
63 | true);
64 | test("check reporting is unaffected by enabling blacklisting - still nothing is filtered",
65 | config.includeInReport(Object.class),
66 | true);
67 |
68 | RO0Agent.out("----- Test 1 whitelisted element -----");
69 | config.setWhitelisting(true);
70 | config.setBlacklisting(false);
71 | config.addToWhitelist(Object.class);
72 | test("check blacklisting is disabled (pretends blacklisted element isn't blacklisted)",
73 | config.isBlacklisted(Object.class),
74 | false);
75 | test("check NON-blacklisted element is still not on the blacklist",
76 | config.isBlacklisted(Integer.class),
77 | false);
78 | test("check whitelisted element is on the whitelist",
79 | config.isWhitelisted(Object.class),
80 | true);
81 | test("check non-whitelisted element is not on the white list",
82 | config.isWhitelisted(Integer.class),
83 | false);
84 | test("check reporting hasn't changed",
85 | config.includeInReport(Object.class),
86 | true);
87 |
88 | RO0Agent.out("----- Test that reporting class filters correctly work");
89 | config.addToClassIgnoreList(Object.class);
90 | config.setReporting(true);
91 | config.setWhitelisting(false);
92 | test("check blacklisting continues to pretend the list is empty when reporting is enabled",
93 | config.isBlacklisted(Object.class),
94 | false);
95 | test("check NON-blacklisted element still isn't found on the blacklist, either",
96 | config.isBlacklisted(Integer.class),
97 | false);
98 | test("check whitelisted still allows the previously whitelisted object...",
99 | config.isWhitelisted(Object.class),
100 | true);
101 | test("and that whitelisting is disabled and therefore pretends everything is whitelisted when reporting enabled",
102 | config.isWhitelisted(Integer.class),
103 | true);
104 | test("check reporting wants to include a non-filtered class in the report",
105 | config.includeInReport(Integer.class),
106 | true);
107 | test("check reporting wants filtered classes in the report when class filtering is disabled",
108 | config.includeInReport(Object.class),
109 | true);
110 | test("check reporting wants unfiltered classes in the report when class filtering is enabled",
111 | config.includeInReport(Integer.class),
112 | true);
113 | config.setClassFiltering(true);
114 | test("check reporting doesn't want filtered classes in the report when class filtering is enabled",
115 | config.includeInReport(Object.class),
116 | false);
117 | test("check reporting still wants unfiltered classes in the report when class filtering is enabled",
118 | config.includeInReport(Integer.class),
119 | true);
120 | config.addToStackIgnoreList(getClass().getName());
121 | test("check reporting wants configured-but-disaled stack-filtered stuff in the report when stack-filtering is disabled",
122 | config.includeInReport(Integer.class),
123 | true);
124 | test("check that the configred-but-disabled stack ignore list has no effect when stack ignore is disabled - unfiltered class is still reported",
125 | config.includeInReport(Integer.class),
126 | true);
127 | config.setClassFiltering(false);;
128 | test("check that cofigred-but-disabled stack filtering still has no effect if class filtering is disabled. filtered class is reported",
129 | config.includeInReport(Object.class),
130 | true);
131 | test("Check that configured-but-disabled stack filtering also allows non-filtered class);",
132 | config.includeInReport(Integer.class),
133 | true);
134 | config.setStackFiltering(true);
135 | test("Check that enabled stack filtering is allowed when on the stack",
136 | config.includeInReport(Integer.class),
137 | false);
138 |
139 | RO0Agent.out("----- Test proper loading of config file -----");
140 | config = new RO0Config();
141 | try {
142 | config.readConfig("../config/unit_test_config_1");
143 | config.readConfig("../config/unit_test_config_2");
144 | } catch (FileNotFoundException e) {
145 | RO0Agent.out("FAIL: Unable to load config file 1");
146 | RO0Agent.out(e);
147 | }
148 | config.setBlacklisting(true);
149 | config.setWhitelisting(true);
150 | config.setReporting(true);
151 | config.setClassFiltering(true);;
152 | test("Test class ignore list includes class ignore element",
153 | config.includeInReport(IgnoreClassListElement.class),
154 | false);
155 | test("Test class ignore list does not include unlisted element",
156 | config.includeInReport(UnlistedElement.class),
157 | true);
158 | test("Test class ignore list does not include whitelist element",
159 | config.includeInReport(WhitelistElement.class),
160 | true);
161 | test("Test class ignore list does not include blacklist element",
162 | config.includeInReport(BlacklistElement.class),
163 | true);
164 |
165 | // testing stack ignore requires a separate config file, to put this class
166 | // in the ignore stack, so this piece had to be set up after the above
167 | // class-ignore tests
168 | try {
169 | config.readConfig("../config/unit_test_config_3");
170 | } catch (FileNotFoundException e) {
171 | RO0Agent.out("FAIL: Unable to load config file 1");
172 | RO0Agent.out(e);
173 | }
174 | test("Test stack ignore list affects class ignore element",
175 | config.includeInReport(IgnoreClassListElement.class),
176 | false);
177 | test("Test reporting includes unlisted element",
178 | config.includeInReport(UnlistedElement.class),
179 | true);
180 | test("Test reporting includes whitelisted element",
181 | config.includeInReport(WhitelistElement.class),
182 | true);
183 | test("Test reporting includes blacklisted element",
184 | config.includeInReport(BlacklistElement.class),
185 | true);
186 |
187 |
188 | test("test blacklist includes blacklisted element",
189 | config.isBlacklisted(BlacklistElement.class),
190 | true);
191 | test("Test blacklist does not include unlisted element",
192 | config.isBlacklisted(UnlistedElement.class),
193 | false);
194 | test("Test blacklist does not include whitelisted element",
195 | config.isBlacklisted(WhitelistElement.class),
196 | false);
197 | test("Test blacklist does not include report ignore element",
198 | config.isBlacklisted(IgnoreClassListElement.class),
199 | false);
200 | test("Test blacklist does not include stack ignore element",
201 | config.isBlacklisted(TestResolveClassController.class),
202 | false);
203 |
204 | test("Test whitelist includes whitelisted element",
205 | config.isWhitelisted(WhitelistElement.class),
206 | true);
207 | test("test whitelist does not include unlisted element",
208 | config.isWhitelisted(UnlistedElement.class),
209 | false);
210 | test("test whitelist does not include blacklisted element",
211 | config.isWhitelisted(BlacklistElement.class),
212 | false);
213 | test("Test whitelist does not include class-ignore element",
214 | config.isWhitelisted(IgnoreClassListElement.class),
215 | false);
216 | test("Test whitelist does not include stack ignore element",
217 | config.isWhitelisted(TestResolveClassController.class),
218 | false);
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/TestResolveClassController.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.IOException;
7 | import java.io.ObjectInputStream;
8 | import java.io.ObjectOutputStream;
9 | import java.io.Serializable;
10 | import java.util.Arrays;
11 |
12 | import com.contrastsecurity.rO0.RO0Agent;
13 | import com.contrastsecurity.rO0.TestCases.BlacklistElement;
14 | import com.contrastsecurity.rO0.TestCases.UnlistedElement;
15 | import com.contrastsecurity.rO0.TestCases.WhitelistElement;
16 |
17 | public class TestResolveClassController
18 | extends Test
19 | {
20 |
21 | public void run() {
22 | RO0Agent.out("--------------------------------------------------");
23 | RO0Agent.out("----- Beginning ResolveClassController tests -----");
24 |
25 | RO0Agent.out("----- trying to load java agent -----");
26 |
27 | BlacklistElement blacklistElement = new BlacklistElement();
28 | WhitelistElement whitelistElement = new WhitelistElement();
29 | UnlistedElement unlistedElement = new UnlistedElement();
30 |
31 | test("verifying flag to load rO0 has been set",
32 | RO0Agent.loaded,
33 | true);
34 |
35 | test("verifying whitelisting is disabled",
36 | RO0Agent.config.getWhitelistEnabled(),
37 | false);
38 | test("verifying blacklisting is disabled",
39 | RO0Agent.config.getBlacklistEnabled(),
40 | false);
41 | test("verifying reporting is disabled by default",
42 | RO0Agent.config.getReportingEnabled(),
43 | false);
44 | RO0Agent.config.setReporting(false);
45 | test("verifying reporting is now disabled",
46 | RO0Agent.config.getReportingEnabled(),
47 | false);
48 | test("verifying class ignore is disabled",
49 | RO0Agent.config.getClassIgnoreEnabled(),
50 | false);
51 | test("verifying stack ignore is disabled",
52 | RO0Agent.config.getStackIgnoreEnabled(),
53 | false);
54 |
55 | RO0Agent.out("----- If no lists are loaded. Everything should deserailize. -----");
56 |
57 | test("verifying whitelisted object serializes",
58 | tryToSerialize(whitelistElement),
59 | true);
60 |
61 | test("verifying blacklisted object serializes",
62 | tryToSerialize(blacklistElement),
63 | true);
64 | test("verifying unlisted object serializes",
65 | tryToSerialize(unlistedElement),
66 | true);
67 |
68 |
69 | RO0Agent.out("----- Load all lists, but don't enable any of them -----");
70 | try {
71 | RO0Agent.config.readConfig("../config/unit_test_config_1");
72 | RO0Agent.config.readConfig("../config/unit_test_config_2");
73 | RO0Agent.config.readConfig("../config/unit_test_config_3");
74 | } catch (FileNotFoundException e) {
75 | RO0Agent.out("FAIL: unable to load config files");
76 | e.printStackTrace();
77 | }
78 | test("verifying whitelisted object serializes",
79 | tryToSerialize(whitelistElement),
80 | true);
81 | test("verifying blacklisted object serializes",
82 | tryToSerialize(blacklistElement),
83 | true);
84 | test("verifying unlisted object serializes",
85 | tryToSerialize(unlistedElement),
86 | true);
87 |
88 | RO0Agent.out("----- Enable whitelisting -----");
89 | RO0Agent.config.setWhitelisting(true);
90 | test("verifying whitelisted object serializes",
91 | tryToSerialize(whitelistElement),
92 | true);
93 | test("verifying blacklisted object doesn't serialize",
94 | tryToSerialize(blacklistElement),
95 | false);
96 | test("verifying unlisted object doesn't serialize",
97 | tryToSerialize(unlistedElement),
98 | false);
99 |
100 | RO0Agent.out("----- Disable whitelisting and enable blacklisting -----");
101 | RO0Agent.config.setWhitelisting(false);
102 | RO0Agent.config.setBlacklisting(true);
103 | test("verifying whitelisted object serializes",
104 | tryToSerialize(whitelistElement),
105 | true);
106 | test("verifying blacklisted object doesn't serialize",
107 | tryToSerialize(blacklistElement),
108 | false);
109 | test("verifying unlisted object serializes",
110 | tryToSerialize(unlistedElement),
111 | true);
112 |
113 | // Reporting can't easily be tested without replacing RO0Agent.out... that doesn't seem
114 | // necessary but unit tests could be added if deemed required. There is related coverage
115 | // in TestRO0Config, to at least ensure that configuration for reporting
116 | // is correctly implemented.
117 | }
118 |
119 | private boolean tryToSerialize(Serializable object)
120 | {
121 | try {
122 | byte[] bytes = serialize(object);
123 | Serializable object2 = deserialize(bytes);
124 |
125 | // validate match
126 | byte[] bytes2 = serialize(object2);
127 | if(Arrays.equals(bytes, bytes2)) return true;
128 | } catch ( SecurityException se ) {
129 | return false;
130 | } catch ( IOException ioe ) {
131 | throw new RuntimeException(ioe);
132 | } catch (ClassNotFoundException cnfe) {
133 | throw new RuntimeException(cnfe);
134 | }
135 | return false;
136 | }
137 |
138 | private byte[] serialize(Serializable object) throws IOException
139 | {
140 | ByteArrayOutputStream bytes = new ByteArrayOutputStream();
141 | ObjectOutputStream out = new ObjectOutputStream(bytes);
142 | out.writeObject(object);
143 | return bytes.toByteArray();
144 | }
145 |
146 | private Serializable deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
147 | ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
148 | return (Serializable)in.readObject();
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/UnlistedElement.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.Serializable;
4 |
5 | public class UnlistedElement implements Serializable {
6 |
7 | /**
8 | *
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/contrastsecurity/rO0/TestCases/WhitelistElement.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0.TestCases;
2 |
3 | import java.io.Serializable;
4 |
5 | public class WhitelistElement implements Serializable {
6 |
7 | /**
8 | *
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/scripts/stats.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | use strict;
3 |
4 | if ( $#ARGV < 0 ) { die "you need to specify an input file.\n"; }
5 |
6 | open FILE, "<$ARGV[0]" or die "can't open $!\n";;
7 |
8 |
9 | ####################
10 | # hashes to store our histograms
11 | # and other related variables
12 | my($num_deserializations);
13 | my(%deserialized_classes);
14 | my(%stack_locations);
15 | my($num_memcache, $memcache_check_flag);
16 |
17 |
18 | ####################
19 | # function: handle_class
20 | # adds the class to the class statistics hash(es)
21 | sub handle_class {
22 | my($klass) = (split ' ', shift)[-1];
23 | $deserialized_classes{$klass}++;
24 | $num_deserializations++;
25 | $memcache_check_flag = 1; # OK to check for memcache in stack
26 | # flag helps avoid duplicates
27 | }
28 |
29 |
30 |
31 | ###################
32 | # function: handle_stack
33 | # adds the stack to the various stack statistcs hashes
34 | sub handle_stack {
35 | my($stack_location) = (split ' ', shift)[-1];
36 | $stack_locations{$stack_location}++;
37 |
38 | if( $memcache_check_flag ) {
39 | if( $stack_location =~ /memcache/i ) {
40 | $num_memcache++;
41 | $memcache_check_flag = 0; # wait for next stack to check for memcache
42 | # to avoid double counting
43 | }
44 | }
45 | }
46 |
47 |
48 |
49 |
50 | ####################
51 | # main
52 | #
53 |
54 | # chew up all lines prior to the first output we care about
55 | my($line);
56 | UNINTERESTING_LINE: while($line = ) {
57 | chomp $line;
58 | if( $line =~ /contrast/ ) {
59 | last UNINTERESTING_LINE;
60 | }
61 | }
62 |
63 | # now start processing our stuff
64 | LINE: while($line = ) {
65 | # skip lines that are clearly not our stuff...
66 | if( not $line =~ /\./ ) { next LINE; }
67 | if( $line =~ /\// ) { next LINE; }
68 | if( $line =~ /NRMUtil/ ) { next LINE; }
69 |
70 | if( $line =~ /contrast-rO0/ ) {
71 | handle_class($line);
72 | next LINE;
73 | }
74 | handle_stack($line);
75 |
76 | }
77 |
78 |
79 | ############
80 | # print histograms
81 | my($klass,$count);
82 | my(@klasses) = keys %deserialized_classes;
83 | my(%packages, @packages, $package);
84 | foreach $klass (@klasses) {
85 | # count the number of unique classes deserialized
86 | $count += $deserialized_classes{$klass};
87 |
88 | # get and count the unique packages deserialized
89 | @packages = split /\./, $klass;
90 | $package = @packages[0...$#packages-1];
91 | $packages{$package}++;
92 | }
93 | my(@stack_locations) = keys %stack_locations;
94 |
95 | printf "recorded $count deserializations out of $num_deserializations\n";
96 | printf "number of unique classes deserialized: $#klasses\n";
97 | printf "number of unique packages from which deserialized classes were found: $#packages\n";
98 | printf "number of unique stack locations involved (includes multiple entries for each deserialize event): $#stack_locations \n";
99 | printf "number of traces that have memcache in the stack: $num_memcache\n";
100 |
101 |
102 |
103 |
104 | #my($max)=50;
105 | #printf "top $max highest stack locations (occuring least often in the histograms): \n";
106 | #
107 | #my($i)=0;
108 | #my($loc);
109 | #foreach $loc ( sort {$stack_locations{$a} <=> $stack_locations{$b}} keys %stack_locations) {
110 | # printf "$stack_locations{$loc} $loc\n";
111 | # if( $i++ > $max ) { last; }
112 | #}
113 |
114 |
--------------------------------------------------------------------------------
/src/test/java/com/contrastsecurity/rO0/DeserializationTest.java:
--------------------------------------------------------------------------------
1 | package com.contrastsecurity.rO0;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.ObjectInputStream;
8 | import java.io.ObjectOutputStream;
9 | import java.io.Serializable;
10 | import java.util.BitSet;
11 |
12 | import org.apache.commons.collections.functors.InvokerTransformer;
13 |
14 | import junit.framework.AssertionFailedError;
15 | import junit.framework.TestCase;
16 |
17 | public class DeserializationTest extends TestCase {
18 |
19 |
20 | public void testDeserialization_Safe() throws Exception {
21 | BitSet bitset = new BitSet();
22 | bitset.set(1,2);
23 | File serializedFile = serialize(bitset);
24 | BitSet bitset2 = (BitSet) deserialize(serializedFile);
25 | assertEquals(bitset,bitset2);
26 | }
27 |
28 | public void testDeserialization_Unsafe() throws Exception {
29 | InvokerTransformer transformer = new InvokerTransformer("foo", new Class[]{}, new Object[]{});
30 | File serializedFile = serialize(transformer);
31 | try {
32 | // try deserialized the file we just wrote -- should break!
33 | transformer = (InvokerTransformer) deserialize(serializedFile);
34 | fail("should have failed to deserialize!");
35 | } catch(AssertionFailedError e) {
36 | throw e;
37 | } catch (SecurityException e) {
38 | // expected
39 | } catch (Throwable t) {
40 | fail("Shouldn't have failed for non-security reasons");
41 | }
42 | }
43 |
44 | private File serialize(Serializable serializable) throws IOException {
45 | File tmpFile = File.createTempFile("contrast-test", ".ser");
46 | FileOutputStream fos = new FileOutputStream(tmpFile);
47 | ObjectOutputStream oos = new ObjectOutputStream(fos);
48 | oos.writeObject(serializable);
49 | oos.close();
50 | fos.close();
51 | return tmpFile;
52 | }
53 |
54 | private Object deserialize(File serializable) throws Exception {
55 | FileInputStream fis = new FileInputStream(serializable);
56 | ObjectInputStream ois = new ObjectInputStream(fis);
57 | Object object = ois.readObject();
58 | ois.close();
59 | fis.close();
60 | return object;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------