{"id":39675,"date":"2021-02-22T16:03:22","date_gmt":"2021-02-22T16:03:22","guid":{"rendered":"https:\/\/packetstormsecurity.com\/news\/view\/32045\/Hunting-For-Bugs-In-Telegrams-Animated-Stickers-Remote-Attack-Surface.html"},"modified":"2021-02-22T16:03:22","modified_gmt":"2021-02-22T16:03:22","slug":"hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface","status":"publish","type":"post","link":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/","title":{"rendered":"Hunting For Bugs In Telegram&#8217;s Animated Stickers Remote Attack Surface"},"content":{"rendered":"<h2 id=\"introduction\">Introduction<\/h2>\n<p>At the end of October \u201819 I was skimming the <a href=\"https:\/\/github.com\/drklo\/telegram\" target=\"_blank\" rel=\"noopener noreferrer\">Telegram\u2019s android app code<\/a>, learning about the technologies in use and looking for potentially interesting features. Just a few months earlier, Telegram had introduced the <a href=\"https:\/\/telegram.org\/blog\/animated-stickers\" target=\"_blank\" rel=\"noopener noreferrer\">animated stickers<\/a>; after reading the blogpost I wondered how they worked <em>under-the-hood<\/em> and if they created a new image format for it, then forgot about it.<br \/>\nBack to the skimming, I stumbled upon the <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/tree\/master\/TMessagesProj\/jni\/rlottie\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>rlottie<\/strong> folder<\/a> and started googling. It turned out to be the <a href=\"https:\/\/github.com\/samsung\/rlottie\" target=\"_blank\" rel=\"noopener noreferrer\">Samsung native library<\/a> for playing Lottie animations, originally created by <a href=\"http:\/\/airbnb.io\/lottie\/#\/\" target=\"_blank\" rel=\"noopener noreferrer\">Airbnb<\/a>. I don\u2019t know about you but the combination of <strong>Telegram<\/strong>, <strong>Samsung<\/strong>, <strong>native<\/strong> and <strong>animations<\/strong> instantly triggered my interest in learning more \ud83d\udc40.<\/p>\n<h2 id=\"executive-summary\">Executive summary<\/h2>\n<p><strong>Research is one of Shielder\u2019s pillars<\/strong> \u2013 head over to our <a href=\"https:\/\/www.shielder.it\/advisories\/\" target=\"_blank\" rel=\"noopener noreferrer\">research page<\/a> to learn more about our commitment to improve the security of the digital ecosystem.<\/p>\n<p>What follows is my journey in researching the lottie animation format, its integration in mobile apps and the <strong>vulnerabilities triggerable by a remote attacker against any Telegram user<\/strong>. The research started in January 2020 and lasted until the end of August, with many pauses in between to focus on other projects.<\/p>\n<p><strong>During my research I have identified 13 vulnerabilities in total<\/strong>: 1 heap out-of-bounds write, 1 stack out-of-bounds write, 1 stack out-of-bounds read, 2 heap out-of-bound read, 1 integer overflow leading to heap out-of-bounds read, 2 type confusions, 5 denial-of-service (null-ptr dereferences).<\/p>\n<p><strong>All the issues I have found have been responsibly reported to and fixed by Telegram<\/strong> with updates released in September and October 2020:<\/p>\n<ul>\n<li><strong>Telegram Android v7.1.0 (2090)<\/strong> (released on September 30, 2020) and later;<\/li>\n<li><strong>Telegram iOS v7.1<\/strong> (released on September 30, 2020) and later;<\/li>\n<li><strong>Telegram macOS v7.1<\/strong> (released on October 2, 2020) and later.<\/li>\n<\/ul>\n<p>Those updates include the fixes (the other types of clients are not affected by the vulnerabilities I have identified) \u2013 basically <strong>if you have updated your Telegram client in the last 4 months you are safe<\/strong>. If not, I recommend you to update it as soon as possible.<\/p>\n<h2 id=\"table-of-contents\">Table of contents<\/h2>\n<ul>\n<li>Lottie by Airbnb<\/li>\n<li>RLottie by samsung, forked by Telegram<\/li>\n<li>Harnessing rlottie and building a corpus<\/li>\n<li>Fuzzing techniques and results\n<ul>\n<li>coverage-guided fuzzing<\/li>\n<li>layman\u2019s guide to crash testcase minimization (excursus)<\/li>\n<li>heap out-of-bounds write in VGradientCache::generateGradientColorTable<\/li>\n<li>structure-aware fuzzing<\/li>\n<\/ul>\n<\/li>\n<li>Telegram\u2019s animated stickers attack surface\n<ul>\n<li>how they patched it<\/li>\n<\/ul>\n<\/li>\n<li>Conclusions<\/li>\n<\/ul>\n<h2 id=\"lottie-by-airbnb\">Lottie by Airbnb<\/h2>\n<p>Let\u2019s start from the original Lottie project by Airbnb, from <a href=\"https:\/\/airbnb.io\/lottie\/\" target=\"_blank\" rel=\"noopener noreferrer\">airbnb.io\/lottie<\/a>:<\/p>\n<blockquote readability=\"9\">\n<p>Lottie is a library for Android, iOS, Web, and Windows that parses Adobe After Effects animations exported as json with Bodymovin and renders them natively on mobile and on the web!<\/p>\n<\/blockquote>\n<p>\u201cAs <strong>json<\/strong>\u201d is particularly interesting here, I was expecting some tricky 90\u2019s proprietary binary specification but instead they chose to use one of the most common and simple formats to date. (This got me also wondering whether memory corruptions would be harder to find, but it was too early to tell!)<\/p>\n<p>As we have read, a Lottie animation is defined as a JSON with some information such as the frame rate \u201c<strong>fr<\/strong>\u201d and the version identifier \u201c<strong>v<\/strong>\u201d at its root, while most of the juicy features lie in the \u201c<strong>layers<\/strong>\u201d array.<\/p>\n<p>At its minimum, a Lottie animation looks like this:<\/p>\n<div class=\"highlight\">\n<div class=\"chroma\">\n<table class=\"lntable\" readability=\"3.5\">\n<tr readability=\"10.5\">\n<td class=\"lntd\">\n<pre class=\"chroma\"><code><span class=\"lnt\">1\n<\/span><span class=\"lnt\">2\n<\/span><span class=\"lnt\">3\n<\/span><span class=\"lnt\">4\n<\/span><span class=\"lnt\">5\n<\/span><span class=\"lnt\">6\n<\/span><span class=\"lnt\">7\n<\/span><\/code><\/pre>\n<\/td>\n<td class=\"lntd\" readability=\"10\">\n<pre class=\"chroma\"><code class=\"language-js\" data-lang=\"js\"><span class=\"p\">{<\/span> <span class=\"s2\">\"v\"<\/span><span class=\"o\">:<\/span><span class=\"s2\">\" \"<\/span><span class=\"p\">,<\/span> <span class=\"c1\">\/\/ version identifier\n<\/span><span class=\"c1\"><\/span> <span class=\"s2\">\"fr\"<\/span><span class=\"o\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"c1\">\/\/ frame rate\n<\/span><span class=\"c1\"><\/span> <span class=\"s2\">\"ip\"<\/span><span class=\"o\">:<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"c1\">\/\/ in-point\n<\/span><span class=\"c1\"><\/span> <span class=\"s2\">\"op\"<\/span><span class=\"o\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"c1\">\/\/ out-point\n<\/span><span class=\"c1\"><\/span> <span class=\"s2\">\"layers\"<\/span><span class=\"o\">:<\/span><span class=\"p\">[]<\/span> <span class=\"c1\">\/\/ the good stuff (tm)\n<\/span><span class=\"c1\"><\/span><span class=\"p\">}<\/span>\n<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<\/div>\n<\/div>\n<p>This doesn\u2019t include any graphical element, but it\u2019s useful to have a bare-minimum example before getting complex (especially in structure-aware fuzzing, as we will discuss later).<\/p>\n<p>Remember the \u201cAdobe After Effects animations exported as json\u201d part? If you open such an animation it contains a lot of useless information and animation\u2019s metadata, for example Adobe After Effects even supports <a href=\"https:\/\/helpx.adobe.com\/after-effects\/user-guide.html\/after-effects\/using\/scripts.ug.html\" target=\"_blank\" rel=\"noopener noreferrer\">\u201cthe Adobe ExtendScript language, which is an extended form of JavaScript\u201d<\/a> (!), which is included in the JSON but <a href=\"https:\/\/github.com\/samsung\/rlottie#supported-after-effects-features\" target=\"_blank\" rel=\"noopener noreferrer\">not supported<\/a> by the Lottie parser we are going to talk about.<\/p>\n<p>It\u2019s important to notice here that Lottie animations are <a href=\"http:\/\/airbnb.io\/lottie\/#\/community-showcase\" target=\"_blank\" rel=\"noopener noreferrer\">widely used<\/a>, though most of the time via static resources such as app\u2019s transitions and animations. Another important thing to notice is that other apps, such as <strong><a href=\"https:\/\/github.com\/signalapp\/Signal-Android\/blob\/v5.3.10\/app\/build.gradle#L379\" target=\"_blank\" rel=\"noopener noreferrer\">Signal<\/a><\/strong>, <strong>chose Airbnb\u2019s java\/swift implementation<\/strong>.<\/p>\n<h2 id=\"rlottie-by-samsung-forked-by-telegram\">RLottie by Samsung, forked by Telegram<\/h2>\n<p>Here we arrive at Samsung\u2019s C++ library <a href=\"https:\/\/github.com\/samsung\/rlottie\" target=\"_blank\" rel=\"noopener noreferrer\">rlottie<\/a> to parse Lottie animations. I\u2019m not sure why Telegram\u2019s developers decided to use this implementation instead of Airbnb\u2019s, besides performance (and the chance to expose a 1-click native attack surface \ud83d\ude0b). That being said, working with an open-source library will come in handy for setting up the fuzzing environment and triaging the crashes, something <a href=\"https:\/\/googleprojectzero.blogspot.com\/2020\/07\/mms-exploit-part-2-effective-fuzzing-qmage.html\" target=\"_blank\" rel=\"noopener noreferrer\">which is not as trivial to do in a black-box scenario<\/a>.<\/p>\n<p><a href=\"https:\/\/github.com\/samsung\/rlottie#supported-after-effects-features\" target=\"_blank\" rel=\"noopener noreferrer\">RLottie doesn\u2019t support all of After Effect\u2019s features<\/a>, however it is still actively maintained to this day, even though I\u2019m not 100% sure what Samsung uses rlottie for besides probably <a href=\"https:\/\/developer.samsung.com\/tizen\/blog\/en-us\/2019\/05\/22\/bring-beautiful-lottie-animation-to-your-galaxy-watch-apps\" target=\"_blank\" rel=\"noopener noreferrer\">Samsung Galaxy Watch Apps<\/a>. (If you do know\/find out where it\u2019s used let me know at <a href=\"https:\/\/twitter.com\/polict_\" target=\"_blank\" rel=\"noopener noreferrer\">@polict_<\/a> ! \ud83e\udd1e\ud83c\udffb)<\/p>\n<p>By checking the <a href=\"https:\/\/github.com\/samsung\/rlottie#quick-start\" target=\"_blank\" rel=\"noopener noreferrer\">README<\/a> it\u2019s clear that writing the harness will be trivial; by looking at <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/blob\/002c01ecd37cd08ed07b3ed84d79318d091dfc85\/TMessagesProj\/src\/main\/java\/org\/telegram\/messenger\/ImageLoader.java#L783\" target=\"_blank\" rel=\"noopener noreferrer\">Telegram\u2019s integration<\/a> it\u2019s even possible to copy the initialization settings and build a 1:1 stand-alone harness.<\/p>\n<p>It\u2019s important to note here also that Telegram developers chose to fork the rlottie project and maintain multiple forks of it, which makes security patching especially hard. This will turn out to be an additional problem since the Samsung\u2019s rlottie developers <strong>do not track security issues caused by untrusted animations<\/strong> in their project because they are not \u201cthe intended use case for rlottie\u201d (quote from <a href=\"https:\/\/gitter.im\/rLottie-dev\/community\">https:\/\/gitter.im\/rLottie-dev\/community<\/a> ).<\/p>\n<h2 id=\"harnessing-rlottie-and-building-a-corpus\">Harnessing rlottie and building a corpus<\/h2>\n<p>I had almost no experience in fuzzing before this research, so I started studying and learning about two of the main players at the time: <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\" target=\"_blank\" rel=\"noopener noreferrer\">AFL++<\/a> and <a href=\"https:\/\/llvm.org\/docs\/LibFuzzer.html\" target=\"_blank\" rel=\"noopener noreferrer\">LibFuzzer<\/a>. The majority of entry-level writeups and walkthroughs available publicly were using AFL[++] so I started with it while learning more about the alternatives available. The first version of the harness was a ctrl+c\/ctrl+v <a href=\"https:\/\/www.youtube.com\/watch?v=WamF64GFPzg\" target=\"_blank\" rel=\"noopener noreferrer\">frankenstein<\/a> but it worked well as a starting point:<\/p>\n<div class=\"highlight\">\n<div class=\"chroma\">\n<table class=\"lntable\" readability=\"8.5\">\n<tr readability=\"25.5\">\n<td class=\"lntd\" readability=\"6\">\n<pre class=\"chroma\"><code><span class=\"lnt\"> 1\n<\/span><span class=\"lnt\"> 2\n<\/span><span class=\"lnt\"> 3\n<\/span><span class=\"lnt\"> 4\n<\/span><span class=\"lnt\"> 5\n<\/span><span class=\"lnt\"> 6\n<\/span><span class=\"lnt\"> 7\n<\/span><span class=\"lnt\"> 8\n<\/span><span class=\"lnt\"> 9\n<\/span><span class=\"lnt\">10\n<\/span><span class=\"lnt\">11\n<\/span><span class=\"lnt\">12\n<\/span><span class=\"lnt\">13\n<\/span><span class=\"lnt\">14\n<\/span><span class=\"lnt\">15\n<\/span><span class=\"lnt\">16\n<\/span><span class=\"lnt\">17\n<\/span><span class=\"lnt\">18\n<\/span><span class=\"lnt\">19\n<\/span><span class=\"lnt\">20\n<\/span><span class=\"lnt\">21\n<\/span><span class=\"lnt\">22\n<\/span><span class=\"lnt\">23\n<\/span><span class=\"lnt\">24\n<\/span><span class=\"lnt\">25\n<\/span><span class=\"lnt\">26\n<\/span><span class=\"lnt\">27\n<\/span><span class=\"lnt\">28\n<\/span><span class=\"lnt\">29\n<\/span><span class=\"lnt\">30\n<\/span><span class=\"lnt\">31\n<\/span><span class=\"lnt\">32\n<\/span><span class=\"lnt\">33\n<\/span><span class=\"lnt\">34\n<\/span><span class=\"lnt\">35\n<\/span><span class=\"lnt\">36\n<\/span><span class=\"lnt\">37\n<\/span><span class=\"lnt\">38\n<\/span><span class=\"lnt\">39\n<\/span><span class=\"lnt\">40\n<\/span><span class=\"lnt\">41\n<\/span><span class=\"lnt\">42\n<\/span><span class=\"lnt\">43\n<\/span><span class=\"lnt\">44\n<\/span><span class=\"lnt\">45\n<\/span><span class=\"lnt\">46\n<\/span><span class=\"lnt\">47\n<\/span><span class=\"lnt\">48\n<\/span><span class=\"lnt\">49\n<\/span><\/code><\/pre>\n<\/td>\n<td class=\"lntd\" readability=\"17\">\n<pre class=\"chroma\"><code class=\"language-cpp\" data-lang=\"cpp\"><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;rlottie.h&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;iostream&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;string&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;vector&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;array&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"cp\"><\/span>\n<span class=\"kt\">int<\/span> <span class=\"nf\">entrypoint<\/span><span class=\"p\">(<\/span><span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">string<\/span> <span class=\"n\">filename<\/span><span class=\"p\">){<\/span> <span class=\"k\">auto<\/span> <span class=\"n\">player<\/span> <span class=\"o\">=<\/span> <span class=\"n\">rlottie<\/span><span class=\"o\">::<\/span><span class=\"n\">Animation<\/span><span class=\"o\">::<\/span><span class=\"n\">loadFromFile<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">,<\/span> <span class=\"nb\">NULL<\/span><span class=\"p\">);<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">player<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"error: renderer initialization failed<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span> <span class=\"k\">return<\/span> <span class=\"mi\">1<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span> <span class=\"c1\">\/\/ metadata[0] in Telegram\/TMessagesProj\/jni\/lottie.cpp:130\n<\/span><span class=\"c1\"><\/span> <span class=\"n\">size_t<\/span> <span class=\"n\">frame_count<\/span> <span class=\"o\">=<\/span> <span class=\"n\">player<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">totalFrame<\/span><span class=\"p\">();<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"frame count:<\/span><span class=\"se\">\\t<\/span><span class=\"s\">%zu<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame_count<\/span><span class=\"p\">);<\/span> <span class=\"c1\">\/\/ default width and height\n<\/span><span class=\"c1\"><\/span> <span class=\"kt\">uint32_t<\/span> <span class=\"n\">w<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">512<\/span><span class=\"p\">;<\/span> <span class=\"kt\">uint32_t<\/span> <span class=\"n\">h<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">512<\/span><span class=\"p\">;<\/span> <span class=\"c1\">\/\/ copied from https:\/\/github.com\/Samsung\/rlottie\/blob\/master\/example\/lottie2gif.cpp\n<\/span><span class=\"c1\"><\/span> <span class=\"k\">auto<\/span> <span class=\"n\">buffer<\/span> <span class=\"o\">=<\/span> <span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">unique_ptr<\/span><span class=\"o\">&lt;<\/span><span class=\"kt\">uint32_t<\/span><span class=\"p\">[]<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span><span class=\"k\">new<\/span> <span class=\"kt\">uint32_t<\/span><span class=\"p\">[<\/span><span class=\"n\">w<\/span> <span class=\"o\">*<\/span> <span class=\"n\">h<\/span><span class=\"p\">]);<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">frame_count<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">1<\/span><span class=\"p\">){<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"no frames to render, quitting<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span> <span class=\"k\">return<\/span> <span class=\"mi\">1<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"starting...<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span> <span class=\"k\">for<\/span> <span class=\"p\">(<\/span><span class=\"n\">size_t<\/span> <span class=\"n\">frame<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"n\">frame<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">frame_count<\/span><span class=\"p\">;<\/span> <span class=\"n\">frame<\/span><span class=\"o\">++<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span> <span class=\"n\">rlottie<\/span><span class=\"o\">::<\/span><span class=\"n\">Surface<\/span> <span class=\"n\">surface<\/span><span class=\"p\">(<\/span><span class=\"n\">buffer<\/span><span class=\"p\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(),<\/span> <span class=\"n\">w<\/span><span class=\"p\">,<\/span> <span class=\"n\">h<\/span><span class=\"p\">,<\/span> <span class=\"n\">w<\/span> <span class=\"o\">*<\/span> <span class=\"mi\">4<\/span><span class=\"p\">);<\/span> <span class=\"n\">player<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">renderSync<\/span><span class=\"p\">(<\/span><span class=\"n\">frame<\/span><span class=\"p\">,<\/span> <span class=\"n\">surface<\/span><span class=\"p\">);<\/span> <span class=\"p\">}<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"done!<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">);<\/span> <span class=\"k\">return<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span> <span class=\"kt\">int<\/span> <span class=\"nf\">main<\/span><span class=\"p\">(<\/span><span class=\"kt\">int<\/span> <span class=\"n\">argc<\/span><span class=\"p\">,<\/span> <span class=\"kt\">char<\/span> <span class=\"o\">**<\/span><span class=\"n\">argv<\/span><span class=\"p\">){<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">argc<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">2<\/span><span class=\"p\">){<\/span> <span class=\"n\">printf<\/span><span class=\"p\">(<\/span><span class=\"s\">\"usage: %s &lt;lottie.json&gt;<\/span><span class=\"se\">\\n<\/span><span class=\"s\">\"<\/span><span class=\"p\">,<\/span> <span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]);<\/span> <span class=\"k\">return<\/span> <span class=\"mi\">1<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span> <span class=\"k\">return<\/span> <span class=\"n\">entrypoint<\/span><span class=\"p\">(<\/span><span class=\"n\">std<\/span><span class=\"o\">::<\/span><span class=\"n\">string<\/span><span class=\"p\">(<\/span><span class=\"n\">argv<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]));<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<\/div>\n<\/div>\n<p>(Only later did I discover the <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\/blob\/stable\/docs\/perf_tips.md\" target=\"_blank\" rel=\"noopener noreferrer\">perf_tips<\/a> AFL++ documentation, I strongly recommend it to people starting out fuzzing!)<\/p>\n<p>Having verified the harness was working, I started looking for animated stickers online to build a minimal corpus to start fuzzing: Telegram channels available as a webpage on <code>t.me\/<\/code> URLS and lottie online communities were especially useful for scraping user-generated stickers in an automated <code>curl<\/code>&#8211;<code>grep<\/code>&#8211;<code>gzip<\/code> fashion.<\/p>\n<h2 id=\"fuzzing-techniques-and-results\">Fuzzing techniques and results<\/h2>\n<h3 id=\"coverage-guided-fuzzing\">Coverage-guided fuzzing<\/h3>\n<p>If there\u2019s one thing I have learned the hard way in my information security experience (and later again by reading <a href=\"https:\/\/twitter.com\/halvarflake\/status\/1010473375247097856\" target=\"_blank\" rel=\"noopener noreferrer\">twitter<\/a> heh), it is that many times doing the laziest thing would have produced the same output as a sophisticated technique, but in way less time: this research was no difference.<\/p>\n<figure readability=\"0.8955223880597\"><img decoding=\"async\" src=\"https:\/\/www.shielder.it\/img\/blog\/the-dumb-fuzzer.png\" alt=\"To say it with a meme shamelessly stolen from infosucks and twitter\"><figcaption readability=\"1.7910447761194\">\n<p>To say it with a meme shamelessly stolen from infosucks and <a href=\"https:\/\/twitter.com\/matalaz\/status\/580600098092105728\">twitter<\/a><\/p>\n<\/figcaption><\/figure>\n<p>After instrumenting and improving the harness and launching afl-fuzz, <strong>crashes started to appear in a matter of seconds<\/strong>. I thought that if anybody was fuzzing it, they were either exploiting the issues or still looking for ASLR-breaking gadgets \u2013 but that\u2019s just a guess! \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f<\/p>\n<p>From the first crash triage cycle it seemed some issues could be serious: heap-based out-of-bounds read\/write, stack-based out-of-bounds write and high-address SEGVs all looked promising, so I started investigating them while studying the code and continuously improving and keeping the fuzzer running. Most of the remaining issues were null-pointer dereferences not useful from an exploitation perspective, however in this context &#8211; as we will see later &#8211; they might become an annoying denial-of-service bug for non-technical users.<\/p>\n<h3 id=\"laymans-guide-to-crash-testcase-minimization-excursus\">Layman\u2019s guide to crash testcase minimization (excursus)<\/h3>\n<p>After triaging and prioritizing the crashes I started analyzing the root-cause of each of them. The problem was that since the library parsed JSONs and skipped useless keys, the crashing testcase included a ton of unnecessary keys and values (imagine a single line 2KB JSON with multiple nested void keys\/arrays\/strings\/objects \ud83d\ude44). At the beginning I thought of writing a JSON minimizer tool in python, but remembering the \u201c<strong>try lazy first<\/strong>\u201d way of thinking I hacked together <a href=\"https:\/\/github.com\/googleprojectzero\/halfempty\" target=\"_blank\" rel=\"noopener noreferrer\">halfempty<\/a>, <a href=\"https:\/\/github.com\/google\/sanitizers\/wiki\/AddressSanitizer\" target=\"_blank\" rel=\"noopener noreferrer\">ASAN<\/a> and <code>grep<\/code> to bruteforce their way to the minimized <em>still-crashing-in-the-same-way<\/em> JSON, and it worked pretty well! \ud83d\udc68\ud83c\udffb\u200d\ud83c\udf73<\/p>\n<p>Let\u2019s have a look at one example fed to halfempty:<\/p>\n<div class=\"highlight\">\n<div class=\"chroma\">\n<table class=\"lntable\" readability=\"1.5\">\n<tr readability=\"4.5\">\n<td class=\"lntd\">\n<pre class=\"chroma\"><code><span class=\"lnt\">1\n<\/span><span class=\"lnt\">2\n<\/span><\/code><\/pre>\n<\/td>\n<td class=\"lntd\" readability=\"6\">\n<pre class=\"chroma\"><code class=\"language-bash\" data-lang=\"bash\"><span class=\"cp\">#!\/bin\/bash\n<\/span><span class=\"cp\"><\/span>timeout -k1s 4s rlottie\/parser-asan \/dev\/stdin 2&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">1<\/span> <span class=\"p\">|<\/span> grep -q <span class=\"s1\">'WRITE of size 4 at'<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"nb\">exit<\/span> <span class=\"m\">0<\/span> <span class=\"o\">||<\/span> <span class=\"nb\">exit<\/span> <span class=\"m\">1<\/span>\n<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<\/div>\n<\/div>\n<p>(I could have added more filters to the grep (error type, $pc, stacktrace, \u2026) but it wasn\u2019t really necessary here\u2026)<\/p>\n<p>Afterwards I could simply run halfempty to bruteforce a minimized testcase:<br \/><code>halfempty --stable --zero-char=0x20 --output=min.json run_and_grep_hbof4write.bash raw.json<\/code><\/p>\n<p>This helped because, without further checks besides checking for a SIGSEGV (<code>test $? -eq 139<\/code>), halfempty would have produced a minimized testcase which crashed rlottie with a null-pointer dereference (still a SIGSEGV but not what I was looking for).<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.shielder.it\/img\/blog\/halfempty-asan-grep.png\"><\/figure>\n<p>Back to the fuzzing now\u2026<\/p>\n<h3 id=\"heap-out-of-bounds-write-in-vgradientcachegenerategradientcolortable\">Heap out-of-bounds write in VGradientCache::generateGradientColorTable<\/h3>\n<p>Let\u2019s walk through one of the most impactful issues I have found: a 4-bytes heap out-of-bounds write in <code>VGradientCache::generateGradientColorTable<\/code>. Here\u2019s a sample ASAN report snippet with a bit of context:<\/p>\n<pre><code>==24332==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000001130 at pc 0x0000005652a4 bp 0x7ffef2d69190 sp 0x7ffef2d69188\nWRITE of size 4 at 0x621000001130 thread T0 #0 0x5652a3 in VGradientCache::generateGradientColorTable(std::vector&lt;std::pair&lt;float, VColor&gt;, std::allocator&lt;std::pair&lt;float, VColor&gt; &gt; &gt; const&amp;, float, unsigned int*, int) rlottie\/src\/vector\/vdrawhelper.cpp:159:25 #1 0x574d5c in VGradientCache::addCacheElement(long, VGradient const&amp;) rlottie\/src\/vector\/vdrawhelper.cpp:125:30 #2 0x573645 in VGradientCache::getBuffer(VGradient const&amp;) rlottie\/src\/vector\/vdrawhelper.cpp:87:24 #3 0x569a39 in VSpanData::setup(VBrush const&amp;, VPainter::CompositionMode, int) rlottie\/src\/vector\/vdrawhelper.cpp:761:46 #4 0x53b528 in VPainter::setBrush(VBrush const&amp;) rlottie\/src\/vector\/vpainter.cpp:140:22 #5 0x5c2a15 in LOTLayerItem::render(VPainter*, VRle const&amp;, VRle const&amp;) rlottie\/src\/lottie\/lottieitem.cpp:332:18 #6 0x5c841e in LOTCompLayerItem::renderHelper(VPainter*, VRle const&amp;, VRle const&amp;) rlottie\/src\/lottie\/lottieitem.cpp:651:28 #7 0x5c7744 in LOTCompLayerItem::render(VPainter*, VRle const&amp;, VRle const&amp;) rlottie\/src\/lottie\/lottieitem.cpp:602:9 #8 0x5c0348 in LOTCompItem::render(rlottie::Surface const&amp;) rlottie\/src\/lottie\/lottieitem.cpp:198:17 #9 0x591070 in AnimationImpl::render(unsigned long, rlottie::Surface const&amp;) rlottie\/src\/lottie\/lottieanimation.cpp:107:16 #10 0x5922a5 in rlottie::Animation::renderSync(unsigned long, rlottie::Surface&amp;) rlottie\/src\/lottie\/lottieanimation.cpp:206:8 #11 0x68b146 in entrypoint(std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;) rlottie_parser.cpp:40:17 #12 0x68b40e in main rlottie_parser.cpp:60:16 #13 0x7f22916cebf6 in __libc_start_main \/build\/glibc-S9d2JN\/glibc-2.27\/csu\/..\/csu\/libc-start.c:310 #14 0x41e439 in _start (rlottie\/parser-asan+0x41e439)\n<\/code><\/pre>\n<p>The vulnerability stems from an <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/blob\/release-6.1.1_1946\/TMessagesProj\/jni\/rlottie\/src\/vector\/vdrawhelper.cpp#L158\" target=\"_blank\" rel=\"noopener noreferrer\">incorrectly bounded loop<\/a> (comments are mine):<\/p>\n<div class=\"highlight\">\n<div class=\"chroma\">\n<table class=\"lntable\" readability=\"11\">\n<tr readability=\"33\">\n<td class=\"lntd\" readability=\"5\">\n<pre class=\"chroma\"><code><span class=\"lnt\"> 1\n<\/span><span class=\"lnt\"> 2\n<\/span><span class=\"lnt\"> 3\n<\/span><span class=\"lnt\"> 4\n<\/span><span class=\"lnt\"> 5\n<\/span><span class=\"lnt\"> 6\n<\/span><span class=\"lnt\"> 7\n<\/span><span class=\"lnt\"> 8\n<\/span><span class=\"lnt\"> 9\n<\/span><span class=\"lnt\">10\n<\/span><span class=\"lnt\">11\n<\/span><span class=\"lnt\">12\n<\/span><span class=\"lnt\">13\n<\/span><span class=\"lnt\">14\n<\/span><span class=\"lnt\">15\n<\/span><span class=\"lnt\">16\n<\/span><span class=\"lnt\">17\n<\/span><span class=\"lnt\">18\n<\/span><span class=\"lnt\">19\n<\/span><span class=\"lnt\">20\n<\/span><span class=\"lnt\">21\n<\/span><span class=\"lnt\">22\n<\/span><span class=\"lnt\">23\n<\/span><span class=\"lnt\">24\n<\/span><span class=\"lnt\">25\n<\/span><span class=\"lnt\">26\n<\/span><span class=\"lnt\">27\n<\/span><span class=\"lnt\">28\n<\/span><\/code><\/pre>\n<\/td>\n<td class=\"lntd\" readability=\"23\">\n<pre class=\"chroma\"><code class=\"language-cpp\" data-lang=\"cpp\"><span class=\"kt\">bool<\/span> <span class=\"n\">VGradientCache<\/span><span class=\"o\">::<\/span><span class=\"n\">generateGradientColorTable<\/span><span class=\"p\">(<\/span><span class=\"k\">const<\/span> <span class=\"n\">VGradientStops<\/span> <span class=\"o\">&amp;<\/span><span class=\"n\">stops<\/span><span class=\"p\">,<\/span> <span class=\"kt\">float<\/span> <span class=\"n\">opacity<\/span><span class=\"p\">,<\/span> <span class=\"kt\">uint32_t<\/span> <span class=\"o\">*<\/span><span class=\"n\">colorTable<\/span><span class=\"p\">,<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">size<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">{<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">dist<\/span><span class=\"p\">,<\/span> <span class=\"n\">idist<\/span><span class=\"p\">,<\/span> <span class=\"n\">pos<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">i<\/span><span class=\"p\">;<\/span> <span class=\"kt\">bool<\/span> <span class=\"n\">alpha<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">false<\/span><span class=\"p\">;<\/span> <span class=\"kt\">int<\/span> <span class=\"n\">stopCount<\/span> <span class=\"o\">=<\/span> <span class=\"n\">stops<\/span><span class=\"p\">.<\/span><span class=\"n\">size<\/span><span class=\"p\">();<\/span> <span class=\"k\">const<\/span> <span class=\"n\">VGradientStop<\/span> <span class=\"o\">*<\/span><span class=\"n\">curr<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">next<\/span><span class=\"p\">,<\/span> <span class=\"o\">*<\/span><span class=\"n\">start<\/span><span class=\"p\">;<\/span> <span class=\"kt\">uint32_t<\/span> <span class=\"n\">curColor<\/span><span class=\"p\">,<\/span> <span class=\"n\">nextColor<\/span><span class=\"p\">;<\/span> <span class=\"kt\">float<\/span> <span class=\"n\">delta<\/span><span class=\"p\">,<\/span> <span class=\"n\">t<\/span><span class=\"p\">,<\/span> <span class=\"n\">incr<\/span><span class=\"p\">,<\/span> <span class=\"n\">fpos<\/span><span class=\"p\">;<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">vCompare<\/span><span class=\"p\">(<\/span><span class=\"n\">opacity<\/span><span class=\"p\">,<\/span> <span class=\"mf\">1.0f<\/span><span class=\"p\">))<\/span> <span class=\"n\">alpha<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">true<\/span><span class=\"p\">;<\/span> <span class=\"n\">start<\/span> <span class=\"o\">=<\/span> <span class=\"n\">stops<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"p\">();<\/span> <span class=\"n\">curr<\/span> <span class=\"o\">=<\/span> <span class=\"n\">start<\/span><span class=\"p\">;<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"n\">curr<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">second<\/span><span class=\"p\">.<\/span><span class=\"n\">isOpaque<\/span><span class=\"p\">())<\/span> <span class=\"n\">alpha<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">true<\/span><span class=\"p\">;<\/span> <span class=\"n\">curColor<\/span> <span class=\"o\">=<\/span> <span class=\"n\">curr<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">second<\/span><span class=\"p\">.<\/span><span class=\"n\">premulARGB<\/span><span class=\"p\">(<\/span><span class=\"n\">opacity<\/span><span class=\"p\">);<\/span> <span class=\"c1\">\/\/ out-of-bounds value, curr-&gt;second is controlled\n<\/span><span class=\"c1\"><\/span> <span class=\"n\">incr<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.0<\/span> <span class=\"o\">\/<\/span> <span class=\"p\">(<\/span><span class=\"kt\">float<\/span><span class=\"p\">)<\/span><span class=\"n\">size<\/span><span class=\"p\">;<\/span> <span class=\"c1\">\/\/ static\n<\/span><span class=\"c1\"><\/span> <span class=\"n\">fpos<\/span> <span class=\"o\">=<\/span> <span class=\"mf\">1.5<\/span> <span class=\"o\">*<\/span> <span class=\"n\">incr<\/span><span class=\"p\">;<\/span> <span class=\"c1\">\/\/ static\n<\/span><span class=\"c1\"><\/span> <span class=\"n\">colorTable<\/span><span class=\"p\">[<\/span><span class=\"n\">pos<\/span><span class=\"o\">++<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">curColor<\/span><span class=\"p\">;<\/span> <span class=\"k\">while<\/span> <span class=\"p\">(<\/span><span class=\"n\">fpos<\/span> <span class=\"o\">&lt;=<\/span> <span class=\"n\">curr<\/span><span class=\"o\">-&gt;<\/span><span class=\"n\">first<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span> <span class=\"c1\">\/\/ curr-&gt;first is controlled and pos is not checked to be &lt; size, leading to <\/span><span class=\"c1\"><\/span> <span class=\"n\">colorTable<\/span><span class=\"p\">[<\/span><span class=\"n\">pos<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"n\">colorTable<\/span><span class=\"p\">[<\/span><span class=\"n\">pos<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">];<\/span> <span class=\"c1\">\/\/ out-of-bounds write\n<\/span><span class=\"c1\"><\/span> <span class=\"n\">pos<\/span><span class=\"o\">++<\/span><span class=\"p\">;<\/span> <span class=\"n\">fpos<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">incr<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span> <span class=\"p\">[...]<\/span>\n<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<\/div>\n<\/div>\n<p>As we can see in the snippet, <code>pos<\/code> is not checked against <code>size<\/code> (the <code>colorTable<\/code> array size), leading to writing out-of-bounds 4 bytes after the end of the <code>colorTable<\/code> array allocated in heap memory.<\/p>\n<p>Specifically, while <code>fpos<\/code>, <code>size<\/code> and <code>incr<\/code> are static, <code>curr-&gt;first<\/code> and <code>curr-&gt;second<\/code> come directly from the animated sticker but <code>colorTable<\/code> is an uint32_t array of static size 1024, hence it is possible to overwrite an arbitrary amount of heap memory after it by carefully using a float number as <code>curr-&gt;first<\/code> in the animated sticker file.<\/p>\n<p>The written bytes are controlled via the sticker file too, but constrained to ARGB encoding performed in <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/blob\/release-6.1.1_1946\/TMessagesProj\/jni\/rlottie\/src\/vector\/vglobal.h#L292\" target=\"_blank\" rel=\"noopener noreferrer\">premulARGB()<\/a> and <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/blob\/release-6.1.1_1946\/TMessagesProj\/jni\/rlottie\/src\/lottie\/lottiemodel.h#L99\" target=\"_blank\" rel=\"noopener noreferrer\">getColorReplacement()<\/a>.<\/p>\n<p>While it\u2019s probably only useful in 32bit environments, coupled with an additional ASLR-bypass gadget it might lead to remote code execution. That being said, during my research I couldn\u2019t find <a href=\"https:\/\/saelo.github.io\/presentations\/36c3_messenger_hacking.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">memory-probing oracles<\/a> or remote infoleaks to overcome this protection so I didn\u2019t investigate further.<\/p>\n<p>The advisories for my other issues are available at shielder.it\/advisories!<\/p>\n<h3 id=\"structure-aware-fuzzing\">Structure-aware fuzzing<\/h3>\n<p>While analyzing the coverage traces I noticed that most of the mutated testcases were breaking the JSON syntax or messing up the few required JSON keys, reaching very shallow code. But in those same days I learnt about <a href=\"https:\/\/github.com\/google\/fuzzing\/blob\/master\/docs\/structure-aware-fuzzing.md\" target=\"_blank\" rel=\"noopener noreferrer\">structure-aware fuzzing<\/a>, which looked like what I was after: since rlottie parses structured data (JSONs), i needed some way to mutate the animations without breaking its syntax; also, I wasn\u2019t much interested in fuzzing the JSON decoding because it was handled by <a href=\"https:\/\/github.com\/Tencent\/rapidjson\" target=\"_blank\" rel=\"noopener noreferrer\">rapidjson<\/a> inside rlottie itself. While the <code>-x<\/code> dictionary flag in AFL++ improved the situation, it didn\u2019t instruct the fuzzer how to add or remove meaningful elements to the animation.<\/p>\n<p>Let\u2019s have a little introduction on structure- \/ grammar-aware fuzzing for who\u2019s not familiar with it (feel free to skip this paragraph if you do!). From the <a href=\"https:\/\/github.com\/google\/fuzzing\/blob\/master\/docs\/structure-aware-fuzzing.md\" target=\"_blank\" rel=\"noopener noreferrer\">structure-aware fuzzing wiki<\/a> I linked earlier:<\/p>\n<blockquote readability=\"12\">\n<p>Coverage-guided mutation-based fuzzers, such as libFuzzer or AFL, are not restricted to a single input type and do not require grammar definitions. Thus, mutation-based fuzzers are generally easier to set up and use than their generation-based counterparts. But the lack of an input grammar can also result in inefficient fuzzing for complicated input types, where any traditional mutation (e.g. bit flipping) leads to an invalid input rejected by the target API in the early stage of parsing.<\/p>\n<\/blockquote>\n<p>As an example let\u2019s imagine we feed to AFL++ a corpus made of JSONs and point it against the harness we have seen earlier, what testcases would it produce? \u2026 \ud83e\udd41 \u2026 <strong>Mostly broken JSONs<\/strong>. \ud83d\ude41 This is because by applying \u201cstandard mutations\u201d (e.g. bit flipping) it might mutate a char responsible for the JSON structure, breaking its syntax. This will lead to shallow code coverage, because the parser will exit once it detects the JSON is malformed, and to a lot of wasted executions, because they couldn\u2019t advance the coverage. But if we instead create a grammar definition about how are lottie animations actually structured, we\u2019d be able to have more control about the testcase mutations. This is where <a href=\"https:\/\/developers.google.com\/protocol-buffers\" target=\"_blank\" rel=\"noopener noreferrer\">protobuf<\/a> and <a href=\"https:\/\/github.com\/google\/libprotobuf-mutator\" target=\"_blank\" rel=\"noopener noreferrer\">libprotobuf-mutator<\/a> come in the picture: by creating a grammar definition in the protobuf syntax and using libprotobuf-mutator to instruct the fuzzer how to mutate a protobuf message, we can produce <strong>always syntactically valid testcases<\/strong> (i.e. in this case valid JSONs) to feed the target harness.<\/p>\n<p>Let\u2019s see an example protobuf message I have written for the main structure by reading the source code and <a href=\"https:\/\/mattbas.gitlab.io\/python-lottie\/group__Lottie.html#lottie_Animation\" target=\"_blank\" rel=\"noopener noreferrer\">mattbas\u2019s python-lottie project documentation<\/a>:<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.shielder.it\/img\/blog\/rlottie-protobuf-preview.png\"><\/figure>\n<p>Writing the rlottie protobuf grammar to use as an intermediate format turned out to be particularly time consuming: while the library code was easily readable, it required some tricky design decisions (<a href=\"https:\/\/www.crankuptheamps.com\/blog\/posts\/2017\/10\/12\/protobuf-battle-of-the-syntaxes\/\" target=\"_blank\" rel=\"noopener noreferrer\">proto2 or proto3?<\/a> multiple types with repeated keys or minimal type + add-ons? etc\u2026) not trivial as setting up the coverage-guided harness, leading to a <strong>~1k LOC harness<\/strong>. Moreover (probably because of that monster harness) the fuzzer was way slower than \u201csimple\u201d coverage-guided benchmarks (x4 slowdown on the same hardware).<\/p>\n<p>To sum up, the structure-aware fuzzer turned out to be faster than the \u201csimple\u201d coverage-guided strategy in finding the same bugs, but required a bigger time investment upfront just to start it, so I\u2019m happy for the knowledge I have acquired but I\u2019d probably recommend and use it against more complex codebases than rlottie, e.g. <a href=\"http:\/\/www.powerofcommunity.net\/poc2018\/ned.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">browser\u2019s IPC<\/a>. \ud83d\ude42<\/p>\n<h2 id=\"telegrams-animated-stickers-attack-surface\">Telegram\u2019s animated stickers attack surface<\/h2>\n<p>So how are animated stickers implemented? They are basically files uploaded to Telegram\u2019s cloud drive and referenced in messages by setting the <code>application\/x-tgsticker<\/code> mime type and attaching the cloud coordinates. A curious limitation I noticed is that in unencrypted chats (the default mode for chats, i.e. not \u201csecret chats\u201d) during my testing I couldn\u2019t receive the malicious sticker to my other testing accounts; this got me wondering whether Telegram servers were doing any kind of parsing\/filtering of the stickers I uploaded, but that\u2019s hard to tell since <strong>Telegram\u2019s server-side code is not open-source<\/strong> (yet?). This also limited the potential impact since only secret chats were usable to send an arbitrary animated sticker, probably because the file uploads are E2E encrypted too.<\/p>\n<p>Another interesting thing I noticed about secret chats is that, besides the macOS client, it\u2019s not possible to configure the client to prevent secret chats from being automatically accepted on that device. This allowed me to automatically start a secret chat and send animated stickers to anyone via <a href=\"https:\/\/frida.re\/\" target=\"_blank\" rel=\"noopener noreferrer\">Frida<\/a> (thanks <a href=\"https:\/\/twitter.com\/Th3Zer0\" target=\"_blank\" rel=\"noopener noreferrer\">@thezero<\/a> for the help with the JavaScript code!), until after my reports Telegram introduced the <a href=\"https:\/\/telegram.org\/blog\/profile-videos-people-nearby-and-more#filter-new-chats-from-non-contacts\" target=\"_blank\" rel=\"noopener noreferrer\">\u201cFilter New Chats from Non-Contacts\u201d setting<\/a> (which is still non-default so probably not enabled by everyone).<\/p>\n<p><del>Un<\/del>Fortunately the animated stickers are parsed and rendered only when the chat is opened, making these vulnerabilities reachable only if the chat is opened by clicking on it. Furthermore, since the animated sticker is downloaded on the device, everytime the chat is opened the issue triggers; this turned useless memory corruptions (such as null-pointer dereferences) into an annoyingly persistent crash which would have prevented non-technical victims from accessing the previous messages in the chat. (Tech-savvy people could have extracted them from the local Telegram\u2019s database, or used another client altogether.)<\/p>\n<h3 id=\"how-they-patched-it\">How they patched it<\/h3>\n<p>After my reports, Telegram introduced an interesting way to prevent such attack surface from being available remotely in a single click, without breaking the end-to-end encryption altogether: each and every animated sticker received in a secret chat (remember that malicious stickers in normal chats are filtered) are verified to be actually part of a sticker set (or \u201csticker pack\u201d, i.e. a collection of stickers of a specific theme\/topic). This probably comes from my own proof-of-concepts where I faked sticker sets references, but at the end of the day it successfully prevents malicious stickers from being decoded on the victim device since during the creation of a sticker set every sticker is parsed (yes, I guess the issues I have found could have been used against Telegram servers themselves in the creation of a sticker pack, but again since the server-side code is not open-source that\u2019s just a guess \ud83e\udd37\ud83c\udffb\u200d\u2642\ufe0f).<\/p>\n<p>We can see an example implementation of these new checks in <a href=\"https:\/\/github.com\/DrKLO\/Telegram\/blob\/release-7.1.0_2092\/TMessagesProj\/src\/main\/java\/org\/telegram\/messenger\/MediaDataController.java#L1247-L1257\" target=\"_blank\" rel=\"noopener noreferrer\">verifyAnimatedStickerMessage<\/a>, part of Telegram\u2019s Android source code:<\/p>\n<div class=\"highlight\">\n<div class=\"chroma\">\n<table class=\"lntable\" readability=\"4\">\n<tr readability=\"12\">\n<td class=\"lntd\" readability=\"5\">\n<pre class=\"chroma\"><code><span class=\"lnt\"> 1\n<\/span><span class=\"lnt\"> 2\n<\/span><span class=\"lnt\"> 3\n<\/span><span class=\"lnt\"> 4\n<\/span><span class=\"lnt\"> 5\n<\/span><span class=\"lnt\"> 6\n<\/span><span class=\"lnt\"> 7\n<\/span><span class=\"lnt\"> 8\n<\/span><span class=\"lnt\"> 9\n<\/span><span class=\"lnt\">10\n<\/span><span class=\"lnt\">11\n<\/span><span class=\"lnt\">12\n<\/span><span class=\"lnt\">13\n<\/span><span class=\"lnt\">14\n<\/span><span class=\"lnt\">15\n<\/span><span class=\"lnt\">16\n<\/span><\/code><\/pre>\n<\/td>\n<td class=\"lntd\" readability=\"9\">\n<pre class=\"chroma\"><code class=\"language-java\" data-lang=\"java\"><span class=\"n\">TLRPC<\/span><span class=\"o\">.<\/span><span class=\"na\">Document<\/span> <span class=\"n\">document<\/span> <span class=\"o\">=<\/span> <span class=\"n\">MessageObject<\/span><span class=\"o\">.<\/span><span class=\"na\">getDocument<\/span><span class=\"o\">(<\/span><span class=\"n\">message<\/span><span class=\"o\">);<\/span>\n<span class=\"n\">String<\/span> <span class=\"n\">name<\/span> <span class=\"o\">=<\/span> <span class=\"n\">MessageObject<\/span><span class=\"o\">.<\/span><span class=\"na\">getStickerSetName<\/span><span class=\"o\">(<\/span><span class=\"n\">document<\/span><span class=\"o\">);<\/span>\n<span class=\"k\">if<\/span> <span class=\"o\">(<\/span><span class=\"n\">TextUtils<\/span><span class=\"o\">.<\/span><span class=\"na\">isEmpty<\/span><span class=\"o\">(<\/span><span class=\"n\">name<\/span><span class=\"o\">))<\/span> <span class=\"o\">{<\/span> <span class=\"k\">return<\/span><span class=\"o\">;<\/span>\n<span class=\"o\">}<\/span>\n<span class=\"n\">TLRPC<\/span><span class=\"o\">.<\/span><span class=\"na\">TL_messages_stickerSet<\/span> <span class=\"n\">stickerSet<\/span> <span class=\"o\">=<\/span> <span class=\"n\">stickerSetsByName<\/span><span class=\"o\">.<\/span><span class=\"na\">get<\/span><span class=\"o\">(<\/span><span class=\"n\">name<\/span><span class=\"o\">);<\/span>\n<span class=\"k\">if<\/span> <span class=\"o\">(<\/span><span class=\"n\">stickerSet<\/span> <span class=\"o\">!=<\/span> <span class=\"kc\">null<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span> <span class=\"k\">for<\/span> <span class=\"o\">(<\/span><span class=\"kt\">int<\/span> <span class=\"n\">a<\/span> <span class=\"o\">=<\/span> <span class=\"n\">0<\/span><span class=\"o\">,<\/span> <span class=\"n\">N<\/span> <span class=\"o\">=<\/span> <span class=\"n\">stickerSet<\/span><span class=\"o\">.<\/span><span class=\"na\">documents<\/span><span class=\"o\">.<\/span><span class=\"na\">size<\/span><span class=\"o\">();<\/span> <span class=\"n\">a<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">N<\/span><span class=\"o\">;<\/span> <span class=\"n\">a<\/span><span class=\"o\">++)<\/span> <span class=\"o\">{<\/span> <span class=\"n\">TLRPC<\/span><span class=\"o\">.<\/span><span class=\"na\">Document<\/span> <span class=\"n\">sticker<\/span> <span class=\"o\">=<\/span> <span class=\"n\">stickerSet<\/span><span class=\"o\">.<\/span><span class=\"na\">documents<\/span><span class=\"o\">.<\/span><span class=\"na\">get<\/span><span class=\"o\">(<\/span><span class=\"n\">a<\/span><span class=\"o\">);<\/span> <span class=\"k\">if<\/span> <span class=\"o\">(<\/span><span class=\"n\">sticker<\/span><span class=\"o\">.<\/span><span class=\"na\">id<\/span> <span class=\"o\">==<\/span> <span class=\"n\">document<\/span><span class=\"o\">.<\/span><span class=\"na\">id<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"n\">sticker<\/span><span class=\"o\">.<\/span><span class=\"na\">dc_id<\/span> <span class=\"o\">==<\/span> <span class=\"n\">document<\/span><span class=\"o\">.<\/span><span class=\"na\">dc_id<\/span><span class=\"o\">)<\/span> <span class=\"o\">{<\/span> <span class=\"n\">message<\/span><span class=\"o\">.<\/span><span class=\"na\">stickerVerified<\/span> <span class=\"o\">=<\/span> <span class=\"n\">1<\/span><span class=\"o\">;<\/span> <span class=\"k\">break<\/span><span class=\"o\">;<\/span> <span class=\"o\">}<\/span> <span class=\"o\">}<\/span> <span class=\"k\">return<\/span><span class=\"o\">;<\/span>\n<span class=\"o\">}<\/span>\n<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<\/div>\n<\/div>\n<p><code>sticker.id == document.id<\/code> verifies that the unique Telegram cloud file identifier (used to reference also stickers, even in secret chats) equals the identifier of a sticker in a public sticker set, while <code>sticker.dc_id == document.dc_id<\/code> verifies that the datacenter identifiers match (I\u2019m not 100% sure this was necessary). This way a potential attacker not only needs to find additional issues in the rlottie forks, but also a bypass for these new authenticity checks.<\/p>\n<h2 id=\"conclusions\">Conclusions<\/h2>\n<p>Before starting this research in 2019 I would have been pretty skeptical if you had asked me whether the following year I\u2019d find a single memory corruption in Telegram. Today I shared with you the story of how I have found 13, some with a higher impact than others but all which were promptly fixed by Telegram for all the device families supporting secret chats: Android, iOS and macOS. This research helped me understand <a href=\"https:\/\/googleprojectzero.blogspot.com\/2021\/01\/a-look-at-imessage-in-ios-14.html\" target=\"_blank\" rel=\"noopener noreferrer\">once more<\/a> that <strong>it\u2019s not trivial to limit attack surfaces at scale in end-to-end encrypted contexts<\/strong> without losing functionalities. I hope that this blogpost inspired you in learning more about fuzzing and information security in general. If you have any comment or tip for improvement it would be greatly appreciated: you can reach me at <a href=\"https:\/\/twitter.com\/polict_\" target=\"_blank\" rel=\"noopener noreferrer\">@polict_<\/a> \u2013 until next time! \ud83d\udc4b\ud83c\udffb<\/p>\n<p><strong>Psst, do you feel these issues could have been found in your app? \ud83d\ude33 <a href=\"https:\/\/www.shielder.it\/contacts\">Let\u2019s arrange a security assessment<\/a>!<\/strong><\/p>\n<p>READ MORE <a href=\"https:\/\/packetstormsecurity.com\/news\/view\/32045\/Hunting-For-Bugs-In-Telegrams-Animated-Stickers-Remote-Attack-Surface.html\">HERE<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>READ MORE HERE&#8230;<\/p>\n","protected":false},"author":2,"featured_media":39676,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[60],"tags":[256],"class_list":["post-39675","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-packet-storm","tag-headlinehackerflaw"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Hunting For Bugs In Telegram&#039;s Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News<\/title>\n<meta name=\"description\" content=\"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security &amp; Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Hunting For Bugs In Telegram&#039;s Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News\" \/>\n<meta property=\"og:description\" content=\"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security &amp; Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/\" \/>\n<meta property=\"og:site_name\" content=\"ThreatsHub Cybersecurity News\" \/>\n<meta property=\"article:published_time\" content=\"2021-02-22T16:03:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png\" \/>\n\t<meta property=\"og:image:width\" content=\"903\" \/>\n\t<meta property=\"og:image:height\" content=\"347\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"TH Author\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@threatshub\" \/>\n<meta name=\"twitter:site\" content=\"@threatshub\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"TH Author\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/\"},\"author\":{\"name\":\"TH Author\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#\\\/schema\\\/person\\\/12e0a8671ff89a863584f193e7062476\"},\"headline\":\"Hunting For Bugs In Telegram&#8217;s Animated Stickers Remote Attack Surface\",\"datePublished\":\"2021-02-22T16:03:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/\"},\"wordCount\":2888,\"publisher\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png\",\"keywords\":[\"headline,hacker,flaw\"],\"articleSection\":[\"Packet Storm\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/\",\"url\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/\",\"name\":\"Hunting For Bugs In Telegram's Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png\",\"datePublished\":\"2021-02-22T16:03:22+00:00\",\"description\":\"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security & Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png\",\"contentUrl\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2021\\\/02\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png\",\"width\":903,\"height\":347},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"headline,hacker,flaw\",\"item\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/tag\\\/headlinehackerflaw\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Hunting For Bugs In Telegram&#8217;s Animated Stickers Remote Attack Surface\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/\",\"name\":\"ThreatsHub Cybersecurity News\",\"description\":\"%%focuskw%% Threat Intel \u2013 Threat Intel Services \u2013 CyberIntelligence \u2013 Cyber Threat Intelligence - Threat Intelligence Feeds - Threat Intelligence Reports - CyberSecurity Report \u2013 Cyber Security PDF \u2013 Cybersecurity Trends - Cloud Sandbox \u2013- Threat IntelligencePortal \u2013 Incident Response \u2013 Threat Hunting \u2013 IOC - Yara - Security Operations Center \u2013 SecurityOperation Center \u2013 Security SOC \u2013 SOC Services - Advanced Threat - Threat Detection - TargetedAttack \u2013 APT \u2013 Anti-APT \u2013 Advanced Protection \u2013 Cyber Security Services \u2013 Cybersecurity Services -Threat Intelligence Platform\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#organization\"},\"alternateName\":\"Threatshub.org\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#organization\",\"name\":\"ThreatsHub.org\",\"alternateName\":\"Threatshub.org\",\"url\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/Threatshub_Favicon1.jpg\",\"contentUrl\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/Threatshub_Favicon1.jpg\",\"width\":432,\"height\":435,\"caption\":\"ThreatsHub.org\"},\"image\":{\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/threatshub\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.threatshub.org\\\/blog\\\/#\\\/schema\\\/person\\\/12e0a8671ff89a863584f193e7062476\",\"name\":\"TH Author\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g\",\"caption\":\"TH Author\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Hunting For Bugs In Telegram's Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News","description":"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security & Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/","og_locale":"en_US","og_type":"article","og_title":"Hunting For Bugs In Telegram's Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News","og_description":"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security & Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.","og_url":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/","og_site_name":"ThreatsHub Cybersecurity News","article_published_time":"2021-02-22T16:03:22+00:00","og_image":[{"width":903,"height":347,"url":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png","type":"image\/png"}],"author":"TH Author","twitter_card":"summary_large_image","twitter_creator":"@threatshub","twitter_site":"@threatshub","twitter_misc":{"Written by":"TH Author","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#article","isPartOf":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/"},"author":{"name":"TH Author","@id":"https:\/\/www.threatshub.org\/blog\/#\/schema\/person\/12e0a8671ff89a863584f193e7062476"},"headline":"Hunting For Bugs In Telegram&#8217;s Animated Stickers Remote Attack Surface","datePublished":"2021-02-22T16:03:22+00:00","mainEntityOfPage":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/"},"wordCount":2888,"publisher":{"@id":"https:\/\/www.threatshub.org\/blog\/#organization"},"image":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#primaryimage"},"thumbnailUrl":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png","keywords":["headline,hacker,flaw"],"articleSection":["Packet Storm"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/","url":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/","name":"Hunting For Bugs In Telegram's Animated Stickers Remote Attack Surface 2026 | ThreatsHub Cybersecurity News","isPartOf":{"@id":"https:\/\/www.threatshub.org\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#primaryimage"},"image":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#primaryimage"},"thumbnailUrl":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png","datePublished":"2021-02-22T16:03:22+00:00","description":"ThreatsHub Cybersecurity News | ThreatsHub.org | Cloud Security & Cyber Threats Analysis Hub. 100% Free OSINT Threat Intelligent and Cybersecurity News.","breadcrumb":{"@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#primaryimage","url":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png","contentUrl":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2021\/02\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface.png","width":903,"height":347},{"@type":"BreadcrumbList","@id":"https:\/\/www.threatshub.org\/blog\/hunting-for-bugs-in-telegrams-animated-stickers-remote-attack-surface\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.threatshub.org\/blog\/"},{"@type":"ListItem","position":2,"name":"headline,hacker,flaw","item":"https:\/\/www.threatshub.org\/blog\/tag\/headlinehackerflaw\/"},{"@type":"ListItem","position":3,"name":"Hunting For Bugs In Telegram&#8217;s Animated Stickers Remote Attack Surface"}]},{"@type":"WebSite","@id":"https:\/\/www.threatshub.org\/blog\/#website","url":"https:\/\/www.threatshub.org\/blog\/","name":"ThreatsHub Cybersecurity News","description":"%%focuskw%% Threat Intel \u2013 Threat Intel Services \u2013 CyberIntelligence \u2013 Cyber Threat Intelligence - Threat Intelligence Feeds - Threat Intelligence Reports - CyberSecurity Report \u2013 Cyber Security PDF \u2013 Cybersecurity Trends - Cloud Sandbox \u2013- Threat IntelligencePortal \u2013 Incident Response \u2013 Threat Hunting \u2013 IOC - Yara - Security Operations Center \u2013 SecurityOperation Center \u2013 Security SOC \u2013 SOC Services - Advanced Threat - Threat Detection - TargetedAttack \u2013 APT \u2013 Anti-APT \u2013 Advanced Protection \u2013 Cyber Security Services \u2013 Cybersecurity Services -Threat Intelligence Platform","publisher":{"@id":"https:\/\/www.threatshub.org\/blog\/#organization"},"alternateName":"Threatshub.org","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.threatshub.org\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.threatshub.org\/blog\/#organization","name":"ThreatsHub.org","alternateName":"Threatshub.org","url":"https:\/\/www.threatshub.org\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.threatshub.org\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2025\/05\/Threatshub_Favicon1.jpg","contentUrl":"https:\/\/www.threatshub.org\/blog\/coredata\/uploads\/2025\/05\/Threatshub_Favicon1.jpg","width":432,"height":435,"caption":"ThreatsHub.org"},"image":{"@id":"https:\/\/www.threatshub.org\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/threatshub"]},{"@type":"Person","@id":"https:\/\/www.threatshub.org\/blog\/#\/schema\/person\/12e0a8671ff89a863584f193e7062476","name":"TH Author","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/066276f086d5155df79c850206a779ad368418a844da0182ce43f9cd5b506c3d?s=96&d=mm&r=g","caption":"TH Author"}}]}},"_links":{"self":[{"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/posts\/39675","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/comments?post=39675"}],"version-history":[{"count":0,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/posts\/39675\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/media\/39676"}],"wp:attachment":[{"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/media?parent=39675"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/categories?post=39675"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.threatshub.org\/blog\/wp-json\/wp\/v2\/tags?post=39675"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}