A Covert Channel Over the Telegram
Why would anyone trust their messages to the third-party anyway?
Join the DZone community and get the full member experience.Join For Free
We used to think of Telegram as a reliable and secure transmission medium for messages of any sort. But under the hood, it has a rather common combination of a- and symmetric encryptions. Where’s fun in that? And why would anyone trust their messages to the third-party anyway?
There are many workarounds to transmit data between two users avoiding direct contact. You can use middlemen, crypto-, and steganography methods, broadcasting relay networks, and other extensions of existing protocols. But sometimes, it’s useful being able to establish secure contact using only officially documented features. Or, as one should say, set up a covert channel.
We can see an example of it in a Soviet spy movie “Seventeen Moments of Spring” (this one is, like, really good, give it a chance). In it, a flower in a window of the safe house was used to signal if the spy had failed his mission or not. The flower by itself does not mean anything: It can be there and could be not, such symbiosis is a common thing and only telling us about the owner’s love for flowers. Only a predetermined interpretation distinguishes the information received by a spy from the one received by a random passerby.
In-Window, Flower-Based Channels in Telegram
To organize your own covert channel with the same principle, you’ll need only two things: a window and a flower. The window represents an object you can change the state of seen by others and the flower — possible states and a way of changing them.
So what Alice could change in Telegram that Bob can see? Many things, actually: avatars, usernames, last visited time, and more. But usually, these things are available to everyone at the same time, limiting dialog privacy — if one possesses the transition method, he could read anything Alice sends. Surprisingly, it is possible to get around this limitation without any kind of encryption involved.
I’m Blocking You, HA!
Every user has its own blacklist, and if the reader was annoying enough, they should have noticed after being blocked that their not-already-a-friend ‘last visited’ status changed to ‘last seen a long time ago’. The truth is that they could have been online just a few seconds ago or even be right now, but the Telegram API will not send this information to your app anymore. That way, it is protecting other user’s privacy from unwanted ones. In exchange, they can see if they are blacklisted or not.
Bit exchange algorithm on every clock looks something like this:
- A checks sending a bit and if has different from the previous value changing it depending on a value: - A -> T: block B if bit is 1; - A -> T: unblock B if bit is 0. - B receives a bit: - B -> T: resolve A; - T -> B: available to B information about A; - B: checks if the received information has a status it: - B: if it is -> he is not blocked and the bit is 0 - B: if it is not -> he is blocked and the bit is 1
The possibility to send and receive bits is fun and all, but we still need to describe its exploitation mechanism. Telegram refuses to notice you when blocked, so every ‘receive bit’ action should be initialized by the recipient (let’s call him Bob) and not depend on the sender (and she will be Alice), i. e. by independent. It also follows that Alice and Bob should do requests at the same frequency.
Texts in natural languages have pretty high redundancy, and messages received with errors will still be mostly readable by a human. And since Telegram is a messenger (ignoring some crazy stuff), we can neglect error correction limiting possible transmitting data to simple text messages.
Our channel has extremely low bandwidth, so why we need to use the most effective message encoding available for possible messages. Lucky us, the name of the messenger is reminding about times such problem was a common one.
That’s why we, living in the 21st century, will encode our texts with one of the most efficient available to telegraphers a hundred years ago encodings — the Baudot code. More precisely, its final variation ITA-2 created by Donald Murray to perform fewer API calls at the most frequent symbols of the language.
The only left to successfully transmit a message is to find boundaries of a transmission session so the recipient could find a sent one among the continuous bit stream. Before the transmission has started, Bob is either blocked or not, and his state is not changing by itself anytime soon. That’s why Alice can signal about session start by swapping it to an opposite for only one clock. At the successful end of the session, she will unblock him and leave with peace. He, on the other side, will continue to receive zero bits until decides they are not a part of the message — the Baudot code has no `00000` symbol.
Drawbacks of the method are a practical impossibility to connect (you can, but it will likely require manual error correction due to the bit shift) to ongoing translation and a need to separate null symbols received with errors from ones been sent. But there all problems of implementation.
After several hours spent trying to use an official library to use the API, I got tired and wrote everything with a Python using more human-friendly Telethon library. It even has a synchronous-style API for some odd reasons rare today. Message encoding with ITA-2 I wrote by myself since I found nothing useful on the Internet.
Clock synchronization made with system clocks (and yes, it sleep()s! in between) since it is precise enough considering the time required on every network API call is more than a tenth of a second in most cases. User can set transmission speed to what they want, but I recommend following ‘no more than a request per second’ rule if you don’t want to both see errors on the other side and find yourself banned by a flood prevention system. Telegram turned out to be very picky about API usage, freezing my access for a day from even a few simple (successful!) authorization attempts in a row and just random blocking for a flood during the transmission for an unknown reason. You should always declare your API usage limits, guys.
If the user decided to use such a weird channel to exchange messages, they should not care about any graphical user interface features. And not all systems have it anyway, that’s why I wrote my application in the form of terminal tool. It allows it to both send and receive messages via a channel by a given in command-line arguments user id, but only one operation per launch. Of course, no one will limit you to running only one copy of a program at ones and use multiple channels simultaneously in both ways, you’ll just need to run several copies of the same script with different parameters.
Using the Stuff
You can read more about using this thing as both command-line utility and a python3 library through the API at the GitHub (repository linked at the end of the post). The only problem is to acquire your own API credentials (simple manual is helpful enough) since Telegram does not allow to disclose mine and set according values in your local copy of a script. Everything passed through the command line arguments except for the authorization part (which by default made through the studio) and looks like this:
For Alice: | For Bob: Enter your phone number: XXX | Enter your phone number: XXX Enter auth code: YYY | Enter auth code: YYY Started message transmission... | Listening for the message... ---++ ('O', '9') | ---++ ('O', '9') --+-+ ('H', '#') | --+-+ ('H', '#') +++++ (1, 1) | +++++ (1, 1) --++- ('N', ',') | --++- ('N', ',') --+-- (' ', ' ') | --+-- (' ', ' ') ++-++ (0, 0) | ++-++ (0, 0) --+-+ ('H', '#') | --+-+ ('H', '#') -++-- ('I', '8') | -++-- ('I', '8') --+-- (' ', ' ') | --+-- (' ', ' ') --+++ ('M', '.') | --+++ ('M', '.') ++--- ('A', '-') | ++--- ('A', '-') -+-+- ('R', "'") | -+-+- ('R', "'") ++++- ('K', '(') | ++++- ('K', '(') +++++ (1, 1) | +++++ (1, 1) +-++- ('F', '!') | +-++- ('F', '!') --+++ ('M', '.') | --+++ ('M', '.') --+++ ('M', '.') | --+++ ('M', '.') Done, exiting... | ----- ('', '') | ----- ('', '') | Automatically decoded: OH, HI | MARK!..
Received message decoded automatically, but if you want to correct some errors manually or to track progress, you could do it looking at the command line output.
Outside of the Telegram
It's worth noticing that such a channel could be implemented over any messenger and/or social network in which one can detect if blocked or not by others. Similar interfaces can be easily replaced in the existing code, so if you want to do it, just use mine groundwork. Low python’s performance (compared to usual for such things C/++) will not be a limiting factor due to low transmission speed and API calls response time.
P.S. Special thanks to my passion’s unusual love for blocking me
You can find the source code on GitHub.
Opinions expressed by DZone contributors are their own.