Telegram bug bounties: XSS, privacy issues, official bot exploitation and more…

Davide
10 min readApr 21, 2021

--

Summary

  • Insufficient verification over callback_data parameter (May 2018)
  • XSS Telegram.org (December 2018)
  • Privacy of Profile Pictures (March 2019)
  • Sticker crash (July-December 2019)
  • Permissions issues on bugs.telegram.org (December 2020)

Intro

In Italy we have pizza, pasta and people looking for bugs. Today I want to talk about some bugs we’ve found through the years in Telegram.

Insufficient verification over callback_data parameter

Introduction

For this vulnerability I collaborated with Andrea and together we created a program that used an MTProto (the protocol for communicating with Telegram APIs) client to be able to send and then test requests to the Telegram APIS. Also the bug was found while we were doing some tests together.

What is callback_data?

Telegram allows you to create Bots, which are nothing more than a real and normal Telegram account but with some restrictions. Yet they also have unique features available, such as being able to create a message with buttons.

There are 2 kinds of buttons on Telegram: KeyboardButton and InlineKeyboardButton.
The inline buttons are very interesting, because they allow you to have a user visible text that is different from the query that the Bot will receive; as an example I can show the user a nice button with an emoji and the bot will instead receive a better button name to parse such as btn1.
Here is an example of a JSON inline keyboard:

Understanding the Telegram API

In the BotAPI documentation the (callback) data parameter’s description is the following:
data String Optional. Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
data
is a parameter that corresponds to the callback_data shown above and it's received by the Bot when the button is pressed.

The problem is that the bot is the only one that can create the buttons that the user will be able to press, and in order to manipulate them it would be necessary for the user to have the API Token.
What i thought is that if the creators of the API decided to write down that warning, it would surely be possible to avoid using the Token in order to manipulate the buttons.
We later decided to use a library that support MTProto so that we could make calls to the APIs with a user account, and without being limited by the official client.

Using the Telegram API, (which are different from the BotAPI) we were able to create a fake button press action by invoking the messages.getBotCallbackAnswer method and we noticed that the data parameter could be filled with any string we wanted.

Bad implementation in Telegram Bots

Now you are probably wondering “what is the problem? the documentation clearly states to be careful of this condition”.
The problem is that many developers have not thought about it or have not read about this possibility and have simply implemented checks based on what their bot was sending.
Imagine a button that contains something like ban#1234567 inside the callback_data parameter.
What would happen is that as soon as someone sent the modified parameter to the bot, the user with the id 1234567 would be banned from the group/channel.
You now understand that if there are no data checks, any user could ban others users without permission, or even do a sql injection or even worse.
These I mentioned are not just examples, very similar things actually happened to third-party bots, and it was something pretty common.

There are two official Bots of Telegram’s created specifically to show the functionality of the Bot API: VoteBot and LikeBot.
Well, it turned out that even these bots didn’t have any proper data check on the callback_data parameter

Vote Bot

The bot is not used to ban, it is just text with votes

Using an MTProto library it is possible to receive the updates sent to the user so as to be able to understand what the callback_data of the button is.
The result is the following:

So we immediately tried to alter the values ​​and after a few attempts we discovered that by sending an id of another poll created through the same bot, it was possible to overwrite the title and the text of the votes with those of the other poll.
Accordingly it was possible to modify the messages of other people, or even the messages of the channels that used the bot, with a custom message.

Like Bot

This Bot has a very similar situation of the previous one.
This is the content of the buttons:

In this situation, however, the id had to be changed with the id of a voting with the same amount of buttons, moreover only the text of the buttons was changed and not the text of the message itself.

Conclusion

We reported the problem to the Telegram Team and it was acknowledged, they also fixed it, no longer allowing users to send arbitrary text via messages.getBotCallbackAnswer method.
Now if the data doesn't match the name of an existing button that method returns the error 400 DATA_INVALID.

XSS Telegram.org

Intro

Andrea and I had noticed that in the Telegram app it was possible to play videos directly from the site preview, and we decided to explore the t.me site, which allows you to have a widget with the preview of the posts of public channels and to insert a video player, as happened with YouTube.

Searching for bugs

We set out to create a site with a working video player, and this is the code we used:

index.php
player.php

Now that we have the site, we created a public channel and posted the link ( https://website/test/index.php) to generate the preview, and the site accepted the video player without any hassle.
Then we visited the t.me page that pointed to our post, and voilà: an alert box appeared.

Obviously the cookies of the original page could not be read because the player was inside an iframe.
However, it would have been possible to create alerts, perform Open Redirects, or anything else that could be executed by JavaScript.

This problem also occurred on telegram.org as the two sites were identical, and also on all sites that allowed Telegram widgets, such as Telegra.ph.

Report

We reported the problem to Telegram, which confirmed the problem, and solved it by allowing embedded videos only from trusted domains such as youtube.com, twitter.com, etc…

Privacy of Profile Pictures

Intro

As usual, every time the Beta versions of Telegram release a new update, I look them up.
One of these releases gives you the possibility to change the profile picture privacy settings, making it visible only to your contacts

Discovery

In my Telegram Bot, I use an endpoint to take the profile pictures https://t.me/i/userpic/320/USERNAME.jpg.
I found this endpoint in the Telegram documentations Login Widget.

Right off the bat, I noticed that through this endpoint, it was possible to view the profile pictures also of those users who have set their privacy settings to “only for contacts”.
Regardless, I chose to wait until the end of the Beta version to avoid giving a report about something that is still a work in progress, so not yet official.

Report

Nevertheless, after the official March release update I noticed that this issue was not put into consideration by the Telegram team.

So I reported the endpoint and they confirmed the issue.
Now that endpoint respects the user’s privacy settings and it’s not possible to view the profile picture anymore, unless they’re public.

Sticker crash

Intro

In the update of July 2019, Telegram introduced animated stickers.
Giuseppe and I tried to understand how they worked.

How the animated stickers works

According to the documentation, they use Lottie.
Looking at the app sources Giuseppe realized that it was a compressed JSON.
To compress and decompress an animated sticker, we simply used gzip.

Once the TGS file has been extracted, we will have a JSON containing all the vector info and the layers of the animation, as well as other essential info such as the frame-rate and resolution.

Fuzzing TGS

Now that we know how they work, Giuseppe started developing t.me/jsontgsbot, a bot that could speed up the testing process of the stickers.
It’s basically a JSON-sticker translator: feed it with the right JSON file, and it will produce an animated sticker; give it a sticker, and it will return you its equivalent in JSON.

We also tried to do some zip-bombs but to no avail; the size cannot exceed 64KB compressed; also the JSON must be valid for it to be accepted by the client.

So we tried to manipulate the JSON, to change frames, remove parameters, add them, duplicate them and generate invalid JSON in all the ways we could think of.
Initially we didn’t notice anything in particular, but after a few attempts we noticed that clients sometimes crashed.
For example, removing almost all the parameters such as the frame-rate and all the layers, the client for Android went into error and crashed.

The same happened in the desktop client if you set the frame-rate to 0, as the client tried to divide by 0.

While with these values the desktop client crashed with a SIGSEGV error.

Then the next day we found another crash after additional server-side controls had been put in place.

First report

I reported that the Android client and the desktop one crashed, the problem was confirmed, and it was fixed on both the client and server-side by blocking the upload of invalid stickers.
Invalid stickers now have mime_type application/x-bad-tgsticker instead of application/x-tgsticker and are simply displayed as files.

Crash again

A month later I was scrolling through an old chat where there were many stickers that I had tested and I noticed a crash.
This time, however, I was on another app, Telegram X (the other official client for Android).
It was a bit complicated to understand which sticker caused the crash among the many stickers, since, well, the app was constantly crashing.

But using the Desktop client I was able to locate the sticker and it was one of the same ones I reported some time ago.
I tried to reload the sticker and the server no longer accepted it.
But I had uploaded it before the server-side block was placed, so by forwarding it, it was still viewable as a sticker.
Also I asked a friend with iOS to check on his client, and it actually crashed there as well.

Second report

So with an old sticker, two official clients crashed, the problem was confirmed by Telegram, and it was solved by patching the clients.

Crash again x2

A few months later I revisited an old chat with stickers and… Again I noticed crashes with the following stickers

Android stock, Android tgx
Android stock, Android tgx
Android tgx
tgx crash log

Third report

For some reason the bug was reintroduced.
I again reported the crash to Telegram, which again confirmed the problem, and the clients were rightfully patched.

Permissions issues on bugs.telegram.org

Intro

Recently, Telegram has provisioned a platform for suggestions and for reporting bugs.
The user would write posts in it and there is a comment section below every post, wherein you can leave a like or subscribe to a discussion and receive notifications in the app.
At the time I performed the tests, it wasn’t possible to open posts yet; only comments were allowed.
It was also cited in the comments that in the future, it will be possible to open private posts to report security problems.

Testing and analysis

By exploring the requests made by the browser, I observed the endpoint and the parameters used by the site’s API, so to have an idea on how it works.
Furthermore, I looked into the IDs of the post https://bugs.telegram.org/c/123, and noticed that on some posts, instead of giving the usual error message Sorry, this card doesn't exist., they responded with You can not view this card.

So, I tried to write a comment to the post though the API, but the server blocked me saying that I wasn’t authorized.
Then I tried with leaving a like to a post to which I didn’t have authorization to, but this time the API let me through.
Okay, cool! I was able to put a like to a post I didn’t have access to, and through the API responses I was also able to see the number of likes and dislikes.
Not very useful per se, but it motivated me to search for more.

So as the next step, I tried to send a request to subscribe to a post (the hash value is used as if they were session cookies)

This time, the API’s response was positive; I was able subscribe to a post.

By doing this, I would’ve received notifications for future comments directly to the Telegram app.
Sadly I wasn’t able to verify, for I didn’t have the permission to comment.

By experimenting more thoroughly, I noticed that in posts with a lot of comments, there was an API request to load more comments.
So I tried the same thing: change the ID and load some comments

With this I was able to read comments of private posts, and I could’ve “potentially” leaked part of the security reports done through the platform.
But, since the platform was newly released, there were just comments written by the devs, so nothing important to leak.

Report

At this point, because of the high risk of information leakage, I reported the problem to Telegram, which confirmed the issue.
The bug was fixed during the winter holidays.
Now the website’s APIs do not let access without authorization anymore.

Note

Libraries for MTProto we used to test the Telegram API:

As for the documentation of the methods, I suggest you to use those of the various libraries because they are more up to date than the official ones.

Want to read about other bugs?

Go take a look at our new article:
Telegram bug bounties: RCE, privacy issues, and more…

Special thanks

Special thanks to the contributors who helped revision the article:

--

--