Summary
The bundled-binary auto-updater (src/kimi_cli/ui/shell/update.py) downloads a tarball from https://cdn.kimi.com/binaries/kimi-cli/{version}/..., extracts it with unfiltered tarfile.extractall, copies the kimi binary out, marks it executable, and the user runs it on next launch — all without verifying:
- the SHA-256 of the tarball against a published manifest,
- a signature (minisign / cosign / GPG) over the tarball or the binary,
- or even the byte length against a manifest.
HTTPS protects the channel against passive MITM, but it does not protect against:
- A compromise or misconfiguration of
cdn.kimi.com (cache poisoning, accidental publish of a wrong artifact).
- A user/enterprise environment with a custom root CA installed (corporate proxies, mitmproxy testing setups, malware-installed root CAs).
- A future migration to a different CDN where TLS settings differ.
The first one is the real concern: a single bad publish silently pushes a compromised binary to every kimi-cli user on next auto-update. Because the binary is then chmod +x and executed as the user, this is straightforwardly RCE-on-end-user equivalent.
This pattern is well-understood; modern release tooling (GoReleaser, cargo-dist, Homebrew bottles, npm-with-provenance) all ship a manifest with sha256 sums + optional signatures specifically to break this trust dependency.
Secondary issue (smaller): unfiltered tarfile.extractall(tmpdir)
Python 3.12 deprecated extractall() without an explicit filter= argument; Python 3.14 (which .python-version pins) emits a DeprecationWarning, and a future release will raise. CVE-2007-4559 (the original "tar slip" bug) made filter='data' the recommended default — even for trusted sources, since it's defense-in-depth and silences the deprecation:
- tar.extractall(tmpdir)
+ tar.extractall(tmpdir, filter="data") # PEP 706
I noticed kimi-cli's other archive-extraction sites (src/kimi_cli/cli/plugin.py:77, src/kimi_cli/vis/api/sessions.py:657) both have explicit path-traversal checks before extractall. The auto-updater is the lone outlier.
Repro
I have not exploited this — there is nothing to exploit on a healthy cdn.kimi.com. The repro is structural: read src/kimi_cli/ui/shell/update.py and search for any call into hashlib, cryptography, signify, minisign, gpg, etc. There are none. The download → extract → chmod +x → execute path runs unconditionally if the HTTPS download itself succeeds.
Proposed fix
Two parts.
1. Publish + verify a sha256 manifest. Alongside each kimi-{version}-{target}.tar.gz on the CDN, publish kimi-{version}-{target}.tar.gz.sha256 (and ideally a single manifest.json per version with all targets). The updater fetches both, verifies, and only then extracts:
import hashlib
# After downloading tar to tar_path, also fetch the .sha256 sidecar.
async with session.get(download_url + ".sha256") as resp:
resp.raise_for_status()
expected_sha256 = (await resp.text()).strip().split()[0]
actual_sha256 = hashlib.sha256(Path(tar_path).read_bytes()).hexdigest()
if actual_sha256 != expected_sha256:
logger.error(
"Checksum mismatch for {url}: expected {expected}, got {actual}",
url=download_url, expected=expected_sha256, actual=actual_sha256,
)
_print("[red]Update aborted: checksum mismatch.[/red]")
return UpdateResult.FAILED
2. (Stronger, optional follow-up) sign the manifest with a long-lived release key (cosign / minisign / sigstore). This protects against CDN compromise even if the attacker can replace both the tarball and its .sha256 sidecar.
3. Add filter="data" to the extractall call as a 1-line defense-in-depth + Python-3.14 future-proof fix.
Why I'm filing this publicly
There's no live exploit to disclose privately — this is a structural defense-in-depth gap, observable directly from the public source. Auto-updaters are a textbook supply-chain target, and a 30-line defensive change today saves you from a hard-to-detect incident later. Happy to send a PR if you'd like — I'd start with the sha256 sidecar since it doesn't require key management.
Summary
The bundled-binary auto-updater (
src/kimi_cli/ui/shell/update.py) downloads a tarball fromhttps://cdn.kimi.com/binaries/kimi-cli/{version}/..., extracts it with unfilteredtarfile.extractall, copies thekimibinary out, marks it executable, and the user runs it on next launch — all without verifying:HTTPS protects the channel against passive MITM, but it does not protect against:
cdn.kimi.com(cache poisoning, accidental publish of a wrong artifact).The first one is the real concern: a single bad publish silently pushes a compromised binary to every kimi-cli user on next auto-update. Because the binary is then
chmod +xand executed as the user, this is straightforwardly RCE-on-end-user equivalent.This pattern is well-understood; modern release tooling (GoReleaser, cargo-dist, Homebrew bottles, npm-with-provenance) all ship a manifest with sha256 sums + optional signatures specifically to break this trust dependency.
Secondary issue (smaller): unfiltered
tarfile.extractall(tmpdir)Python 3.12 deprecated
extractall()without an explicitfilter=argument; Python 3.14 (which.python-versionpins) emits a DeprecationWarning, and a future release will raise. CVE-2007-4559 (the original "tar slip" bug) madefilter='data'the recommended default — even for trusted sources, since it's defense-in-depth and silences the deprecation:I noticed kimi-cli's other archive-extraction sites (
src/kimi_cli/cli/plugin.py:77,src/kimi_cli/vis/api/sessions.py:657) both have explicit path-traversal checks beforeextractall. The auto-updater is the lone outlier.Repro
I have not exploited this — there is nothing to exploit on a healthy
cdn.kimi.com. The repro is structural: readsrc/kimi_cli/ui/shell/update.pyand search for any call intohashlib,cryptography,signify,minisign,gpg, etc. There are none. The download → extract → chmod +x → execute path runs unconditionally if the HTTPS download itself succeeds.Proposed fix
Two parts.
1. Publish + verify a sha256 manifest. Alongside each
kimi-{version}-{target}.tar.gzon the CDN, publishkimi-{version}-{target}.tar.gz.sha256(and ideally a singlemanifest.jsonper version with all targets). The updater fetches both, verifies, and only then extracts:2. (Stronger, optional follow-up) sign the manifest with a long-lived release key (cosign / minisign / sigstore). This protects against CDN compromise even if the attacker can replace both the tarball and its
.sha256sidecar.3. Add
filter="data"to theextractallcall as a 1-line defense-in-depth + Python-3.14 future-proof fix.Why I'm filing this publicly
There's no live exploit to disclose privately — this is a structural defense-in-depth gap, observable directly from the public source. Auto-updaters are a textbook supply-chain target, and a 30-line defensive change today saves you from a hard-to-detect incident later. Happy to send a PR if you'd like — I'd start with the sha256 sidecar since it doesn't require key management.