Phar TAR phar_tar_number() Integer Overflow - Archive Entry Injection

Kurzinfo

Metadaten

  • Original (extern): Link oeffnen
  • Veroeffentlicht: 2026-09-24 10:06 UTC
  • Importiert: 2026-10-21 14:15 UTC
  • CVSS: 4.3
  • Quelle-ID: php_sec
  • uid_hash: b7164bf15a96ca39150cac2a636279d0d0f3f01da30b7d690accb89286a8451f

Inhalt

Summary

phar_tar_number() parses the octal size field of a TAR header into a uint32_t with no overflow check. The field is 11 octal digits wide and holds values up to 0x1FFFFFFFF, so a size above 0xFFFFFFFF silently wraps. The parser then skips the wrong number of data blocks and interprets attacker-controlled file content as the next TAR header, which lets a crafted archive inject entries that PharData reports and extracts as if they were genuine.

Details

The parser accumulates octal digits without bounding the result:

https://github.com/php/php-src/blob/php-8.5.10/ext/phar/tar.c#L23-L38

Octal field True value uint32_t result Effect
37777777777 0xFFFFFFFF 4294967295 largest value that does not wrap
40000000000 0x100000000 0 size wraps to 0
40000000001 0x100000001 1 size wraps to 1
40000001000 0x100000200 512 size wraps to 512

When the size wraps to 0 the parser reads the size at line 275, rounds it up to a block boundary at line 585 where (0 + 511) & ~511 stays 0, then skips the data because the size > 0 condition at line 587 is false. The next 512 bytes read are the entry's own data, parsed as a TAR header.

The GNU long filename path has an overflow check, but it only rejects UINT_MAX and 0:

https://github.com/php/php-src/blob/php-8.5.10/ext/phar/tar.c#L368

A size that wraps to 1 passes it and allocates a one-byte buffer for what the header declares as a filename of more than 4 GB.

GNU tar rejects the same archive with not stopped at a record boundary, while PharData accepts it and exposes the injected entries.

The fix replaces the size parse with phar_tar_size(), which accumulates into a uint64_t, rejects the GNU base-256 encoding, requires the whole field to be consumed, and refuses any value above UINT32_MAX - 511. It also introduces phar_tar_type_has_data() so the data blocks of every entry type that carries data are skipped, and rejects the unsupported GNU long link record outright.

PoC

  1. Build a TAR archive with a legitimate entry followed by an entry whose size field is 40000000000.
  2. Place a valid TAR header in the data block immediately after that entry.
  3. Open the archive with new PharData($file).

The injected header is parsed as a real entry with an attacker-chosen name, size and content, and appears in the manifest alongside the legitimate entries. Reading through phar:// and extracting with extractTo() both return the injected version. Giving the injected entry the same name as a legitimate one replaces the original in the manifest, and a single overflow entry can introduce any number of further entries.

The regression tests ext/phar/tests/tar/GHSA-j3wh-g957-2m85-size.phpt, -longlink.phpt and -typeflag.phpt cover the wrapped size, the long filename check and the entry types that carry data.

Impact

An application that opens an untrusted TAR archive with PharData or the phar:// stream wrapper sees a set of entries that differs from what the archive actually contains, and extractTo() writes the injected entries to disk. Tooling that validates an archive before extracting it can be made to inspect one set of files and extract another.

Composer's TarDownloader uses new PharData($file) followed by extractTo() for every .tar.gz package, so a crafted package can place files into vendor/ that are not present in the archive as any other tool reads it.

Verknuepfte CVEs

CVE-ID Severity (Quelle) CVSS (Quelle) Severity (CVE.org) CVSS (CVE.org) EPSS EPSS-Pctl Veroeffentlicht (CVE.org)

CVE-2026-6103

MEDIUM

4.3

MEDIUM

4.3

2026-09-25

Quellen-Details

Bezeichnung Name Kategorie Tags Zielgruppe Sprache Feed-URL
PHP Security (php/php-src GHSA)

php_sec

vendor_advisory php, runtime - de https://github.com/php/php-src/security/advisories