Kafka Connect 4.3.1 - High - Windows FileStream classpath leftover - unauth file read/write
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · kafka-connect-filestream-win
Apache Kafka Connect 4.3.1 (26b251a) - Apache Software Foundation
KAFKA-13748 took FileStream off the Unix Connect CLASSPATH so unauth REST could not open local files. The Windows launcher never got that exclude. The tarball still ships connect-file into libs/. kafka-run-class.bat concatenates every jar in there.
Unauth POST /connectors on a Windows-launched worker reads and appends files the Connect UID can touch.
| ID | no CVE yet |
| CWE | CWE-552, CWE-1188 |
| CVSS | High: 9.1 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Product | Apache Kafka Connect |
| Affected | 4.3.1 (26b251a); Windows bin/windows/kafka-run-class.bat. Unix .sh already excludes connect-file. |
| Auth | unauthenticated Connect REST (default listeners=http://:8083, no rest.extension.classes) |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Point a browser or curl at Connect REST on a worker started with the shipped Windows .bat files. Create a FileStream source. The worker opens whatever path you put in file and produces the bytes onto a Kafka topic. Create a FileStream sink. The worker creates or appends whatever path you put in file. There is no allowlist.
The Unix launcher already drops connect-file.*.jar. Same JSON against stock connect-distributed.sh is a 400: unknown connector. Default apache/kafka Docker is the Linux broker. This leftover is the Windows CLASSPATH.
I read the Kafka CVE list first: JAAS Jndi/Ldap, OAUTHBEARER URL allowlist, DefaultJwtValidator, ConfigProvider, FileStream's public JIRA. Unix kafka-run-class.sh still has the exclude:
regex="(-(test|test-sources|src|scaladoc|javadoc)\.jar|jar.asc|connect-file.*\.jar)$"
cc_pkg also omits file. Then I opened the Windows copy:
for %%p in (api runtime file json tools) do (
for %%i in ("%BASE_DIR%\connect\%%p\build\libs\connect-%%p*.jar") do (
call :concat "%%i"
)
)
rem Classpath addition for release
for %%i in ("%BASE_DIR%\libs\*") do (
call :concat "%%i"
)
build.gradle still copies connect-file into libs/. Docs say FileStream is not on the default CLASSPATH. That sentence is true for .sh.
Doyensec wrote this class in 2022. KAFKA-13748 / PR 11908 was the Unix fix. The .bat was never updated.
Wrong turns already recorded: Wine; executing the .bat on macOS; treating plugin.path as the hole (it is empty on both workers); claiming Linux Docker is this bug; taking a 201 on Unix as SUCCESS. Theatre: a reverse shell. The oracle is Unix 400 plus a witness line in the sink file.
cd lab
./run.sh
Unix REST 127.0.0.1:18083 is stock connect-distributed.sh. Windows-sim 127.0.0.1:18084 is unfiltered libs/*, the release .bat libs\* loop. Not Wine. Broker is apache/kafka:4.3.1 on the compose network. Bind it to loopback.
unix-filestream=absent unix-post=400
win-filestream=present win-source=201 win-sink=201 pwned=yes
SUCCESS kafka-connect-filestream-win ... KAFKA-CONNECT-FILESTREAM-WIN-WITNESS
Give kafka-run-class.bat the same connect-file exclude the .sh already has. Drop file from the gradle-from-source for %%p in (api runtime file json tools) loop. Keep shipping the jar for people who set plugin.path on purpose.
26b251a451ce941d3d7a55e6487bcb7f16b5ad48)bin/windows/kafka-run-class.bat · bin/kafka-run-class.shFileStreamSourceTask.java · FileStreamSinkTask.java