V12, a company that builds AI agents for finding vulnerabilities, posted a video on November 11: a message with a widget in Telegram for Android launches the calculator on the phone. The calculator is the traditional demonstration that an attacker is running their own code on the device, not just the code the sandbox allows. The vulnerability is fixed in Telegram for Android 13.2.2 (7303); it concerns anyone running a widget-capable Android client older than that. V12's tweet quotes Pavel Durov's post "Any Telegram message can now become an app", with the last word swapped for RCE.

The post on X — @v12sec
V12's video of a widget launching the calculator

What to do

Update Telegram for Android to 13.2.2 or newer. Your version number is shown at the bottom of the Settings screen. A widget's code runs when its bubble is shown in the chat, so until you update it is sensible not to open chats and channels where unfamiliar bots and people send widgets. V12 did not say whether the attack requires tapping anything.

Neither V12 nor Telegram's commits say whether iOS and the desktop clients are affected.

What a widget is and where the bug was

A widget is an HTML document with styles and JavaScript that a bot sends in a message and the client draws right inside the chat bubble. There is no browser underneath: Telegram's own renderer draws the markup, and the script runs in QuickJS, a small JavaScript interpreter written in C. How it all fits together is covered in the piece on widgets.

The widget sandbox limits what a script can do through the programming interface: network, links, requests to the bot. But the interpreter itself works with memory directly, and a bug in it sits below that sandbox. According to the description of the vulnerability V12 found, it is a use-after-return in QuickJS: access to a function's memory after that function has already returned.

In Telegram's open source code exactly this kind of bug is fixed by a commit to the QuickJS fork dated November 10; the Android client's documentation marks it as a report against version 13.2.1. The mechanism, as the commit describes it, goes like this. JavaScript recently gained the await using construct: a variable declaration whose resource is released automatically when the block exits, awaiting an asynchronous operation. Awaiting is only allowed in async functions, but in one place the QuickJS parser did not check for that. As a result an await instruction ended up in an ordinary function, and on exit the interpreter took the path meant for generators, skipping the step where closures are detached from the function's variables. Closures created inside kept pointing at a region of the stack that, after the return, had already been handed over to other data. A reference into reused memory like this is a typical starting point for reading and overwriting other data in the process.

The fix is twofold: the parser now rejects await using outside async functions, and the interpreter refuses to suspend an ordinary function on any await or generator instruction, so that the next parser slip cannot open the same path.

The link between the commit and V12's report rests on the matching description: the commit names no researchers, and there is no write-up from V12 yet. Instead of details, V12's tweet carries a string of 64 hexadecimal characters, the length of a SHA-256 hash. This is usually how the contents of a report or exploit are pinned down without revealing them, to prove priority later; V12 did not explain what exactly was hashed.

How it was fixed

There have been several fixes to the widget engine over the past week. They go through the shared widget-sandbox repository, from which the clients build the engine:

DateWhat shipped
November 6v0.6.1: seven memory fixes from upstream QuickJS, among them a RegExp bug marked as a submitted RCE report
November 7Telegram for Android 13.2.0 and 13.2.1 in the sources
November 9v0.6.2 and v0.6.3: the QuickJS parser no longer goes out of bounds of its tables when memory runs out
November 10, 13:16 UTCthe await using fix in the QuickJS fork
November 10, 13:37 UTCPavel Durov announces widgets, Bot API 10.4
November 10v0.6.4: five more memory fixes, all reachable from ordinary widget JavaScript
November 11, 11:00 UTCV12's video
November 12Telegram for Android 13.2.2 in the sources

The await using fix is dated 21 minutes before Durov's post that opened widgets to all bots: the report arrived before the announcement. In 13.2.2 the engine is pinned to the commit with this fix; the five fixes of v0.6.4 came out later the same day and, judging by the pinned versions, did not make it into 13.2.2.

The commits do not say who reported the RegExp bug fixed on November 6.

A separate process for widgets

The main change in 13.2.2 is not a single line in the parser but a new boundary. The release commit touches 96 files and about 6,800 lines, and most of that moves widget code into a separate isolated process, :widgets. According to the documentation in the client's code:

  • This process runs the widget's JavaScript, the validator and the renderer. It has no Android permissions, no network and no access to the app's files, meaning accounts, messages and keys.
  • The main Telegram process no longer loads the engine library at all.
  • Everything the widget process sends back the client treats as untrusted: it re-checks links, text copying, requests to the bot and wallet calls itself — for example, one link per one real tap by the user.
  • On top sits a seccomp system call filter. For now it runs in logging mode: forbidden calls are recorded but still executed. Launching programs and creating processes are refused already.

There is one process for all widgets rather than one per bot: that option was tried and dropped because of memory, about 25 MB per process. Isolation separates widget code from the app's data, but not widgets from each other. If a similar bug turns up in QuickJS again, code execution will land in a process that has neither the account nor the chats.

Who V12 are

V12 is an autonomous system for finding vulnerabilities in code: AI agents analyse a repository and return findings with a proof and a fix. The founder is Jasraj Bedi, co-founder of the auditing firm Zellic, who previously worked on hacking Android at Google. V12's list of findings includes vulnerabilities in Redis, PostgreSQL, MariaDB, QEMU and Signal's contact discovery enclave; in September the company already named Telegram among the apps whose in-app browser on iOS rendered downloaded files as a page.

This is the second claimed RCE in Telegram for Android in two weeks: at the end of October the startup NebuSec described a bug in the call code.