lunes, 3 de marzo de 2014

About the myth of Telegram's messaging encryption



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.
In this directory we find a new “tgdata.db” file that we haven’t seen in our backup. However, the most widely used SQLite tools fail to open this file and show the following error message: 
"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"  


Abel Gómez is Senior Pentester at INCIDE

You can read more of his posts in zprian.blogspot.com.es

No hay comentarios:

Publicar un comentario