Task 6a requires you to
- spoof a message and
- prevent spoofed user from reading replies.
When sending a message, TerrorTime will encrypt the message content with a symmetric key. This key is then encrypted using the sender’s local public keys and the receiver’s public keys. It’s interesting that only the local public keys are used, and not all of the ones listed on the sender’s VCard. The app makes the assumption that if the key is on the user’s VCard, then it is also in the user’s local copy. This prevents the message key from being encrypted with the spoof user’s public key. I could send a message from a spoofed user and verify that it was using my key. When the spoofed user logged in, TerrorTime would not be able to decrypt the message and would ignore the message.
Below is the format of the message body.
{ "messageKey": { "<base64 encoded public key fingerprint>" : "<base64 encoded public key>", "<base64 encoded public key fingerprint>" : "<base64 encoded public key fingerprint>" }, "messageSig" : "<base64 encoded message signature>" , "message" : { "msg" : "<base64 encoded encrypted message>", "iv" : "<base64 encoded iv>" } }
Unfortunately, the recipient was not able to read the message either. I needed to look deeper into how the message is decrypted.
In the function Messaging.decryptMessage, after the message is decrypted an internal structure is checked.
Below is the JSON of the internal structure.
{ "<client username>": ["<base64 encoded public key fingerprint>"] , "<contact username>": ["<base64 encoded public key fingerprint>"] , "body": "<Message between users>" }
Example
{ "kingsley--vhost-174@terrortime.app": ["Icaj54y\/0uKYGaOA1dn9bO9whI0F8pvwJiX0K4efK2k=","EWiwDLmEZnoMQsivUpWpmoo55z1VEbQVHXkLD9msnW0="] , "arianna--vhost-174@terrortime.app": ["\/f58KjUf4i+3lquXE6eMhJNVxNswU72bgKK6spBF1hw=","Icaj54y\/0uKYGaOA1dn9bO9whI0F8pvwJiX0K4efK2k=","EWiwDLmEZnoMQsivUpWpmoo55z1VEbQVHXkLD9msnW0="] , "body": "This is my test for future messages" }
Each user’s public key fingerprints in the internal structure are checked to determine if the the message key was encrypted with a corresponding public key. If the internal structure contains a fingerprint that was not used, then TerrorTime treats the message as corrupt and drops it. Interestingly, the app does not check if the public key belongs to the sender or the receiver. Additionally, the message key can be encrypted with a public key that is not recorded in the internal structure. This is why I could send messages without error; even though they could not be read.
Below is the JavaScript I injected into TerrorTime to manipulate the message’s internal structure to fix the corrupt issue. The script hooks the Messaging.encryptMessage function and changes the arguments sent to the real function. The client’s public key fingerprints list is replaced with the contact’s. Now when the contact receives the message, the internal structure only contains fingerprints for their own public keys.
Removing the spoofed user’s public key from their VCard is another method to achieve the same result.
Preventing Spoofed User From Reading Replies
The second part of the task can be completed two different ways; only one is checked for by the scoring system. The first method is to add the recipient to the spoofed user’s block list. This is not a function provided by the TerrorTime app, but is a function of the XMPP server. The second method is to remove the spoofed user’s public key from their VCard. Their public key is added back to the server when the user next logs in, which prevents stopping communication permanently.
In my opinion, adding the user to a block list is a better method, since it
- has a more grainier focus,
- enables spoofing while the user is logged in, and
- is less detectable.
Removing the public key has the following downsides,
- blocks all contacts,
- you are unable to control when spoofed user logs back in, and
- is more detectable.
Below is the JavaScript I injected to add user to a block list. I had some trouble accessing the Enum PrivacyItem.Type from the JavaScript. This forced me to hook the toXML function to set the privacyItem values. When a stanza is sent to the XMPP server, the send function will call the function toXML of the object representing the stanza. By hooking this function, I was able to format the XML correctly to block a user.
Below is the XML representing a PrivacyItem.
<item action="deny" type="jid" value="zachary--vhost-174@terrortime.app" order="0"> <message/> </item>
Below are pictures of the initial and final states of testing the method, along with a gif showing the message being blocked. The Powershell window is running an interactive Python session I used to interactive with Frida. Below are the steps I took:
- first sent a message to ensure connectivity,
- added Zachary to Brian’s block list,
- sent a second message which was blocked,
- added Zachary to Brian’s allow list, and
- sent a final message that was received.
Full video can be found here.
Final state
Removing the public key from VCard
I used the VCardHelper.savePublicKey function as a template to write JavaScript to replace the spoofed user’s public key from their VCard with my own. The below JavaScript will monitor TerrorTime for instances of the class com.badguy.terrortime.TerrorTimeApplication. Once found, the javascript
- retrieves the current instances of VCardManager and XMPPTCPConnection,
- requests the current user’s VCard,
- sets the “DESC” to my public key,
- saves the modified VCard.
Now when a user replies to the spoofed user, the message key is not encrypted with their public key; preventing decryption. Removing the spoofed user’s public key from their VCard fixes both problems, prevents the public key fingerprint from being in the messages internal structure and prevents the spoofed user from reading replies.