Research/cve-2026-105221-gist-tls
N-dayCVE-2026-105221HighPublic

CVE-2026-105221: gist RubyGem VERIFY_NONE leaks GitHub tokens

gist RubyGem before 6.1.0 sets OpenSSL VERIFY_NONE on HTTPS to GitHub. An on-path attacker can present any certificate, read the OAuth token the CLI sends, and then read or change the victim's gists. Labbed 6.0.0 against 6.1.0 on loopback. No public PoC existed.

Name
CVE-2026-105221: gist RubyGem VERIFY_NONE leaks GitHub tokens
Type
N-day analysis
CVE
CVE-2026-105221
CVE Risk
high
Disclosure Status
public
Vendor
defunkt
Affected
gist RubyGem 4.0.0 through 6.0.0. Lab pin 6.0.0. Fixed in 6.1.0 (07ccc1a). On-path MITM of the gist CLI.
Published
05 Oct 2026
Updated
05 Oct 2026
Tags
n-day, gist, ruby, tls, mitm, cwe-295

gist hung up on github with the cert check off

The proof of concept is on GitHub: abraxas/cve-2026-105221-gist-tls (loopback client; @abraxas_null). The lab stack is lab/: Dockerfile, docker-compose.yml, run.sh, poc.py. Authorized lab only. It talks to loopback.

This is CVE-2026-105221 in the gist RubyGem. defunkt. CWE-295. 7.4 High (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N). VulnCheck's 4.0 score is 9.1. On-path MITM of the CLI. Patched in 6.1.0 (07ccc1a). Siyang Wu found it. Issue 373 / PR 374. I labbed the public CVE. There was no public PoC.

gist is the old command-line client for gist.github.com. You gist --login, it stores an OAuth token in ~/.gist, then every upload and list call hits the GitHub API over HTTPS. Encryption was on. The peer check was not.

What an attacker can do

The attacker sits on the path and takes the GitHub token.

  • Present any TLS certificate. gist 6.0.0 sets verify_mode = OpenSSL::SSL::VERIFY_NONE. A self-signed cert with CN=mitm.invalid is enough. The CLI keeps talking.
  • Read Authorization: token .... Upload, list, delete, and login all go through http_connection. The token in ~/.gist rides that socket in the clear to whoever terminated TLS.
  • Read and edit the victim's gists. That token is a GitHub OAuth credential with the gist scope. Private gists. Updates. Deletes.
  • Catch a login in flight. Device-flow and username/password login use the same unverified TLS. The attacker can take the fresh token before it ever hits disk.

They need a network position: hostile Wi-Fi, a proxy, a box in the middle. This is not unauthenticated remote RCE against a server. It is the gist CLI trusting whoever answered.

6.1.0 sets VERIFY_PEER. The same bogus cert dies with certificate verify failed (self-signed certificate) and the token never leaves the client.

How I found it

The CVE text names http_connection in lib/gist.rb. I pinned gem 6.0.0 next to 6.1.0 and read the one-line flip.

lib/gist.rb on 6.0.0:

Plain text
if uri.scheme == "https"
  connection.use_ssl = true
  connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

6.1.0 is VERIFY_PEER. ConradIrwin's note on the PR: Ruby's certificate story was bad when this was written, and it had not been revisited.

I did not intercept real api.github.com. The lab is a fake GitHub Enterprise HTTPS stub on loopback with a self-signed cert. GITHUB_URL=https://127.0.0.1:8443 puts gist in GHE mode, so it POSTs /api/v3/gists. A lab token CVE-2026-105221-WITNESS sits in the matching ~/.gist.* file. gist 6.0.0 completes TLS. The stub captures Authorization: token CVE-2026-105221-WITNESS and writes the witness. gist 6.1.0 against the same stub fails TLS. Capture count on 6.1.0 stays zero.

Plain text
SUCCESS CVE-2026-105221 vuln-tls=ok token-captured=1 patched-tls=fail witness-written=yes CVE-2026-105221-WITNESS

Wrong turns already recorded: none. First run.sh printed SUCCESS. A reverse shell. Theatre. The oracle is the captured lab token on 6.0.0 and the self-signed TLS error on 6.1.0.

VERIFY_NONE is a peer, not a lock

The gem still spoke TLS. Wireshark on the wire would see ciphertext. The person who terminated that TLS, with any cert they minted, would see the token.

Upgrade to 6.1.0 or later.

I am @abraxas_null. Site abraxaslabs.tech. GitHub abraxas. Mail abraxas.null@proton.me.

CVE-2026-105221: gist RubyGem VERIFY_NONE leaks GitHub tokens · Abraxas Labs