← research

1SEAL-2026-007: heap buffer over-read in Telegram iOS TL deserialization from operator precedence bug

medium
CVSS 5.3 CWE-125 TelegramMessenger/Telegram-iOS fix commit 8e9cd79855683efb9a3cbf14a1ecd637cfbf7b54 fixed in release-12.4 2026-03-29

summary

readBytesAsInt32(_:) in Telegram iOS used a mixed && / || gate for 1-4 byte reads. because && binds tighter than ||, the count > 0 && count <= 4 side of the condition could succeed before the remaining-buffer check was enforced.

when TL parsing reached the 3-byte length lane used after a 0xfe bytes marker, parseBytes could call readBytesAsInt32(3) on truncated input and still reach memcpy. the result was a bounded read past the logical end of the tracked buffer.

the report was sent privately to Telegram on 2026-01-30. the public fix landed on 2026-02-04 in commit 8e9cd79855683efb9a3cbf14a1ecd637cfbf7b54 and the corrected implementation is present in tagged state release-12.4.

severity

MEDIUM — CVSS 3.1 Base Score: 5.3

Vector: AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

CWE: CWE-125 (Out-of-bounds Read)

vendor outcome: accepted by Telegram with a USD 1,500 bounty offer, declined by the researcher.

affected versions

confirmed vulnerable in tagged releases release-11.15, release-12.0, release-12.3.1, and release-12.3.2.

the vulnerable form was present no later than commit 60a504a9a896cb0828f1d53057705df33d5c0938.

patched versions

fixed by 8e9cd79855683efb9a3cbf14a1ecd637cfbf7b54 and confirmed fixed in release-12.4 and later observed tags such as release-12.5.

root cause

the vulnerable branch tried to enforce two independent conditions:

  1. the requested read width is between 1 and 4 bytes
  2. offset + count stays within buffer._size

because those predicates were joined with ||, the first condition was enough to enter the branch for small reads. parseBytes uses the 3-byte length path after the 0xfe TL marker, so attacker-controlled truncated TL input could steer execution into memcpy even when fewer than 3 bytes remained.

exploitation chain

  1. an attacker supplies malformed TL-encoded bytes to a parser path that reaches parseBytes
  2. the parser observes a 0xfe marker and requests readBytesAsInt32(3)
  3. the mixed boolean expression accepts the read before enforcing the remaining-buffer check
  4. memcpy reads past the logical end tracked by buffer._size
  5. the result is limited adjacent-byte disclosure with crash potential in harsher memory layouts

fix

the public fix rewrote the branch as a fail-closed guard sequence:

  • require count > 0
  • require count <= 4
  • require self.offset + UInt(count) <= self.buffer._size
  • require self.buffer.data to be non-nil before memcpy

timeline

date event
2026-01-30 report emailed privately to Telegram
2026-02-04 public fix commit 8e9cd79855683efb9a3cbf14a1ecd637cfbf7b54 lands
2026-02-04 tagged state release-12.4 contains the corrected implementation

credit

discovered and reported by Oleh Konko (1seal).

user guidance

users should move to release-12.4 or later, or to any build that includes commit 8e9cd79855683efb9a3cbf14a1ecd637cfbf7b54.

because the bug sits in the shared TL buffer reader, downstream forks or snapshots that copied the same readBytesAsInt32(_:) implementation should audit for the same condition and adopt the fixed guard logic.

references