Mission 5

Enoncé

Brief de mission

Lors d’une arrestation au domicile d’un des attaquants précédemment identifiés, l’équipe a saisi une vielle tablette Google utilisée pour leurs communications. Durant son analyse, une application de chat semble être chiffrée, rendant impossible l’accès et la découverte de son contenu.

Objectifs de la mission

Analyser l’application mobile. Accéder aux informations chiffrées.

Exploit

On va décompiler l’application avec mobSF

On trouve rapidement des informations intéressantes :

@GET("messages")
Object getMessages(@Query("id") String str, Continuation<? super Response<ApiResponse>> continuation);

Si on se rend sur la route http://163.172.67.201:8000/messages?id=1 on obtient ceci :

A priori les messages à déchiffrer :

{
"messages": [
    {
    "content": "M2geCVKOzPlyug9p9DvthxPip0oe9BPiT2sDfFhWy7iC3+JQI4SfO7+SLAlFSUmu8LoGj1hrUWil/uNXvc+5mKBMrRNFQT8ijBK14P0Z8qA=",
    "isEncrypted": true,
    "sender": "Agent-02",
    "timestamp": "2025-04-01 08:00:00"
    },
    {
    "content": "//5PBsYWhHlgqhVgG1omUyevzmlErLZVsTCLO78Rbb9qBMPnsKCS5/RZ4GEdWRBPiZ4BtO5h7j2PuIutfqf7ag==",
    "isEncrypted": true,
    "sender": "Agent-1337",
    "timestamp": "2025-04-01 10:00:00"
    },
    {
    "content": "2uNMSnJZa5JExhYgNA+V3RAiafhuLkj8Jnr4U+lSZOrrpMWjyA13w0Do3IIPcVBgK070rmweRKX/GkCAxat4i3JfWk1UvWNSmEZbHQlFznR7VFW6FKK84iJKhiDOp8Tk",
    "isEncrypted": true,
    "sender": "Agent-01",
    "timestamp": "2025-04-02 15:30:00"
    },
    {
    "content": "Swz/ycaTlv3JM9iKJHaY+f1SRyKvfQ5miG6I0/tUb8bvbOO+wyU5hi+bGsmcJD3141FrmrDcBQhtWpYimospymABi3bzvPPi01rPI8pNBq8=",
    "isEncrypted": true,
    "sender": "Agent-02",
    "timestamp": "2025-04-03 13:20:00"
    },
    {
    "content": "NAe44oieygG7xzLQT3j0vN+0NoPNUu0TAaid9Az3IlpcKwR0lSKaPT8F4y1zpbArWFIGpgzsPZtPAwL50qocTRMG/g5u+/wcc1nxmhBjCbg=",
    "isEncrypted": true,
    "sender": "Agent-04",
    "timestamp": "2025-04-04 08:30:00"
    },
    {
    "content": "dfeKlZP/gIntHySBYine2YUlNiX3LjlMOLu7y9tgprFyJIIcQpfghlQXut6cJUG2wtzGBVQUm7ITdpLNeVaZjamQHhPWEtNIJE/xtFg66Klui1qCKYKSrmZ4wm1CG/ZPy4csqbM28Ur8dts7XoV5FA==",
    "isEncrypted": true,
    "sender": "Agent-04",
    "timestamp": "2025-04-05 16:45:00"
    },
    {
    "content": "HgVONjPe9UpULkg8e9Ps5++m3t4r6RK0pPfMUQJK/Ok5NinC3UJqkltlEukrvehfyas/uOQygGwMFYdRWT6m4gQBq/TdHf9Xpf4kLJl+o9l2shuwBGFpayRLMkRZ0yX1",
    "isEncrypted": true,
    "sender": "Agent-03",
    "timestamp": "2025-04-06 11:15:00"
    },
    {
    "content": "M+bWr4Az5IbaOw4oGRKZ0pWrGjK7hhLeXVpOiaOAFw91v8KGwhl5b6bXoEl3qqz5APuiy9gLQp7x7GGDod4mLOBWRby48g2RjABqGa6mg3g=",
    "isEncrypted": true,
    "sender": "Agent-01",
    "timestamp": "2025-04-06 14:20:00"
    },
    {
    "content": "Z56plvPjUwczUuTZajCJNqvxB0ArZ9vk38PJnGHSlayR6xe9la8wB4sMOdChcOozbXsjwMRY78wuogxsY57R3iiKe1O3nDpwt3y85BXl9sLEF15wZ8wxc5IfpjVUnRJT",
    "isEncrypted": true,
  "sender": "Agent-00",
  "timestamp": "2025-04-07 09:00:00"
        }
    ]
}

Mais coment faire ?

On trouve également dans le code :

  • Un SALT : private static final String STATIC_SALT = « s3cr3t_s@lt »;

  • Un IV : private static final String STATIC_IV = « LJo+0sanl6E3cvCHCRwyIg== »;

La fonction de création du hashDeviceID :

public final String hashDeviceId(String model, String brand) {
        String str = model + ':' + brand;
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        Charset UTF_8 = StandardCharsets.UTF_8;
        Intrinsics.checkNotNullExpressionValue(UTF_8, "UTF_8");
        byte[] bytes = str.getBytes(UTF_8);
        Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
        String encodeToString = Base64.encodeToString(messageDigest.digest(bytes), 2);
        Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)");
        return encodeToString;
    }

Et la fonction de déchiffrement des messages :

public final String decryptMessage(String encryptedMessage) {
    try {
        String MODEL = Build.MODEL;
        Intrinsics.checkNotNullExpressionValue(MODEL, "MODEL");
        String BRAND = Build.BRAND;
        Intrinsics.checkNotNullExpressionValue(BRAND, "BRAND");
        byte[] deriveKey = deriveKey(hashDeviceId(MODEL, BRAND), STATIC_SALT);
        byte[] decode = Base64.decode(STATIC_IV, 0);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(2, new SecretKeySpec(deriveKey, "AES"), new IvParameterSpec(decode));
        byte[] doFinal = cipher.doFinal(Base64.decode(encryptedMessage, 0));
        Intrinsics.checkNotNull(doFinal);
        Charset UTF_8 = StandardCharsets.UTF_8;
        Intrinsics.checkNotNullExpressionValue(UTF_8, "UTF_8");
        return new String(doFinal, UTF_8);
    } catch (Exception e) {
        Log.e("DECRYPT_ERROR", "Error decrypting message", e);
        return "[Encrypted] This message was encrypted with old device credentials";
    }
}

On va récupérer la liste des devices google : https://storage.googleapis.com/play_public/supported_devices.html

  • Google;guybrush

  • Google;skyrim

  • Google;zork

  • Google;grunt

  • Google;kalista

  • Google;Acer Chromebook 14 (CB3-431)

  • Google;Chromebook 14 (CB3-431)

  • Google;Google Chromebook Pixel (2015)

  • Google;Chromebox Reference

  • Google;fizz

  • Google;puff

  • Google;Chromecast

  • Google;Chromecast HD

  • Google;AOSP on IA Emulator

  • Google;Android SDK built for x86

  • Google;Android SDK built for x86_64

  • Google;GT-I9505G

  • Google;sdk_gphone_x86

  • Google;Android SDK built for x86_64

  • Google;Car on x86_64 emulator

  • Google;HPE device

  • Google;Google TV Streamer

  • Google;brya

  • Google;brask

  • Google;nissa

  • Google;rammus

  • Google;coral

  • Google;ASUS Chromebook C213NA

  • Google;Intel Apollo Lake Chromebook

  • Google;reef

  • Google;Braswell Chrome OS Device

  • Google;Intel Braswell Chromebook

  • Google;Intel Braswell Chromebook

  • Google;hatch

  • Google;octopus

  • Google;dedede

  • Google;keeby

  • Google;nami

  • Google;rex

  • Google;volteer

  • Google;jacuzzi

  • Google;kukui

  • Google;corsola

  • Google;staryu

  • Google;geralt

  • Google;asurada

  • Google;cherry

  • Google;Lenovo N23 Yoga/Flex 11 Chromebook

  • Google;Mediatek MT8173 Chromebook

  • Google;Mediatek MTK8173 Chromebook

  • Google;Pixel

  • Google;Pixel 2

  • Google;Pixel 2 XL

  • Google;Pixel 3

  • Google;Pixel 3 XL

  • Google;Pixel 3a

  • Google;Pixel 3a XL

  • Google;Pixel 4

  • Google;Pixel 4 XL

  • Google;Pixel 4a

  • Google;Pixel 4a (5G)

  • Google;Pixel 5

  • Google;Pixel 5a

  • Google;Pixel 6

  • Google;Pixel 6 Pro

  • Google;Pixel 6a

  • Google;Pixel 7

  • Google;Pixel 7 Pro

  • Google;Pixel 7a

  • Google;Pixel 8

  • Google;Pixel 8 Pro

  • Google;Pixel 8a

  • Google;Pixel 9

  • Google;Pixel 9 Pro

  • Google;Pixel 9 Pro Fold

  • Google;Pixel 9 Pro XL

  • Google;Pixel 9a

  • Google;Pixel C

  • Google;Pixel Fold

  • Google;Google Pixel Slate

  • Google;Pixel Slate

  • Google;nocturne

  • Google;Pixel Tablet

  • Google;Google Pixel Watch

  • Google;Google Pixel Watch

  • Google;Google Pixel Watch 2

  • Google;Google Pixel Watch 2

  • Google;Pixel XL

  • Google;Pixel Watch 3

  • Google;Pixel Watch 3

  • Google;Pixel Watch 3

  • Google;Pixel Watch 3

  • Google;Google Pixelbook Go

  • Google;atlas

  • Google;Google Pixelbook

  • Google;Project Tango Tablet Development Kit

  • Google;Yellowstone

  • Google;trogdor

  • Google;trogdor64

  • Google;strongbad

  • Google;RK3288 Chrome OS Device

  • Google;Rockchip RK3288 Chromebook

  • Google;RK3288 Chrome OS Device

  • Google;Rockchip RK3288 Chromebook

  • Google;sdk_gphone64_x86_64

  • Google;Automotive SDK built for x86_64

  • Google;AOSP on x86

  • Google;Automotive SDK built for x86

On fait un script python qui permet de détecter le bon couple Model / Brand : Yellowstone/Google

import base64
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# === Constantes ===
STATIC_SALT = b's3cr3t_s@lt'  # Sel statique
STATIC_IV = base64.b64decode("LJo+0sanl6E3cvCHCRwyIg==")  # IV statique

def hash_device_id(model: str, brand: str) -> str:
    """Hachage SHA-256 du modèle et de la marque, puis encodage en Base64."""
    # Concatenation du modèle et de la marque
    device_id = f"{model}:{brand}"

    # Hachage SHA-256
    sha256_hash = hashlib.sha256(device_id.encode('utf-8')).digest()

    # Encodage du hash en Base64
    return base64.b64encode(sha256_hash).decode('utf-8')

def derive_key(device_id_b64: str, salt: bytes) -> bytes:
    """Dérive la clé en concaténant le device_id en Base64 et le sel, puis en hachant le tout avec SHA-256."""
    # Concaténation du device_id en Base64 et du sel (en tant que chaînes de caractères)
    str_to_hash = f"{device_id_b64}:{salt.decode('utf-8')}"

    # Hachage SHA-256 pour dériver la clé
    return hashlib.sha256(str_to_hash.encode('utf-8')).digest()

def decrypt_message(encrypted_message: str, model: str, brand: str) -> str:
    """Déchiffre le message en utilisant AES CBC et la clé dérivées."""
    try:
        # Génération de la clé à partir du modèle et de la marque
        device_hash_b64 = hash_device_id(model, brand)
        derived_key = derive_key(device_hash_b64, STATIC_SALT)

        # Décodage du message chiffré
        encrypted_bytes = base64.b64decode(encrypted_message)

        # Décryptage avec AES CBC
        cipher = AES.new(derived_key, AES.MODE_CBC, STATIC_IV)
        decrypted = cipher.decrypt(encrypted_bytes)

        # Suppression du padding
        plaintext = unpad(decrypted, AES.block_size).decode('utf-8')

        return plaintext

    except Exception as e:
        # Gestion des erreurs
        print(f"❌ Erreur de déchiffrement pour {model}/{brand} : {e}")
        return "[Encrypted] Ce message a été chiffré avec de anciennes informations d'appareil."

def process_devices(file_path: str, encrypted_message: str):
    """Processus principal pour parcourir les modèles et marques et déchiffrer les messages."""
    with open(file_path, 'r') as file:
        for line in file:
            brand, model = line.strip().split(';')
            print(f"\n🔍 Décryptage pour {model}/{brand}")
            decrypted_message = decrypt_message(encrypted_message, model, brand)
            print(f"Message déchiffré pour {model}/{brand}: {decrypted_message}")

# === Point d’entrée ===
if __name__ == "__main__":
    # Exemple de message chiffré (à remplacer par le message réel)
    encrypted_message = "M2geCVKOzPlyug9p9DvthxPip0oe9BPiT2sDfFhWy7iC3+JQI4SfO7+SLAlFSUmu8LoGj1hrUWil/uNXvc+5mKBMrRNFQT8ijBK14P0Z8qA="

    # Chemin vers le fichier devices.txt
    devices_file = "devices.txt"

    # Traitement des appareils
    process_devices(devices_file, encrypted_message)

On peut maintenant déchiffrer le reste des messages avec ce script :

import base64
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# === Constantes ===
STATIC_SALT = b's3cr3t_s@lt'  # Sel statique
STATIC_IV = base64.b64decode("LJo+0sanl6E3cvCHCRwyIg==")  # IV statique

def hash_device_id(model: str, brand: str) -> str:
    """Hachage SHA-256 du modèle et de la marque, puis encodage en Base64."""
    # Concatenation du modèle et de la marque
    device_id = f"{model}:{brand}"

    # Hachage SHA-256
    sha256_hash = hashlib.sha256(device_id.encode('utf-8')).digest()

    # Encodage du hash en Base64
    return base64.b64encode(sha256_hash).decode('utf-8')

def derive_key(device_id_b64: str, salt: bytes) -> bytes:
    """Dérive la clé en concaténant le device_id en Base64 et le sel, puis en hachant le tout avec SHA-256."""
    # Concaténation du device_id en Base64 et du sel (en tant que chaînes de caractères)
    str_to_hash = f"{device_id_b64}:{salt.decode('utf-8')}"

    # Hachage SHA-256 pour dériver la clé
    return hashlib.sha256(str_to_hash.encode('utf-8')).digest()

def decrypt_message(encrypted_message: str, model: str, brand: str) -> str:
    """Déchiffre le message en utilisant AES CBC et la clé dérivées."""
    try:
        # Génération de la clé à partir du modèle et de la marque
        device_hash_b64 = hash_device_id(model, brand)
        derived_key = derive_key(device_hash_b64, STATIC_SALT)

        # Décodage du message chiffré
        encrypted_bytes = base64.b64decode(encrypted_message)

        # Décryptage avec AES CBC
        cipher = AES.new(derived_key, AES.MODE_CBC, STATIC_IV)
        decrypted = cipher.decrypt(encrypted_bytes)

        # Suppression du padding
        plaintext = unpad(decrypted, AES.block_size).decode('utf-8')

        return plaintext

    except Exception as e:
        # Gestion des erreurs
        print(f"❌ Erreur de déchiffrement : {e}")
        return "[Encrypted] Ce message a été chiffré avec de anciennes informations d'appareil."

def decrypt_all_messages(messages, model, brand):
    """Déchiffre tous les messages dans la liste."""
    decrypted_messages = []
    for message in messages:
        if message['isEncrypted']:
            decrypted_text = decrypt_message(message['content'], model, brand)
            decrypted_messages.append({
                "sender": message['sender'],
                "timestamp": message['timestamp'],
                "decrypted_content": decrypted_text
            })
        else:
            decrypted_messages.append(message)  # Message non chiffré
    return decrypted_messages

# === Point d’entrée ===
if __name__ == "__main__":
    # Messages chiffrés à déchiffrer (les données JSON fournies)
    encrypted_messages = [
        {"content": "M2geCVKOzPlyug9p9DvthxPip0oe9BPiT2sDfFhWy7iC3+JQI4SfO7+SLAlFSUmu8LoGj1hrUWil/uNXvc+5mKBMrRNFQT8ijBK14P0Z8qA=", "isEncrypted": True, "sender": "Agent-02", "timestamp": "2025-04-01 08:00:00"},
        {"content": "//5PBsYWhHlgqhVgG1omUyevzmlErLZVsTCLO78Rbb9qBMPnsKCS5/RZ4GEdWRBPiZ4BtO5h7j2PuIutfqf7ag==", "isEncrypted": True, "sender": "Agent-1337", "timestamp": "2025-04-01 10:00:00"},
        {"content": "2uNMSnJZa5JExhYgNA+V3RAiafhuLkj8Jnr4U+lSZOrrpMWjyA13w0Do3IIPcVBgK070rmweRKX/GkCAxat4i3JfWk1UvWNSmEZbHQlFznR7VFW6FKK84iJKhiDOp8Tk", "isEncrypted": True, "sender": "Agent-01", "timestamp": "2025-04-02 15:30:00"},
        {"content": "Swz/ycaTlv3JM9iKJHaY+f1SRyKvfQ5miG6I0/tUb8bvbOO+wyU5hi+bGsmcJD3141FrmrDcBQhtWpYimospymABi3bzvPPi01rPI8pNBq8=", "isEncrypted": True, "sender": "Agent-02", "timestamp": "2025-04-03 13:20:00"},
        {"content": "NAe44oieygG7xzLQT3j0vN+0NoPNUu0TAaid9Az3IlpcKwR0lSKaPT8F4y1zpbArWFIGpgzsPZtPAwL50qocTRMG/g5u+/wcc1nxmhBjCbg=", "isEncrypted": True, "sender": "Agent-04", "timestamp": "2025-04-04 08:30:00"},
        {"content": "dfeKlZP/gIntHySBYine2YUlNiX3LjlMOLu7y9tgprFyJIIcQpfghlQXut6cJUG2wtzGBVQUm7ITdpLNeVaZjamQHhPWEtNIJE/xtFg66Klui1qCKYKSrmZ4wm1CG/ZPy4csqbM28Ur8dts7XoV5FA==", "isEncrypted": True, "sender": "Agent-04", "timestamp": "2025-04-05 16:45:00"},
        {"content": "HgVONjPe9UpULkg8e9Ps5++m3t4r6RK0pPfMUQJK/Ok5NinC3UJqkltlEukrvehfyas/uOQygGwMFYdRWT6m4gQBq/TdHf9Xpf4kLJl+o9l2shuwBGFpayRLMkRZ0yX1", "isEncrypted": True, "sender": "Agent-03", "timestamp": "2025-04-06 11:15:00"},
        {"content": "M+bWr4Az5IbaOw4oGRKZ0pWrGjK7hhLeXVpOiaOAFw91v8KGwhl5b6bXoEl3qqz5APuiy9gLQp7x7GGDod4mLOBWRby48g2RjABqGa6mg3g=", "isEncrypted": True, "sender": "Agent-01", "timestamp": "2025-04-06 14:20:00"},
        {"content": "Z56plvPjUwczUuTZajCJNqvxB0ArZ9vk38PJnGHSlayR6xe9la8wB4sMOdChcOozbXsjwMRY78wuogxsY57R3iiKe1O3nDpwt3y85BXl9sLEF15wZ8wxc5IfpjVUnRJT", "isEncrypted": True, "sender": "Agent-00", "timestamp": "2025-04-07 09:00:00"}
    ]

    # Déchiffrement de tous les messages
    decrypted_messages = decrypt_all_messages(encrypted_messages, "Yellowstone", "Google")

    # Affichage des messages déchiffrés
    for msg in decrypted_messages:
        print(f"Sender: {msg['sender']} | Timestamp: {msg['timestamp']} | Decrypted Content: {msg['decrypted_content']}")

Résultalt :

Sender: Agent-02 | Timestamp: 2025-04-01 08:00:00 | Decrypted Content: Target acquired. Hospital network vulnerable. Initiating ransomware deployment.
Sender: Agent-1337 | Timestamp: 2025-04-01 10:00:00 | Decrypted Content: Keep this safe. RM{788e6f3e63e945c2a0f506da448e0244ac94f7c4}
Sender: Agent-01 | Timestamp: 2025-04-02 15:30:00 | Decrypted Content: New target identified. School district network. Estimated payout: 500k in crypto.
Sender: Agent-02 | Timestamp: 2025-04-03 13:20:00 | Decrypted Content: New ransomware strain ready for deployment. Testing phase complete.
Sender: Agent-04 | Timestamp: 2025-04-04 08:30:00 | Decrypted Content: Security patch released. Need to modify attack vector. Meeting at usual place.
Sender: Agent-04 | Timestamp: 2025-04-05 16:45:00 | Decrypted Content: New zero-day exploit in a linux binary discovered. Perfect for next operation. Details incoming.
Sender: Agent-03 | Timestamp: 2025-04-06 11:15:00 | Decrypted Content: [Encrypted] Ce message a été chiffré avec de anciennes informations d'appareil.
Sender: Agent-01 | Timestamp: 2025-04-06 14:20:00 | Decrypted Content: [Encrypted] Ce message a été chiffré avec de anciennes informations d'appareil.
Sender: Agent-00 | Timestamp: 2025-04-07 09:00:00 | Decrypted Content: [Encrypted] Ce message a été chiffré avec de anciennes informations d'appareil.