Since Facebook announced WhatsApp purchase, their users have been looking for alternatives to this instant messaging application. An alternative application that has increased its popularity in the last few days (2 million new users), due to the WhatsApp recent blackout, is Telegram. Regarding its creators, the key points of Telegram's great success are that it will be free forever and their privacy and security. As a proof of its security, Telegram has offered a reward for anyone who could break their message encryption.
The main difference
between Telegram and WhatsApp is that Telegram allows their users to use
peer-to-peer encryption and message time expiration. Despite not being
activated by default, every Telegram user can use these security options when
needed.
However,
conversations are not only sent between mobile devices, they are stored in your
mobile devices as well. Today we are going to talk about how these
conversations are stored “at-rest” in your mobile device, even encrypted ones.
We are going to use an iPhone (iOS 7.04), but similar techniques could be used
in different platforms such as Android.
Our first approach
was to use an iTunes backup. When backing up a mobile device, all information
that would be needed to completely restore the device is stored, including all
kind of internal information such as WhatsApp or Telegram conversations. As
usually happen in mobile device applications, Telegram stores its internal
information in SQLite files. In this iTunes backup we find a file called
“tgdata_index.db” that contains interesting tables and information.
The most
interesting tables in this database are “messageIndex_v29” and
“messageIndex_v29_content”. Both of them store the conversation messages, but
we can’t identify whom is the user talking with. Analyzing in depth
“messageIndex_v29_content” table, we find that “docid” is some kind of message
id, but it works in two different ways: Sometimes it is sequential (1, 2, 3, …)
and sometimes looks random (-2147483635, 800000013, ...). Regarding our
research, this behavior change depending on the kind of communication
(encrypted or not).
As a result, anyone
that could backup your device or to get access to your backups, would be able
to read your messages, even encrypted ones.
Other interesting
approach would be to look into the mobile device itself. Using a jailbroken
device we copied the application’s directory for further analysis.
"malformed database schema (unread_by_cid_with_date) - near "WHERE": syntax error"
In order to access
to the information, it is necessary to use the sqlite3’s dump feature and to
create a new database file from its SQL sentences. We can now access to the
full content and rebuild completely the conversations, even identify the people
whom the user is talking with.
In addition, encrypted
conversation IDs are stored in the “encrypted_cids_v29” table, so you can use
this information together with the messages information in order to rebuilt
completely all the encrypted conversations.
Finally, we have
developed a python script that exports all the conversations to a human
readable text format from the original database file.
Given a Telegram
conversation such as the following one:
You can use our
tool in order to export it from the database files as follows:
As you can see,
self-destructed messages are not present in the database, but there is a
reference to them. You can know how many messages were sent, when they were
sent and whom they were sent. The only removed information is the message
itself, which is stored as “None”. Some other SQLite forensic techniques could
be used in order to recover these messages, but we will go in depth on this in following posts.
Concluding, It
seems that Telegram’s security is mainly focused on network communications, and
not in how these conversations are stored in-rest. Does it mean that Telegram
communications are completely secure? Well… just let us to go in depth with it
and stay tuned with us.
The used python
script is following:
1: #!/usr/bin/python
2: #coded by "Abel Gomez at INCIDE"
3: import codecs # -*- coding: utf-8 -*-
4: import unicodedata
5: import datetime
6: import os
7: import sqlite3 as lite
8: import subprocess
9: import sys
10: reload(sys)
11: sys.setdefaultencoding("utf-8")
12:
13: users = {}
14: con = None
15: directory = "messages"
16: try:
17: con = lite.connect('./Documents/tgdata.db')
18: except Exception:
19: print "\nEs necesario ejecutar el archivo desde el directorio raiz de Telegram\n"
20: sys.exit()
21: if not os.path.exists(directory):
22: #if the directory does not exists we create a directory to store the messages extracted
23: os.makedirs(directory)
24:
25: # the file that contains the db is corrupted, so we make a dump of the DB and create a new one.
26: if os.path.exists('dump.sql'):
27: os.remove('dump.sql')
28: os.remove('dump.db')
29: f = os.system('sqlite3 Documents/tgdata.db ".dump" |grep -v "CREATE INDEX" >dump.sql; sqlite3 dump.db ".read dump.sql"')
30:
31: con = lite.connect('dump.db')
32: cur = con.cursor()
33: cur.execute("SELECT uid,first_name,last_name FROM users_v29")
34: for i in cur.fetchall():
35: #user list
36: users[i[0]] = "%s %s" % (i[1],i[2])
37:
38: cur.execute("SELECT cid,date,from_uid,message FROM convesations_v29")
39: #at this point we have the conversation list
40: convs = cur.fetchall()
41: sep = "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"
42: for i in convs:
43: #for each conversation, we test if the conversation is encrypted and extract all the conversations
44: conv = i[0]
45: partner = users[i[2]]
46: fd = open(directory+'/conversation_'+str(conv)+'.txt' ,'w');
47: fd.write("\nConversation %s with %s :\n\n%s"% (str(conv),partner,sep))
48: cur.execute("SELECT 'True' FROM encrypted_cids_v29 WHERE cid='%s'" % (str(conv)))
49: cypher = cur.fetchone()
50: if cypher is not None:
51: fd.write("This conversation is encrypted\n\n%s" % (sep))
52: cur.execute("SELECT from_id,to_id,date,message,mid FROM messages_v29 WHERE cid = %s ORDER BY date,mid" % (str(conv)))
53: for men in cur.fetchall():
54: missage = men[3]
55: user_from= users[men[0]]
56: if men[1] == conv: user_to = partner
57: else: user_to = users[men[1]]
58: if men[3] == None: #could be an image or a deleted message
59: cur.execute("SELECT 'True' FROM media_v29 WHERE mid = %s" % (str(men[4])))
60: if cur.fetchone() is not None: missage = "+-+-+SHARED FILE+-+-+"
61: date = datetime.datetime.fromtimestamp(men[2])
62:
63: fd.write("%s ---> %s (%s): %s \n%s\n" % (user_from,user_to,date,missage,sep))
64:
65: print "Ha finalizado el proceso de export, puede consultar los mensajes en './messages'\n"
You can read more of his posts in zprian.blogspot.com.es
No hay comentarios:
Publicar un comentario