Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What security privileges does Jython need, to successfully run untrusted Python code?

In brief

Jython is running in a JVM with a security manager. The security manager should not prevent the Jython engine itself from doing its job, but it should not allow any privileged action by the python script running within Jython. What is the minimum Java security policy that will accomplish this?

In other words (the long version)

This question is about using Java's security mechanisms to sandbox untrusted python scripts. (I don't want a discussion about other ways to sandbox python.) If there's something fundamentally wrong with this approach to security, please say so.

The untrusted python code will invoke methods on trusted Java objects, and will be embedded in Java as a PyObject. This is explained in Jythonbook's example, and we'll use the code from that example. This is Jython 2.5.2 on Java 1.6.

We need to give some very specific instructions to the security manager, because Jython does some privileged actions of its own, but it also executes untrusted python code that should not have any of those privileges.

First, for security, we install a SecurityManager in the Java VM:

/home/me% export CLASSPATH=.:jython.jar
/home/me% javac ./org/jython/book/interfaces/BuildingType.java ./org/jython/book/Main.java ./org/jython/book/util/BuildingFactory.java
/home/me% java org.jython.book.Main
Building Info: null BUILDING-A 100 WEST MAIN
Building Info: null BUILDING-B 110 WEST MAIN
Building Info: null BUILDING-C 120 WEST MAIN

Great. But for us, Building.py is untrusted code so we lock down the JVM. Problem is, that cripples Jython:

/home/me% java -Djava.security.manager org.jython.book.Main
Jul 23, 2013 7:07:17 PM org.python.google.common.base.internal.Finalizer getInheritableThreadLocalsField
INFO: Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will inherit thread local values.
Exception in thread "main" java.security.AccessControlException: access denied (java.util.PropertyPermission user.dir read)
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
        at java.security.AccessController.checkPermission(AccessController.java:546)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
        ...
        at java.io.File.getAbsolutePath(File.java:501)
        at org.python.core.PySystemState.<init>(PySystemState.java:181)
        at org.python.core.PySystemState.doInitialize(PySystemState.java:890)
        at org.python.core.PySystemState.initialize(PySystemState.java:800)
        at org.python.core.PySystemState.initialize(PySystemState.java:750)
        at org.python.core.PySystemState.initialize(PySystemState.java:743)
        at org.python.core.PySystemState.initialize(PySystemState.java:737)
        at org.python.core.PySystemState.initialize(PySystemState.java:733)
        at org.python.core.ThreadStateMapping.getThreadState(ThreadStateMapping.java:17)
        at org.python.core.Py.getThreadState(Py.java:1315)
        at org.python.core.Py.getThreadState(Py.java:1311)
        at org.python.core.Py.getSystemState(Py.java:1331)
        at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:102)
        at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:92)
        at org.python.util.PythonInterpreter.<init>(PythonInterpreter.java:64)
        at org.jython.book.util.BuildingFactory.<init>(BuildingFactory.java:22)
        at org.jython.book.Main.main(Main.java:21){code}

Security Manager is not letting anyone read the "user.dir" property. Of course, it should be okay for Jython itself to read that property. Can I write that into a policy file like this?

/home/me% cat jython.policy
grant codeBase "/home/me/*" {
  permission java.util.PropertyPermission "user.dir", "read";
};
/home/me% java -Djava.security.manager -Djava.security.policy=jython.policy org.jython.book.Main

Well, no, I can't ... because the untrusted python code also runs within the python interpreter classes. So untrusted python would also get this privilege, and that's bad.

So I need to authorize only a specific jython class, in this case PySystemState. (I may also need to edit its source code so that it runs certain code within a doPrivilegedAction call.)

Great. The next required privilege is a doozy: the dangerous createClassLoader permission.

java.security.AccessControlException: access denied (java.lang.RuntimePermission createClassLoader)
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
        at java.security.AccessController.checkPermission(AccessController.java:546)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
        at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.java:594)
        at java.lang.ClassLoader.<init>(ClassLoader.java:226)
        at java.security.SecureClassLoader.<init>(SecureClassLoader.java:76)
        at java.net.URLClassLoader.<init>(URLClassLoader.java:113)
        at org.python.core.BytecodeLoader$Loader.<init>(BytecodeLoader.java:81)
        at org.python.core.BytecodeLoader.makeClass(BytecodeLoader.java:27)
        at org.python.core.BytecodeLoader.makeCode(BytecodeLoader.java:67)
        at org.python.compiler.LegacyCompiler$LazyLegacyBundle.loadCode(LegacyCompiler.java:43)
        at org.python.core.CompilerFacade.compile(CompilerFacade.java:34)
        at org.python.core.Py.compile_flags(Py.java:1703)
        at org.python.core.Py.compile_flags(Py.java:1708)
        at org.python.core.Py.compile_flags(Py.java:1738)
        at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:206)
        at org.jython.book.util.BuildingFactory.<init>(BuildingFactory.java:23)
        at org.jython.book.Main.main(Main.java:22)

Nestled towards the bottom of the stack we see that PythonInterpreter.exec has triggered this access request. Does exec() have to run as a PrivilegedAction?

So this discovery process could go on for a while, and I was hoping that someone would simply know the answer: What classes in Jython need to be authorized, and what specific permissions do they need?

And, if this approach is destined to fail, then an answer to that effect would be great.

like image 406
Travis Wilson Avatar asked Jul 25 '13 01:07

Travis Wilson


People also ask

Can Jython run Python?

The Jython project provides implementations of Python in Java, providing to Python the benefits of running on the JVM and access to classes written in Java. The current release (a Jython 2.7. x) only supports Python 2 (sorry).

What is a Jython in Python?

Jython is a version of the Python programming language that runs on the Java platform. It allows users to write programs in Python and compile them to Java bytecodes that run directly on a Java Virtual Machine, or JVM. It's similar to otherJVM languages like; Scala, Kotlin, Groovy, or Clojur.

Is Jython a framework?

Jython is an open source implementation of the Python programming language, integrated with the Java platform. A programmer can compile Python source code to Java bytecode , and run the code on any Java virtual machine . The integration of Python and Java gives programmers access to all Java libraries.


1 Answers

For Jython 2.5.3 (the version I've been using), the minimum permissions are:

permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "getProtectionDomain";
permission java.io.FilePermission "${user.dir}/*", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "user.dir", "read";
permission java.util.PropertyPermission "line.separator", "read";

If you want scripts to be able to subclass Java API classes, you'll also need:

permission java.lang.RuntimePermission "accessDeclaredMembers";

As for the createClassLoader permission, that could be a problem, but I've discovered that you can grant this permission to the Jython interpreter without granting it to Python code running in the interpreter by moving the following classes from the Jython jar into a separate jar.

org.python.core.PyReflectedConstructor
org.python.core.PyReflectedField
org.python.core.PyReflectedFunction

This separate jar is placed on the classpath alongside the standard Jython jar, but isn't granted any permissions in the Java security policy file, and so these classes, which are always on the call stack when scripts try to call Java APIs, are prevented from taking advantage of the createClassLoader permission.

It's also worth noting that it's not easy to exploit the createClassLoader permission from Python code. I was able to do from within a Python script, but I had to extend java.secure.SecureClassLoader, which is only possible if the accessDeclaredMembers permission is also granted. Depending on your appetite for risk, it might not be worth messing with the Jython jars just to block createClassLoader, especially if you're not granting accessDeclaredMembers.

It's also well worth pointing out that the createClassLoader fix is very susceptible to changes within Jython, and I've only tested it with Jython 2.5.3.

I've written this up in more detail in a blog post.

like image 113
alphaloop Avatar answered Nov 16 '22 09:11

alphaloop