From 7d3d65ec97bfeb6071af99ffda3d95797ae91b9c Mon Sep 17 00:00:00 2001 From: nickl- Date: Fri, 27 Oct 2017 08:33:55 +0200 Subject: [PATCH] Some functional unit tests proofs. Proves that you cannot access restricted functionality then goes ahead and mock test all 3 exposures JAVA_9, JAVA_7+ and OTHERS by changing the private static final class referenced default chosen on initialise. --- .../proxy/TestSecuredPrivileged.java | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 src/test/test/javassist/proxy/TestSecuredPrivileged.java diff --git a/src/test/test/javassist/proxy/TestSecuredPrivileged.java b/src/test/test/javassist/proxy/TestSecuredPrivileged.java new file mode 100644 index 00000000..c51555f5 --- /dev/null +++ b/src/test/test/javassist/proxy/TestSecuredPrivileged.java @@ -0,0 +1,281 @@ +package test.javassist.proxy; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.util.proxy.DefineClassHelper; + +public class TestSecuredPrivileged { + + public TestSecuredPrivileged() { + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + /** + * Test proves that you cannot even access members with + * private static and final modifiers. */ + @Test + public void testDefinedHelperPrivilegedFieldVisibility() { + try { + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + assertTrue(Modifier.isStatic(privi.getModifiers())); + thrown.expectCause(instanceOf(IllegalAccessException.class)); + thrown.expectMessage(both(stringContainsInOrder(Arrays.asList("cannot access a member"))) + .and(stringContainsInOrder(Arrays.asList("with modifiers \"private static final".split("", 1))))); + + privi.get(null); + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + /** + * Test proves that the default enum constant is a class and specifically + * auto selected for Java 9. */ + @Test + public void testDefinedHelperPrivilegedField() { + try { + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + assertTrue(Modifier.isStatic(privi.getModifiers())); + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + assertThat(inst, instanceOf(DefineClassHelper.class)); + privi.setAccessible(true); + Object p = privi.get(inst); + assertThat(""+p, equalTo("JAVA_9")); + assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$1")); + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + /** + * Test proves that caller class security is enforced and works + * as expected. */ + @Test + public void testDefinedHelperPrivilegedFieldMethodAccessDenied() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object priviInst = privi.get(inst); + Method defineClass = priviInst.getClass().getDeclaredMethod( + "defineClass", new Class[] { + String.class, byte[].class, int.class, int.class, + ClassLoader.class, ProtectionDomain.class + }); + + assertThat(defineClass, notNullValue()); + defineClass.setAccessible(true); + assertThat(defineClass.getName(), equalTo("defineClass")); + assertTrue(defineClass.canAccess(priviInst)); + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.C"); + byte[] bc = c.toBytecode(); + + thrown.expectCause(instanceOf(IllegalAccessError.class)); + thrown.expectMessage(equalTo("java.lang.IllegalAccessError: Access denied for caller.")); + + @SuppressWarnings("unused") + Object res = defineClass.invoke(priviInst, new Object[] { + c.getName(), bc, 0, bc.length, new ClassLoader() {}, + ClassLoader.class.getProtectionDomain() + }); + } catch(InvocationTargetException t) { + throw new RuntimeException(t.getTargetException()); + } catch(Throwable t) { throw new RuntimeException(t); } + } + /** + * Test proves that we do have 3 enum constants in the private static + * inner class. */ + @Test + public void testDefinedHelperEnumClass() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + assertThat(DefineClassHelper.class.getDeclaredClasses(), arrayWithSize(1)); + Class secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + assertTrue(secPriv.isEnum()); + assertThat(secPriv.getEnumConstants(), arrayWithSize(3)); + assertThat(""+secPriv.getEnumConstants()[0], equalTo("JAVA_9")); + assertThat(""+secPriv.getEnumConstants()[1], equalTo("JAVA_7")); + assertThat(""+secPriv.getEnumConstants()[2], equalTo("JAVA_OTHER")); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that you cannot modify private static final reference even + * with setAccessible(true). */ + @Test + public void testDefinedHelperCannotSetPrivileged() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + thrown.expectCause(instanceOf(IllegalAccessException.class)); + thrown.expectMessage(startsWith("java.lang.IllegalAccessException: Can not set static final")); + privi.set(inst, J7); + + } catch (Throwable t) {throw new RuntimeException(t);} + + } + /** + * Test proves that you can achieve the impossible and modify private + * static final class reference without an instance. Now we can Mock + * test JDK 6 to 8 functionality */ + @Test + public void testDefinedHelperSetPrivilegedToJava7() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); + + Object p = privi.get(inst); + assertThat(""+p, equalTo("JAVA_7")); + assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$2")); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that Java 7+ MethodHandle defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperJava7ToClass() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); + + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.J7"); + byte[] bc = c.toBytecode(); + Class bcCls = DefineClassHelper.toClass("a.b.J7", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.J7")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that Java 6 reflection method defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperJavaOtherToClass() { + try { + Constructor con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object JO = secPriv.getEnumConstants()[2]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, JO}}); + + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.JO"); + byte[] bc = c.toBytecode(); + Class bcCls = DefineClassHelper.toClass("a.b.JO", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.JO")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that default Java 9 defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperDefaultToClass() { + try { + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.D"); + byte[] bc = c.toBytecode(); + Class bcCls = DefineClassHelper.toClass("a.b.D", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.D")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + } catch (Throwable t) {t.printStackTrace();} + + } +} -- 2.39.5