{"id":246,"date":"2024-12-30T12:59:58","date_gmt":"2024-12-30T12:59:58","guid":{"rendered":"https:\/\/tolva.fr\/?p=246"},"modified":"2024-12-31T10:02:14","modified_gmt":"2024-12-31T10:02:14","slug":"etude-dune-application-android-de-messagerie-instantanee","status":"publish","type":"post","link":"https:\/\/tolva.fr\/index.php\/2024\/12\/30\/etude-dune-application-android-de-messagerie-instantanee\/","title":{"rendered":"\u00c9tude d&rsquo;une application Android de messagerie instantan\u00e9e"},"content":{"rendered":"\n<p>\u00c0 cot\u00e9 des messageries ayant aujourd&rsquo;hui pignon sur rue comme WhatsApp, Signal, iMessage ou Telegram, on peut trouver sur Google Play tout un maquis d&rsquo;applications similaires mais plus confidentielles.<\/p>\n\n\n\n<p>J&rsquo;ai choisi de m&rsquo;int\u00e9resser \u00e0 une de ses applications, SeaTalk :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"648\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_1.png\" alt=\"\" class=\"wp-image-247\" style=\"width:471px;height:auto\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_1.png 540w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_1-250x300.png 250w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/><\/figure>\n\n\n\n<p>Au moment de la r\u00e9daction de cet article (fin 2024\/d\u00e9but 2025), l&rsquo;application a fait l&rsquo;objet de l&rsquo;ordre de 100000 t\u00e9l\u00e9chargements.<\/p>\n\n\n\n<p>L&rsquo;application fait l&rsquo;objet d&rsquo;un site internet (<a href=\"http:\/\/seatalk.io\" data-type=\"link\" data-id=\"seatalk.io\">seatalk.io<\/a>) qui donne acc\u00e8s \u00e0 diverses informations :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1313\" height=\"659\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/website_seatalk_1.png\" alt=\"\" class=\"wp-image-248\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/website_seatalk_1.png 1313w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/website_seatalk_1-300x151.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/website_seatalk_1-768x385.png 768w\" sizes=\"auto, (max-width: 1313px) 100vw, 1313px\" \/><\/figure>\n\n\n\n<p>SeaTalk semble destin\u00e9e en premier lieu \u00e0 des utilisateurs asiatiques, comme le sugg\u00e8re la liste des langues support\u00e9es :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1156\" height=\"340\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/langages_seatalk.png\" alt=\"\" class=\"wp-image-249\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/langages_seatalk.png 1156w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/langages_seatalk-300x88.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/langages_seatalk-768x226.png 768w\" sizes=\"auto, (max-width: 1156px) 100vw, 1156px\" \/><\/figure>\n\n\n\n<p>Le m\u00eame site affirme qu&rsquo;aucun message n&rsquo;est stock\u00e9 sur le serveur, affirmation \u00e9videmment inv\u00e9rifiable (ce qui ne signifie pas qu&rsquo;elle est inexacte !) :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"619\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/security_claims_seatalk.png\" alt=\"\" class=\"wp-image-250\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/security_claims_seatalk.png 975w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/security_claims_seatalk-300x190.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/security_claims_seatalk-768x488.png 768w\" sizes=\"auto, (max-width: 975px) 100vw, 975px\" \/><\/figure>\n\n\n\n<p>La version test\u00e9e est la version 3.50.1 :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"220\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_2.png\" alt=\"\" class=\"wp-image-251\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_2.png 540w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/googleplay_seatalk_2-300x122.png 300w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Prise en main de l&rsquo;application<\/strong><\/h4>\n\n\n\n<p>L&rsquo;application s&rsquo;installe \u00e0 partir du playstore Google sans difficult\u00e9 notable.<\/p>\n\n\n\n<p>Utiliser l&rsquo;application n\u00e9cessite la cr\u00e9ation d&rsquo;un compte. Cette \u00e9tape pr\u00e9liminaire demande de renseigner une adresse email, choisir un mot de passe, une photo et un pseudonyme.<\/p>\n\n\n\n<p>SeaTalk peut \u00eatre utilis\u00e9e dans un contexte professionnel mais propose \u00e9galement les services basiques d&rsquo;une messagerie instantan\u00e9e : \u00e9change de messages texte et de fichiers, appels vocaux.<\/p>\n\n\n\n<p>C&rsquo;est \u00e0 ces fonctionnalit\u00e9s que nous allons nous int\u00e9resser.<\/p>\n\n\n\n<p>Afin de cr\u00e9er un environnement de test, Seatalk est install\u00e9e sur trois t\u00e9l\u00e9phones :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Installation sur Pixel 7a Android 14 root\u00e9, compte \u00ab\u00a0alice\u00a0\u00bb<\/li>\n\n\n\n<li>Installation sur XPeria Android 10 root\u00e9, compte \u00ab\u00a0bob\u00a0\u00bb<\/li>\n\n\n\n<li>Installation sur Samsung A20e Android 11 non-root\u00e9, compte \u00ab\u00a0ivan\u00a0\u00bb<\/li>\n<\/ul>\n\n\n\n<p>L&rsquo;\u00e9cran d&rsquo;accueil de l&rsquo;application montre la liste des conversations :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"405\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/contacts_seatalk.png\" alt=\"\" class=\"wp-image-252\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/contacts_seatalk.png 541w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/contacts_seatalk-300x225.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n\n<p>La croix bleue en haut \u00e0 droite de l&rsquo;application permet d&rsquo;ajouter un nouveau contact :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"543\" height=\"406\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/options_seatalk.png\" alt=\"\" class=\"wp-image-253\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/options_seatalk.png 543w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/options_seatalk-300x224.png 300w\" sizes=\"auto, (max-width: 543px) 100vw, 543px\" \/><\/figure>\n\n\n\n<p>Il existe diff\u00e9rentes fa\u00e7ons d&rsquo;ajouter un contact, en renseignant l&rsquo;adresse email du contact par exemple :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"538\" height=\"380\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/add_contacts_seatalk.png\" alt=\"\" class=\"wp-image-254\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/add_contacts_seatalk.png 538w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/add_contacts_seatalk-300x212.png 300w\" sizes=\"auto, (max-width: 538px) 100vw, 538px\" \/><\/figure>\n\n\n\n<p>Une demande est alors envoy\u00e9e au contact potentiel. S&rsquo;il accepte, il est ajout\u00e9 \u00e0 la liste des contacts.<\/p>\n\n\n\n<p>Un utilisateur peut acc\u00e9der \u00e0 son propre profil, o\u00f9 appara\u00eet son pseudonyme, sa photo ainsi qu&rsquo;un identifiant num\u00e9rique sur 10 chiffres, nomm\u00e9 SeaTalk ID :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"301\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/profile_seatalk.png\" alt=\"\" class=\"wp-image-255\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/profile_seatalk.png 540w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/profile_seatalk-300x167.png 300w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/><\/figure>\n\n\n\n<p>Au sein d&rsquo;un chat avec un contact<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"358\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/chat_seatalk.png\" alt=\"\" class=\"wp-image-256\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/chat_seatalk.png 541w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/chat_seatalk-300x199.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n\n<p>il est possible d&rsquo;\u00e9changer des messages texte, des fichiers :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"355\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/envoi_fichiers_seatalk.png\" alt=\"\" class=\"wp-image-257\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/envoi_fichiers_seatalk.png 540w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/envoi_fichiers_seatalk-300x197.png 300w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/><\/figure>\n\n\n\n<p>ou d&rsquo;autres actions (envoyer une photo, passer un appel vocal, partager sa localisation\u2026)<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"402\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/actions_seatalk.png\" alt=\"\" class=\"wp-image-258\" style=\"width:537px;height:auto\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/actions_seatalk.png 541w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/actions_seatalk-300x223.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Flux r\u00e9seau induits par l&rsquo;application<\/strong><\/h4>\n\n\n\n<p>Si l&rsquo;on capture le trafic du terminal lorsque SeaTalk est en cours d&rsquo;utilisation, on observe essentiellement du trafic TLS :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>$ tshark -r captures\/seatalk_capture_0.pcap -Y tls.handshake.type==1 -T fields -e tls.handshake.extensions_server_name | grep -vE '^$' | sort -u\napi.haiserve.com\nedge-co.haiserve.com\ngraph.facebook.com\nlamssettings-pa.googleapis.com\noa.haiserve.com\ns.haiserve.com<\/code><\/pre>\n\n\n\n<p>Un appel vocal donne de plus lieu \u00e0 du trafic TLS avec un serveur ayant pour nom de domaine <code>call.haiserve.com<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>$ tshark -r captures\/seatalk_audiocall_0.pcap -Y tls.handshake.type==1 -T fields -e tls.handshake.extensions_server_name | grep -vE '^$' | sort -u\ncall.haiserve.com\ns.haiserve.com<\/code><\/pre>\n\n\n\n<p>Ainsi qu&rsquo;\u00e0 du trafic udp :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"950\" height=\"806\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/audiocall_udp_traffic_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-259\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/audiocall_udp_traffic_seatalk.png 950w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/audiocall_udp_traffic_seatalk-300x255.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/audiocall_udp_traffic_seatalk-768x652.png 768w\" sizes=\"auto, (max-width: 950px) 100vw, 950px\" \/><\/figure>\n\n\n\n<p>Certains de ces serveurs sont cod\u00e9s en dur dans <code>com.seagroup.seatalk.libenv.servers.STServers<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1383\" height=\"592\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/hardcoded_servers_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-260\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/hardcoded_servers_seatalk.png 1383w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/hardcoded_servers_seatalk-300x128.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/hardcoded_servers_seatalk-768x329.png 768w\" sizes=\"auto, (max-width: 1383px) 100vw, 1383px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Interception du trafic<\/strong><\/h4>\n\n\n\n<p>Hormis le trafic vocal auquel nous ne nous int\u00e9resserons pas aujourd&rsquo;hui, tout est transport\u00e9 par TLS (port 443) vers diff\u00e9rents serveurs <code>*.haiserve.com<\/code>.<\/p>\n\n\n\n<p>Si l&rsquo;on tente d&rsquo;intercepter le trafic avec HttpToolkit, on constate que le trafic vers certains serveurs n&rsquo;est pas correctement g\u00e9r\u00e9 par HttpToolkit :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1830\" height=\"759\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/httpToolkit_errors_seatalk.png\" alt=\"\" class=\"wp-image-261\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/httpToolkit_errors_seatalk.png 1830w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/httpToolkit_errors_seatalk-300x124.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/httpToolkit_errors_seatalk-768x319.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/httpToolkit_errors_seatalk-1536x637.png 1536w\" sizes=\"auto, (max-width: 1830px) 100vw, 1830px\" \/><\/figure>\n\n\n\n<p>Comme HttpToolkit ne fonctionne pas correctement ici, on interceptera le trafic avec Burp (en suivant l&rsquo;excellent tutoriel <a href=\"https:\/\/knifecoat.com\/Posts\/Installing+Burp+Suite+CA+on+Android+14\">https:\/\/knifecoat.com\/Posts\/Installing+Burp+Suite+CA+on+Android+14<\/a>).<\/p>\n\n\n\n<p>Comme une partie du trafic ne passe pas au travers du proxy (notamment les messages textes), on utilisera \u00e9galement d&rsquo;autres m\u00e9thodes d&rsquo;interception du trafic :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>D&rsquo;une part, comme vu auparavant (https:\/\/tolva.fr\/index.php\/2024\/08\/26\/dechiffrer-le-trafic-tls-dune-application-android-avec-frida\/), on utilisera un script frida permettant de provoquer la g\u00e9n\u00e9ration d&rsquo;un fichier <code>SSLKEYLOGFILE<\/code> ;<\/li>\n\n\n\n<li>D&rsquo;autre part, on utilisera un script frida interceptant les fonctions <code>SSL_write<\/code> et <code>SSL_read<\/code> de lecture\/\u00e9criture dans un canal TLS :<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>const SSL_read_ptr = Module.getExportByName('libssl.so', 'SSL_read');\nconst SSL_write_ptr = Module.getExportByName('libssl.so', 'SSL_write');\n\nvar read_buf_ptr = null;\nvar read_buf_len = -1;\n\nInterceptor.attach(SSL_read_ptr, {\n    onEnter: function(args) {\n        read_buf_ptr = args&#91;1];\n        read_buf_len = parseInt(args&#91;2]);\n    },\n\n    onLeave: function(retval) {\n        console.log(\"SSL_read()\");\n        if (read_buf_ptr != null &amp;&amp; read_buf_len != -1)\n        {\n            console.log(\"SSL_read, num : \" + read_buf_len);\n            var bufferContent = Memory.readByteArray(read_buf_ptr, read_buf_len);\n            console.log(\"SSL_read, buf:\\n\" + hexdump(bufferContent));\n        }\n\n        read_buf_ptr = null;\n        read_buf_len = -1;\n    }\n});\n\nInterceptor.attach(SSL_write_ptr, {\n    onEnter: function(args) {\n        console.log(\"SSL_write()\");\n\n        var buf = args&#91;1];\n        var num = parseInt(args&#91;2]);\n\n        console.log(\"SSL_write, num : \" + num);\n        var bufferContent = Memory.readByteArray(buf, num);\n        console.log(\"SSL_write, buf:\\n\" + hexdump(bufferContent));\n    },\n        onLeave: function(retval) {\n    }\n});<\/code><\/pre>\n\n\n\n<p>L&rsquo;analyse du trafic obtenu par ces diff\u00e9rentes m\u00e9thodes aboutit \u00e0 ces conclusions partielles :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Les messages textuels ne semblent pas directement pr\u00e9sents dans le trafic intercept\u00e9s avec Burp. Cela peut \u00eatre d\u00fb \u00e0 la pr\u00e9sence d&rsquo;une couche de chiffrement suppl\u00e9mentaire, ou au fait que le trafic transportant les messages textes ne pasent pas dans le proxy, ou bien les deux.<\/li>\n\n\n\n<li>Le trafic intercept\u00e9 par Burp est du HTTP2. C&rsquo;est notamment le cas du trafic contenant les fichiers \u00e9chang\u00e9s.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Envoi de fichiers<\/strong><\/h4>\n\n\n\n<p>Contrairement aux messages texte, les fichiers sont envoy\u00e9s tels quels vers un des serveurs <code>*.haiserve.com<\/code>.<\/p>\n\n\n\n<p>En effet, si l&rsquo;on cr\u00e9e un fichier texte <code>bbbbbbbbbbbbbbbbbbbbbb.txt<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>1|lynx:\/sdcard\/Download $ echo \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" &gt; bbbbbbbbbbbbbbbbbbbbbb.txt\nlynx:\/sdcard\/Download $ ls -l bbbbbbbbbbbbbbbbbbbbbb.txt                                                                                                                                                          \n-rw-rw---- 1 u0_a242 media_rw 66 2024-11-29 13:02 bbbbbbbbbbbbbbbbbbbbbb.txt<\/code><\/pre>\n\n\n\n<p>Et que l&rsquo;on intercepte avec Burp le trafic cons\u00e9cutif \u00e0 l&rsquo;envoi du fichier, on constate qu&rsquo;une premi\u00e8re requ\u00eate contenant le nom du fichier est envoy\u00e9e \u00e0 <code>f.haiserve.com<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image alignwide size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1883\" height=\"315\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_1.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-262\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_1.png 1883w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_1-300x50.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_1-768x128.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_1-1536x257.png 1536w\" sizes=\"auto, (max-width: 1883px) 100vw, 1883px\" \/><\/figure>\n\n\n\n<p>Analysons quelques-uns des \u00e9l\u00e9ments de cette requ\u00eate :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>En-t\u00eate <code>Seatalk-User-Id<\/code> : C&rsquo;est l&rsquo;identifiant num\u00e9rique de l&rsquo;\u00e9metteur. Il est \u00e0 noter que l&rsquo;identifiant utilis\u00e9 (641926) diff\u00e8re de celui mentionn\u00e9 dans la GUI de l&rsquo;application (9323603334). Une op\u00e9ration de translation de l&rsquo;identifiant \u00ab\u00a0user-friendly\u00a0\u00bb vers l&rsquo;identifiant r\u00e9el est donc r\u00e9alis\u00e9es quelque part.<\/li>\n\n\n\n<li>Cet identifiant num\u00e9rique appara\u00eet aussi dans le param\u00e8tre <code>sender<\/code> du corps de la requ\u00eate.<\/li>\n\n\n\n<li>champ <code>md5<\/code> : Ce champ contient le hach\u00e9 md5 du fichier envoy\u00e9. En effet :<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>$ md5sum bbbbbbbbbbbbbbbbbbbbbb.txt \ndb3f892a3d4a4320cf060c0e8a9b48b8  bbbbbbbbbbbbbbbbbbbbbb.txt<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>champ <code>filesize<\/code> : Longueur totale du fichier envoy\u00e9.<\/li>\n\n\n\n<li>champ <code>filename<\/code> : On s&rsquo;en doute, c&rsquo;est le nom du fichier \u00e0 envoyer.<\/li>\n<\/ul>\n\n\n\n<p>Le corps du fichier est envoy\u00e9 dans des requ\u00eates POST ult\u00e9rieures vers le m\u00eame serveur <code>f.haiserve.com<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image alignwide size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1887\" height=\"536\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_2.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-263\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_2.png 1887w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_2-300x85.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_2-768x218.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/fileUpload_2-1536x436.png 1536w\" sizes=\"auto, (max-width: 1887px) 100vw, 1887px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Interception des messages textes<\/strong><\/h4>\n\n\n\n<p>L&rsquo;interception du trafic avec Burp permet de se convaincre assez rapidement que les messages textes ne passent pas dans le proxy :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>aucune requ\u00eate en rapport avec les frappes clavier n&rsquo;appara\u00eet dans l&rsquo;historique Burp,<\/li>\n\n\n\n<li>les messages texte arrivent m\u00eame lorsque Burp bloque les requ\u00eates intercept\u00e9es.<\/li>\n<\/ul>\n\n\n\n<p>Lancer tcpdump sur le t\u00e9l\u00e9phone montre du trafic non proxifi\u00e9 \u00e9chang\u00e9 avec 143.92.74.215, qui est l&rsquo;adresse IP de <code>edge-co.haiserve.com<\/code> (adresse rencontr\u00e9e plus haut).<\/p>\n\n\n\n<p>Au fil des hooks, on finit par aboutir \u00e0 la classe <code>ChatMessage<\/code>.<\/p>\n\n\n\n<p>Cette classe contient une m\u00e9thode <code>toString()<\/code> qui affiche une grande partie des composants d&rsquo;un objet ChatMessage, hormis le champ <code>content<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1013\" height=\"866\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/class_ChatMessage_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-264\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/class_ChatMessage_seatalk.png 1013w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/class_ChatMessage_seatalk-300x256.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/class_ChatMessage_seatalk-768x657.png 768w\" sizes=\"auto, (max-width: 1013px) 100vw, 1013px\" \/><\/figure>\n\n\n\n<p>Un hook sur cette m\u00e9thode :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>chatMessage.toString.implementation = function()\n{\n    console.log(\"appel de com.garena.ruma.model.ChatMessage.toString()\");\n    console.log(\"clientId : \" + this.clientId.value);\n    console.log(\"sessionId : \" + this.sessionId.value);\n\n    var contentVal = new Uint8Array(this.content.value);\n\n    \/* honestly i don't understand the meaning of these 11 and 160 *\/\n    var msgLen = contentVal&#91;11] - 160;\n\n    var msgTxtRaw = contentVal.subarray(12, 12 + msgLen);\n    var msgTxtPlain = new String();\n    for (var i = 0; i &lt; msgTxtRaw.length; i++)\n    {\n        msgTxtPlain += String.fromCharCode(msgTxtRaw&#91;i]);\n    }\n\n    console.log(\"Message content length : \" + msgLen);\n    console.log(\"Message content : \" + msgTxtPlain);\n\n    var result = toString.call(this);\n    console.log(\"resultat : \" + result);\n\n    console.log(\"appel de ChatMessage.toString() - pile d'appel :\");\n    console.log(stackTraceHere());\n\n    return result;\n};<\/code><\/pre>\n\n\n\n<p>permet d&rsquo;acc\u00e9der au contenu d&rsquo;un message texte :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>appel de com.garena.ruma.model.ChatMessage.toString()\nclientId : 440\nsessionId : 641936\nMessage content length : 17\nMessage content : J'aime la semoule\nresultat : ChatMessage{clientId=440, msgId=0, sessionMsgId=0, sessionId=641936, order=297, fromId=641926, tag='text', content=&#91;...], extraContent=&#91;...], type=514, state=16, timestamp=1734640291, client ts=1734640291051, requestId=0, createTime=1734640291, whisperDuration=0, quote=&#91;...], fromPush=false, options=0, ftsUpdated=0, forward=false, seenTime=-1, isTranslating=false, scenario=normal, disappearTimestamp=0}\nappel de ChatMessage.toString() - pile d'appel :\njava.lang.Exception\n    at com.garena.ruma.model.ChatMessage.toString(Native Method)\n    at java.lang.String.valueOf(String.java:4092)\n    at java.lang.StringBuilder.append(StringBuilder.java:179)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.l(BaseSendMessageToServerTask.kt:140)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.u(BaseSendMessageToServerTask.kt:59)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.c(BaseSendMessageToServerTask.kt:1)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$run$2.invokeSuspend(IBaseCoroutineTask.kt:33)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$run$2.invoke(IBaseCoroutineTask.kt:13)\n    at kotlinx.coroutines.intrinsics.UndispatchedKt.a(Undispatched.kt:5)\n    at kotlinx.coroutines.BuildersKt.f(Unknown Source:75)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$DefaultImpls.a(IBaseCoroutineTask.kt:12)\n    at com.garena.ruma.framework.taskmanager.BaseCoroutineTask.f(BaseCoroutineTask.kt:1)\n    at com.garena.ruma.framework.taskmanager.CoroutineTaskSchedulerKt.a(CoroutineTaskScheduler.kt:65)\n    at com.garena.ruma.framework.taskmanager.CoroutineTaskScheduler$execute$taskChannel$1$1$channel$1.invokeSuspend(CoroutineTaskScheduler.kt:333)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:99)<\/code><\/pre>\n\n\n\n<p>En plus du contenu (champ <code>content<\/code>), un <code>ChatMessage<\/code> contient diverses informations :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>clientId<\/code> : Un identifiant de message, incr\u00e9ment\u00e9 d&rsquo;un message \u00e0 l&rsquo;autre<\/li>\n\n\n\n<li><code>sessionId<\/code> : L&rsquo;identifiant du destinataire<\/li>\n\n\n\n<li><code>order<\/code> : Un autre identifiant de message<\/li>\n\n\n\n<li><code>fromId<\/code> : L&rsquo;identifiant de l&rsquo;\u00e9metteur<\/li>\n\n\n\n<li><code>timestamp<\/code> : Un Horodatage<\/li>\n\n\n\n<li><code>clientts<\/code> : Un autre horodatage<\/li>\n\n\n\n<li><code>createTime<\/code> : Un dernier horodatage<\/li>\n\n\n\n<li><code>whisperDuration<\/code> : La dur\u00e9e de vie du message, en seconde, la valeur <code>0<\/code> signifiant que le message n&rsquo;expire pas<\/li>\n<\/ul>\n\n\n\n<p>(cette liste n&rsquo;est pas exhaustive !)<\/p>\n\n\n\n<p>Si les messages sont envoy\u00e9s \u00e0 <code>edge-co.haiserve.com<\/code> et n&rsquo;apparaissent pas dans l&rsquo;historique Burp, il est int\u00e9ressant de constater que chaque envoi de message donne lieu \u00e0 l&rsquo;envoi d&rsquo;une requ\u00eate \u00e0 <code><a href=\"https:\/\/api.haiserve.com\">https:\/\/api.haiserve.com<\/a><\/code>, qui embarque des m\u00e9tadonn\u00e9es relatives au message :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1423\" height=\"813\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/textMsg_metadata_3.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-265\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/textMsg_metadata_3.png 1423w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/textMsg_metadata_3-300x171.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/textMsg_metadata_3-768x439.png 768w\" sizes=\"auto, (max-width: 1423px) 100vw, 1423px\" \/><\/figure>\n\n\n\n<p>Une autre classe d&rsquo;int\u00e9r\u00eat est la classe <code>CryptoUtils<\/code>, qui contient des m\u00e9thodes de chiffrement\/d\u00e9chiffrement.<\/p>\n\n\n\n<p>Cependant, tenter de tracer les appels aux m\u00e9thodes de <code>CryptoUtils<\/code> sur le Pixel 7a sous Android 14 ne donne rien : En r\u00e9alit\u00e9 ces m\u00e9thodes sont pourtant bien utils\u00e9es, comme l&rsquo;on s&rsquo;en rend compte en r\u00e9p\u00e9tant l&rsquo;op\u00e9ration avec un t\u00e9l\u00e9phone utilisant Android 10 : Il semble que frida n&rsquo;arrive pas \u00e0 tracer correctement certaines m\u00e9thodes sur un terminal sous Android 14, en particulier les appels aux m\u00e9thodes d&rsquo;une classe prenant en argument une instance de ladite classe, comme ci-dessous :<\/p>\n\n\n\n<pre class=\"wp-block-code has-small-font-size\"><code>public class Toto\n{\n    ...\n    public static a(Toto toto, byte &#91;] a, int b)\n    {\n        ...\n    }\n}<\/code><\/pre>\n\n\n\n<p>La classe <code>CryptoUtils<\/code> contient 5 m\u00e9thodes, <code>a<\/code>, <code>b<\/code>, <code>c<\/code>, <code>d<\/code> et et <code>e<\/code>.<\/p>\n\n\n\n<p>La m\u00e9thode <code>a<\/code> effectue le d\u00e9chiffrement AES256-GCM d&rsquo;un tableau <code>content<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"875\" height=\"324\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_a_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-266\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_a_seatalk.png 875w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_a_seatalk-300x111.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_a_seatalk-768x284.png 768w\" sizes=\"auto, (max-width: 875px) 100vw, 875px\" \/><\/figure>\n\n\n\n<p>La cl\u00e9 de d\u00e9chiffrement est le <code>byte [] key<\/code>, deuxi\u00e8me argument de la m\u00e9thode. Le nonce GCM utilis\u00e9 provient des 12 premiers octets de <code>content<\/code>.<\/p>\n\n\n\n<p>\u00c0 l&rsquo;inverse, la m\u00e9thode <code>b<\/code> chiffre en AES256-GCM le tableau <code>bArr<\/code> avec la cl\u00e9 <code>bArr2<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"881\" height=\"394\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_b_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-267\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_b_seatalk.png 881w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_b_seatalk-300x134.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_b_seatalk-768x343.png 768w\" sizes=\"auto, (max-width: 881px) 100vw, 881px\" \/><\/figure>\n\n\n\n<p>Le nonce GCM est tir\u00e9 au sort (appel <code>b.nextBytes(bArr3)<\/code>, sachant que <code>b<\/code> est un <code>SecureRandom<\/code>).<\/p>\n\n\n\n<p>La m\u00e9thode retourne <code>result<\/code>, qui est la concat\u00e9nation du nonce GCM, du message chiffr\u00e9 et du tag GCM.<\/p>\n\n\n\n<p>La m\u00e9thode <code>c<\/code> r\u00e9alise le chiffrement RSA de <code>bArr<\/code> en utilisant la cl\u00e9 publique <code>publicKey<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"894\" height=\"173\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_c_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-268\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_c_seatalk.png 894w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_c_seatalk-300x58.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_c_seatalk-768x149.png 768w\" sizes=\"auto, (max-width: 894px) 100vw, 894px\" \/><\/figure>\n\n\n\n<p>Enfin, les m\u00e9thodes <code>d<\/code> et <code>e<\/code> r\u00e9alisent des appels \u00e0 HmacSHA256.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"479\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_d_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-269\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_d_seatalk.png 686w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_d_seatalk-300x209.png 300w\" sizes=\"auto, (max-width: 686px) 100vw, 686px\" \/><\/figure>\n\n\n\n<p>m\u00e9thode <code>e<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"886\" height=\"323\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_e_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-270\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_e_seatalk.png 886w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_e_seatalk-300x109.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/CryptoUtils_e_seatalk-768x280.png 768w\" sizes=\"auto, (max-width: 886px) 100vw, 886px\" \/><\/figure>\n\n\n\n<p>Ces deux m\u00e9thodes sont utilis\u00e9es pour des op\u00e9rations de d\u00e9rivation de cl\u00e9, sur lesquelles nous reviendront ult\u00e9rieurement.<\/p>\n\n\n\n<p>Capturons un autre message texte, o\u00f9 le corps du message est la phrase \u00ab\u00a0j&rsquo;aime la galette\u00a0\u00bb :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>appel de com.garena.ruma.model.ChatMessage.toString()\nclientId : 360\nsessionId : 641926\nMessage content length : 57\nMessage content : &amp;J'aime la galette savez vous comment ?\u00a1l\u0090\u00a3ipl\u00c2\nresultat : ChatMessage{clientId=360, msgId=0, sessionMsgId=0, sessionId=641926, order=327, fromId=641936, tag='text', content=&#91;...], extraContent=&#91;...], type=514, state=16, timestamp=1735246536, client ts=1735246535780, requestId=0, createTime=1735246536, whisperDuration=0, quote=&#91;...], fromPush=false, options=0, ftsUpdated=0, forward=false, seenTime=-1, isTranslating=false, scenario=normal, disappearTimestamp=0}\nappel de ChatMessage.toString() - pile d'appel :\njava.lang.Exception\n    at com.garena.ruma.model.ChatMessage.toString(Native Method)\n    at java.lang.String.valueOf(String.java:2924)\n    at java.lang.StringBuilder.append(StringBuilder.java:132)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.l(BaseSendMessageToServerTask.kt:140)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.u(BaseSendMessageToServerTask.kt:59)\n    at com.garena.seatalk.message.chat.task.send2server.BaseSendMessageToServerTask.c(BaseSendMessageToServerTask.kt:1)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$run$2.invokeSuspend(IBaseCoroutineTask.kt:33)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$run$2.invoke(IBaseCoroutineTask.kt:13)\n    at kotlinx.coroutines.intrinsics.UndispatchedKt.a(Undispatched.kt:5)\n    at kotlinx.coroutines.BuildersKt.f(Unknown Source:75)\n    at com.garena.ruma.framework.taskmanager.IBaseCoroutineTask$DefaultImpls.a(IBaseCoroutineTask.kt:12)\n    at com.garena.ruma.framework.taskmanager.BaseCoroutineTask.f(BaseCoroutineTask.kt:1)\n    at com.garena.ruma.framework.taskmanager.CoroutineTaskSchedulerKt.a(CoroutineTaskScheduler.kt:65)\n    at com.garena.ruma.framework.taskmanager.CoroutineTaskScheduler$execute$taskChannel$1$1$channel$1.invokeSuspend(CoroutineTaskScheduler.kt:333)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:99)<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Chiffrement des messages textes<\/strong><\/h4>\n\n\n\n<p>Si l&rsquo;on trace \u00e9galement les appels aux m\u00e9thodes de <code>CryptoUtils<\/code>, on retrouve ce message dans un appel \u00e0 la m\u00e9thode de chiffrement CryptoUtils.b :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>com.seagroup.seatalk.utils.CryptoUtils.b (AES-GCM encryption)\nb, bArr (plaintext) : \n85 a1 69 cf 07 6d c2 b4 22 58 40 7b a1 74 cf 26\n0a 3b 98 00 09 cb 90 a1 6d de 00 12 a3 63 69 64 \ncd 01 68 a3 63 74 73 cf 00 00 01 94 04 c0 dc 64\na1 63 c4 3b 85 a2 65 74 00 a2 65 76 00 a1 63 d9 \n26 4a 27 61 69 6d 65 20 6c 61 20 67 61 6c 65 74\n74 65 20 73 61 76 65 7a 20 76 6f 75 73 20 63 6f \n6d 6d 65 6e 74 20 3f a1 6c 90 a3 69 70 6c c2 a3\n64 74 73 00 a1 75 ce 00 09 cb 90 a2 69 64 00 a4 \n72 6d 69 64 00 a3 70 74 73 00 a1 6f 00 a1 71 c4\n00 a5 72 74 6d 69 64 00 a2 6e 61 c2 a3 73 74 73 \n00 a3 6d 69 64 00 a1 74 a4 74 65 78 74 a2 74 73\nce 67 6d c2 c8 a2 74 6f 00 a1 77 00 a1 62 ce 00 \n09 cb 86 a1 72 00\nb, bArr (plaintext) : (...)J'aime la galette savez vous comment ?(...)\nb, bArr2 (key) : \n2c 3e c5 6f 4a 09 39 68 4d 72 f6 97 9c cc f9 d2 ce 76 b1 c1 5b ca f5 c9 1d 57 94 41 d4 6e e2 c4 \n\njava.lang.Exception\n    at com.seagroup.seatalk.utils.CryptoUtils.b(Native Method)\n    at com.garena.ruma.network.tcp.STTcpPacketCodec.d(STTcpPacketCodec.kt:116)\n    at com.garena.ruma.network.tcp.STTcpPacketCodec.c(STTcpPacketCodec.kt:31)\n    at com.garena.ruma.network.tcp.lib.TcpClient.f(TcpClient.kt:26)\n    at com.garena.ruma.network.tcp.TcpHandler$SendTcpInterceptor.a(TcpHandler.kt:499)\n    at com.garena.ruma.network.tcp.TcpHandler$CoordinatorChain.c(TcpHandler.kt:56)\n    at com.garena.ruma.framework.network.guard.CaptchaInterceptor.a(CaptchaInterceptor.kt:10)\n    at com.garena.ruma.network.tcp.TcpHandler$CoordinatorChain.c(TcpHandler.kt:56)\n    at com.garena.ruma.framework.network.guard.TcpMonitorInterceptor.a(TcpMonitorInterceptor.kt:57)\n    at com.garena.ruma.network.tcp.TcpHandler$CoordinatorChain.c(TcpHandler.kt:56)\n    at com.garena.ruma.framework.network.guard.RequestFilterInterceptor.a(RequestFilterInterceptor.kt:108)\n    at com.garena.ruma.network.tcp.TcpHandler$CoordinatorChain.c(TcpHandler.kt:56)\n    at com.garena.ruma.network.tcp.TcpHandler.g(TcpHandler.kt:429)\n    at com.garena.ruma.network.tcp.TcpHandler.handleMessage(TcpHandler.kt:95)\n    at android.os.Handler.dispatchMessage(Handler.java:107)\n    at android.os.Looper.loop(Looper.java:359)\n    at android.os.HandlerThread.run(HandlerThread.java:67)\n\nb, result : \naa 53 05 01 5d d9 3c 83 ef 16 78 4e 70 7d 31 67\nb1 17 97 37 9c 53 40 f0 1f db 07 22 d3 79 fe 29 \ndf f0 92 f7 1d e6 b5 d9 23 ea 98 f6 18 cd 52 cb\nfb 18 28 b4 cc 6f b5 f5 37 70 8d 45 12 1f 9c c4 \n69 61 3d 56 06 53 fc 69 df e3 57 2c 75 7b 8e ea\nc8 e5 1a 70 60 a7 8b 56 65 11 32 eb 64 c4 b6 e3 \n37 fd f0 76 07 9b df 11 b6 da ac 86 a5 e6 13 62\ncf d9 7b 90 18 63 7a 13 18 e8 a2 c1 e7 3f bf 50 \n11 1c 61 8d cb 3c 15 2d d3 8c ad 2f 22 19 ff 70\nd6 49 46 b7 44 5c 3c 81 c7 96 df 68 8a df 0b 17 \nbb fe 1f ea b5 d0 27 3c 09 03 de 10 f5 4c 8a ad\nf7 95 8a 9b 34 14 ec 8e 0e d1 79 21 89 e3 05 9e \ne2 81 bb 2f 45 8d f7 1a 88 9e db a1 cc 3b f5 4b\n12 20 8b bf 29 ba be c1 06 c7 b9 32 5a 77 5e fe \nf8 79 <\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Structure partielle du message texte<\/strong><\/h4>\n\n\n\n<p>La m\u00e9thode <code>ChatMessage.toString()<\/code> permet d&rsquo;obtenir des informations partielles sur la structure du message clair :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>85 a1 69 cf 07 6d c2 b4 22 58 40 7b a1 74 cf 26\n0a 3b 98 00 09 cb 90 a1 6d de 00 12 a3 63 69 64 \ncd \n   01 68 \/* clientId *\/\n         a3 63 74 73 cf 00 00 \n                              01 94 04 c0 dc 64 \/* client ts *\/\na1 63 c4 3b 85 a2 65 74 00 a2 65 76 00 a1 63 d9 \n26 \/* longueur du contenu *\/\n   4a 27 61 69 6d 65 20 6c 61 20 67 61 6c 65 74\n74 65 20 73 61 76 65 7a 20 76 6f 75 73 20 63 6f \n6d 6d 65 6e 74 20 3f \/* contenu du message texte *\/\n                     a1 6c 90 a3 69 70 6c c2 a3\n64 74 73 00 a1 75 ce 00\n                        09 cb 90 \/* fromId *\/\n                                 a2 69 64 00 a4 \n72 6d 69 64 00 a3 70 74 73 00 a1 6f 00 a1 71 c4\n00 a5 72 74 6d 69 64 00 a2 6e 61 c2 a3 73 74 73 \n00 a3 6d 69 64 00 a1 74 a4 74 65 78 74 a2 74 73\nce \n   67 6d c2 c8 \/* timestamp\/createTime *\/\n               a2 74 6f 00 a1 77 00 a1 62 ce 00 \n09 cb 86 \/* sessionId *\/\n         a1 72 00<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Devenir du message chiffr\u00e9<\/strong><\/h4>\n\n\n\n<p>Un buffer contenant le chiffr\u00e9 AES-GCM du message (rencontr\u00e9 plus haut) est pass\u00e9 en param\u00e8tre de la fonction <code>SSL_write<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>SSL_write()\nSSL_write, num : 226\nSSL_write, buf:\n           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF\n00000000  aa 53 05 01 5d d9 3c 83 ef 16 78 4e 70 7d 31 67  .S..].&lt;...xNp}1g\n00000010  b1 17 97 37 9c 53 40 f0 1f db 07 22 d3 79 fe 29  ...7.S@....\".y.)\n00000020  df f0 92 f7 1d e6 b5 d9 23 ea 98 f6 18 cd 52 cb  ........#.....R.\n00000030  fb 18 28 b4 cc 6f b5 f5 37 70 8d 45 12 1f 9c c4  ..(..o..7p.E....\n00000040  69 61 3d 56 06 53 fc 69 df e3 57 2c 75 7b 8e ea  ia=V.S.i..W,u{..\n00000050  c8 e5 1a 70 60 a7 8b 56 65 11 32 eb 64 c4 b6 e3  ...p`..Ve.2.d...\n00000060  37 fd f0 76 07 9b df 11 b6 da ac 86 a5 e6 13 62  7..v...........b\n00000070  cf d9 7b 90 18 63 7a 13 18 e8 a2 c1 e7 3f bf 50  ..{..cz......?.P\n00000080  11 1c 61 8d cb 3c 15 2d d3 8c ad 2f 22 19 ff 70  ..a..&lt;.-...\/\"..p\n00000090  d6 49 46 b7 44 5c 3c 81 c7 96 df 68 8a df 0b 17  .IF.D\\&lt;....h....\n000000a0  bb fe 1f ea b5 d0 27 3c 09 03 de 10 f5 4c 8a ad  ......'&lt;.....L..\n000000b0  f7 95 8a 9b 34 14 ec 8e 0e d1 79 21 89 e3 05 9e  ....4.....y!....\n000000c0  e2 81 bb 2f 45 8d f7 1a 88 9e db a1 cc 3b f5 4b  ...\/E........;.K\n000000d0  12 20 8b bf 29 ba be c1 06 c7 b9 32 5a 77 5e fe  . ..)......2Zw^.\n000000e0  f8 79                                            .y<\/code><\/pre>\n\n\n\n<p>Le message chiffr\u00e9 est donc envoy\u00e9 dans la session TLS avec 143.92.74.215, c&rsquo;est-\u00e0-dire <code>edge-co.haiserve.com<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Provenance de la cl\u00e9 de chiffrement du message texte<\/strong><\/h4>\n\n\n\n<p>Dans notre exemple, la cl\u00e9 utilis\u00e9e par <code>CryptoUtils.b<\/code> pour chiffrer a pour valeur<\/p>\n\n\n\n<p class=\"has-small-font-size\"><code>2c 3e c5 6f 4a 09 39 68 4d 72 f6 97 9c cc f9 d2 ce 76 b1 c1 5b ca f5 c9 1d 57 94 41 d4 6e e2 c4<\/code><\/p>\n\n\n\n<p>Dans la trace obtenue avec les diff\u00e9rents scripts frida, cette cl\u00e9 appara\u00eet pour la premi\u00e8re fois comme r\u00e9sultat de <code>CryptoUtils.d<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>com.seagroup.seatalk.utils.CryptoUtils.e\ne, bArr : \ncd 59 58 f8 0b 9e ff b4 6a bf e7 2c 35 ea e2 ae 1b cf e3 ed 79 a6 0b 3c 0e 84 71 6e d9 4a 8e 20 \n\ne, serverFactor : \n42 38 bf bc b9 ae f0 c3 ad 61 fb d7 a5 6d ec da b7 f8 7d 94 a6 24 76 87 4a 83 7a 6e cb e8 e2 c9 \n\njava.lang.Exception\n    at com.seagroup.seatalk.utils.CryptoUtils.e(Native Method)\n    at com.garena.ruma.framework.network.TcpManager.r(TcpManager.kt:523)\n    at com.garena.ruma.framework.network.TcpManager$negotiateCipher$1.invokeSuspend(TcpManager.kt:14)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)\n    at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)\n    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n    at java.lang.Thread.run(Thread.java:919)\n\n\ncom.seagroup.seatalk.utils.CryptoUtils.d\nd, bArr (key) : \ne6 3b 5e 6b 87 12 73 81 32 66 00 d1 5d 83 74 24 37 8d f7 6f c0 85 d6 6e 2e e9 56 4d fd 45 f1 79 \n\njava.lang.Exception\n    at com.seagroup.seatalk.utils.CryptoUtils.d(Native Method)\n    at com.seagroup.seatalk.utils.CryptoUtils.e(CryptoUtils.kt:52)\n    at com.seagroup.seatalk.utils.CryptoUtils.e(Native Method)\n    at com.garena.ruma.framework.network.TcpManager.r(TcpManager.kt:523)\n    at com.garena.ruma.framework.network.TcpManager$negotiateCipher$1.invokeSuspend(TcpManager.kt:14)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)\n    at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)\n    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n    at java.lang.Thread.run(Thread.java:919)\n\nd, result : \n2c 3e c5 6f 4a 09 39 68 4d 72 f6 97 9c cc f9 d2 ce 76 b1 c1 5b ca f5 c9 1d 57 94 41 d4 6e e2 c4 \n\ne, result : \n2c 3e c5 6f 4a 09 39 68 4d 72 f6 97 9c cc f9 d2 ce 76 b1 c1 5b ca f5 c9 1d 57 94 41 d4 6e e2 c4 <\/code><\/pre>\n\n\n\n<p>La cl\u00e9 de chiffrement des messages (nommons-la <code>k2<\/code>) est donc le r\u00e9sultat de <code>CryptoUtils.d<\/code>, qui est lui-m\u00eame le r\u00e9sultat de <code>CryptoUtils.e<\/code>.<\/p>\n\n\n\n<p>Attardons-nous sur le code de <code>CryptoUtils.e<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>public final byte&#91;] e(@NotNull byte&#91;] bArr, @NotNull byte&#91;] serverFactor) {\n    Intrinsics.f(serverFactor, \"serverFactor\");\n    try {\n        int length = bArr.length;\n        int length2 = serverFactor.length;\n        byte&#91;] result = Arrays.copyOf(bArr, length + length2);\n        System.arraycopy(serverFactor, 0, result, length, length2);\n        Intrinsics.e(result, \"result\");\n        Mac mac = Mac.getInstance(\"HmacSHA256\");\n        mac.init(new SecretKeySpec(new byte&#91;mac.getMacLength()], \"HmacSHA256\"));\n        byte&#91;] doFinal = mac.doFinal(result);\n        Intrinsics.e(doFinal, \"mac.doFinal(ikm)\");\n        return d(this, doFinal);\n    } catch (Exception e) {\n        Log.b(\"CryptoUtils\", r5.g(e, new StringBuilder(\"fail to generate secret key spec: \")), new Object&#91;0]);\n        return new byte&#91;0];\n    }\n}<\/code><\/pre>\n\n\n\n<p>La m\u00e9thode <code>CryptoUtils.e<\/code> commence par calculer le HMAC-SHA256 de la concat\u00e9nation <code>barr + serverFactor<\/code> de ses deux arguments d&rsquo;entr\u00e9e,<\/p>\n\n\n\n<p>la cl\u00e9 utilis\u00e9e \u00e9tant une cl\u00e9 constitu\u00e9e d&rsquo;octets nuls. Le hmac obtenu est utilis\u00e9 comme argument bArr par la m\u00e9thode <code>CryptoUtils.d<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>public static byte&#91;] d(CryptoUtils cryptoUtils, byte&#91;] bArr) {\n    cryptoUtils.getClass();\n    Mac mac = Mac.getInstance(\"HmacSHA256\");\n    mac.init(new SecretKeySpec(bArr, \"HmacSHA256\"));\n    int i = 1;\n    int macLength = ((mac.getMacLength() + 32) - 1) \/ mac.getMacLength();\n    if (macLength &lt;= 255) {\n        ByteBuffer allocate = ByteBuffer.allocate(32);\n        byte&#91;] bArr2 = new byte&#91;0];\n        if (1 &lt;= macLength) {\n            while (true) {\n                mac.update(bArr2);\n                mac.update((byte) i);\n                bArr2 = mac.doFinal();\n                Intrinsics.e(bArr2, \"mac.doFinal()\");\n                allocate.put(bArr2, 0, Math.min(allocate.remaining(), bArr2.length));\n                if (i == macLength) {\n                    break;\n                }\n                i++;\n            }\n        }\n        byte&#91;] array = allocate.array();\n        Intrinsics.e(array, \"okm.array()\");\n        return array;\n    }\n    throw new IllegalArgumentException(\"Requested output length too long\");\n}<\/code><\/pre>\n\n\n\n<p>En d\u00e9finitive, cette fonction calcule le HMAC-SHA256 d&rsquo;une cha\u00eene de caract\u00e8re r\u00e9duite \u00e0 l&rsquo;octet <code>0x01<\/code>, comme le d\u00e9montre le snippet python ci-dessous, qui reproduit la d\u00e9rivation de cl\u00e9 r\u00e9alis\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>$ cat poc_hmac.py \n#!\/usr\/bin\/python3\n\nimport binascii\nimport hashlib\nimport hmac\n\nk1 = b'\\xcd\\x59\\x58\\xf8\\x0b\\x9e\\xff\\xb4\\x6a\\xbf\\xe7\\x2c\\x35\\xea\\xe2\\xae\\x1b\\xcf\\xe3\\xed\\x79\\xa6\\x0b\\x3c\\x0e\\x84\\x71\\x6e\\xd9\\x4a\\x8e\\x20'\nserverFactor = b'\\x42\\x38\\xbf\\xbc\\xb9\\xae\\xf0\\xc3\\xad\\x61\\xfb\\xd7\\xa5\\x6d\\xec\\xda\\xb7\\xf8\\x7d\\x94\\xa6\\x24\\x76\\x87\\x4a\\x83\\x7a\\x6e\\xcb\\xe8\\xe2\\xc9'\nnull_key = b'\\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'\n\npadding_vector = b'\\x01'\n\nresult0 = hmac.new(null_key,  k1 + serverFactor, hashlib.sha256)\nprint(binascii.hexlify(result0.digest()))\n\nresult1 = hmac.new(result0.digest(), padding_vector, hashlib.sha256)\nprint(binascii.hexlify(result1.digest()))\n\n$ python .\/poc_hmac.py \nb'e63b5e6b87127381326600d15d837424378df76fc085d66e2ee9564dfd45f179'\nb'2c3ec56f4a0939684d72f6979cccf9d2ce76b1c15bcaf5c91d579441d46ee2c4'<\/code><\/pre>\n\n\n\n<p>Une fois cette d\u00e9rivation r\u00e9alis\u00e9e, la cl\u00e9 <code>k2<\/code> est utilis\u00e9e pour chiffrer tous les messages \u00e9mis avec <code>CryptoUtils.b<\/code>, et d\u00e9chiffrer les messages re\u00e7us avec <code>CryptoUtils.a<\/code>.<\/p>\n\n\n\n<p>Il est int\u00e9ressant de remarquer que cette cl\u00e9 est utilis\u00e9e quelque soit le destinataire : Le (sur)chiffrement des messages n&rsquo;est donc pas fait de bout en bout.<\/p>\n\n\n\n<p>Continuons de tirer le fil de la pelote et int\u00e9ressons-nous aux arguments d&rsquo;entr\u00e9e de <code>CryptoUtils.e<\/code>.<\/p>\n\n\n\n<p>Le deuxi\u00e8me argument de <code>CryptoUtils.e<\/code>, <code>serverFactor<\/code> n&rsquo;appara\u00eet qu&rsquo;une seule autre fois, comme r\u00e9sultat d&rsquo;un appel \u00e0 la fonction de d\u00e9chiffrement <code>CryptoUtils.a<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>com.seagroup.seatalk.utils.CryptoUtils.a (AES-GCM decryption)\na, key : cd 59 58 f8 0b 9e ff b4 6a bf e7 2c 35 ea e2 ae 1b cf e3 ed 79 a6 0b 3c 0e 84 71 6e d9 4a 8e 20 \n\na, content (ciphertext) : \n3a 21 57 63 fb 72 15 6e 07 04 3b de b0 a8 ac 7a 0a 79 99 ec 2e b6 12 ad fc 68 aa 3b ed 00 a6 35 \nf4 9d 2e 70 3c 95 d2 71 c9 eb 20 b1 0e c3 89 b1 1d 75 2c 06 2c ee 35 26 70 a2 d9 8f 10 3e aa c7 \na6 6c dd c4 5c ab 18 01 ef 59 77 a7 66 e0 f6 c7 a2 5d 1e 6a 0b 7d 70 7c \njava.lang.Exception\n    at com.seagroup.seatalk.utils.CryptoUtils.a(Native Method)\n    at com.garena.ruma.framework.network.TcpManager.r(TcpManager.kt:387)\n    at com.garena.ruma.framework.network.TcpManager$negotiateCipher$1.invokeSuspend(TcpManager.kt:14)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)\n    at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)\n    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n    at java.lang.Thread.run(Thread.java:919)\n\na, result : \n84 a2 69 65 c3 a2 75 70 c2 a2 73 72 c4 20 42 38 bf bc b9 ae f0 c3 ad 61 fb d7 a5 6d ec da b7 f8 \n7d 94 a6 24 76 87 4a 83 7a 6e cb e8 e2 c9 a2 65 61 aa 41 45 53 32 35 36 2d 47 43 4d <\/code><\/pre>\n\n\n\n<p>Remarquons que le <code>serverFactor<\/code> (\u00e9galement nomm\u00e9 <code>serverRandom<\/code> dans le code) est pr\u00e9c\u00e9d\u00e9 par la cha\u00eene de caract\u00e8re <code>sr<\/code> (octets <code>73 72<\/code>), et qu&rsquo;on retrouve peu apr\u00e8s la cha\u00eene de caract\u00e8res<\/p>\n\n\n\n<p><code>AES256-GCM<\/code> (octets <code>41 45 53 32 35 36 2d 47 43 4d<\/code>).<\/p>\n\n\n\n<p>Le <code>ciphertext<\/code> pass\u00e9 \u00e0 <code>CryptoUtils.a<\/code> provient de <code>edge-co.haiserve.com<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>SSL_read()\nSSL_read, num : 105\nSSL_read, buf:\n           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF\n00000000  82 a1 69 cf 07 6d c2 b4 22 58 40 48 a2 6e 72 c4  ..i..m..\"X@H.nr.\n00000010  58 3a 21 57 63 fb 72 15 6e 07 04 3b de b0 a8 ac  X:!Wc.r.n..;....\n00000020  7a 0a 79 99 ec 2e b6 12 ad fc 68 aa 3b ed 00 a6  z.y.......h.;...\n00000030  35 f4 9d 2e 70 3c 95 d2 71 c9 eb 20 b1 0e c3 89  5...p&lt;..q.. ....\n00000040  b1 1d 75 2c 06 2c ee 35 26 70 a2 d9 8f 10 3e aa  ..u,.,.5&amp;p....&gt;.\n00000050  c7 a6 6c dd c4 5c ab 18 01 ef 59 77 a7 66 e0 f6  ..l..\\....Yw.f..\n00000060  c7 a2 5d 1e 6a 0b 7d 70 7c                       ..].j.}p|<\/code><\/pre>\n\n\n\n<p>Le <code>serverRandom<\/code> est donc obtenu en d\u00e9chiffrant un bloc de donn\u00e9e re\u00e7u du serveur avec une cl\u00e9 que nous nommerons dor\u00e9navant <code>k1<\/code>.<\/p>\n\n\n\n<p>Il int\u00e9ressant de noter que cette cl\u00e9 <code>k1<\/code> est \u00e9galement utilis\u00e9e comme premier argument <code>CryptoUtils.e<\/code>.<\/p>\n\n\n\n<p>Enfin, la premi\u00e8re occurrence de la cl\u00e9 <code>k1<\/code> est observ\u00e9e lors d&rsquo;un appel \u00e0 la fonction <code>CryptoUtils.c<\/code> de chiffrement RSA :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>com.seagroup.seatalk.utils.CryptoUtils.c (RSA encryption)\nc, bArr : \ncd 59 58 f8 0b 9e ff b4 6a bf e7 2c 35 ea e2 ae 1b cf e3 ed 79 a6 0b 3c 0e 84 71 6e d9 4a 8e 20\nc, publicKey : \n30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 \n00 bf 5c f1 f5 02 f0 ad 9c 93 3c 63 c9 39 84 09 dd 56 4f d8 c0 67 d4 61 c8 a3 28 7c eb f6 92 6a \n12 e5 b3 fc 97 3a 81 76 7f bd ef ed 92 1e a8 12 ef 49 70 7e d3 58 e6 38 f8 19 40 25 7b 04 e4 9d \nb2 b7 c0 71 dd 74 08 d4 47 b6 36 d1 89 b8 52 55 f1 d1 03 13 24 42 1e 68 d1 78 93 2a 87 26 2a 14 \n9b 35 af 6d 12 75 68 f0 28 42 7e 5d 53 03 4f d0 c6 00 92 0d 36 38 fc 06 6d d3 df ad 03 d3 b0 25 \nfa 3e 2b 3d df f6 75 01 55 6b ad 65 48 b5 cc ec e7 c3 f9 21 33 56 fd c1 e8 27 58 7e 20 09 e3 40 \n3a 5f 6c 6a 5a 81 f1 ff 7d bc 36 c1 12 d9 a6 24 6b 40 b7 3c 17 c8 47 44 de 48 7f 03 3a e6 85 10 \n6c 7b 13 03 9c 2f ac c3 0b 2f 2a 51 21 f0 92 e9 c2 6d a8 da 30 82 8b 6b cb d0 3c ee 6c 40 37 a2 \n57 85 e5 42 71 07 8c 63 7a 5f 4c 27 7b 3a ab 88 68 d5 b1 d5 41 c7 20 7a 33 81 25 f8 94 97 c4 70 \n75 02 03 01 00 01\njava.lang.Exception\n    at com.seagroup.seatalk.utils.CryptoUtils.c(Native Method)\n    at com.garena.ruma.framework.network.TcpManager.r(TcpManager.kt:320)\n    at com.garena.ruma.framework.network.TcpManager.f(TcpManager.kt:429)\n    at com.garena.ruma.framework.network.TcpManager$listenContextChanges$3$1.b(TcpManager.kt:95)\n    at com.garena.ruma.framework.network.TcpManager$listenContextChanges$3$1.a(TcpManager.kt:3)\n    at kotlinx.coroutines.flow.SharedFlowImpl.m(SharedFlow.kt:187)\n    at kotlinx.coroutines.flow.SharedFlowImpl$collect$1.invokeSuspend(SharedFlow.kt:13)\n    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)\n    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:116)\n    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)\n    at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)\n    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n    at java.lang.Thread.run(Thread.java:919)\n\nc, result : \nad e3 bd 55 6b 51 64 46 88 30 b7 d7 3b c9 0b 86 39 c8 57 e7 ce f0 19 39 36 b5 e8 2b 28 fa 20 66 \n6e 76 00 87 84 d5 12 8e ab 51 04 31 08 fe d1 64 d8 44 56 45 3b df 74 6c 84 30 61 71 93 41 2d 64 \n86 32 45 c3 87 63 72 ff 4d 23 98 ad c5 81 41 fc 18 56 95 a9 aa cb ce 84 3d db 9b 67 86 ff 68 f9 \n2d a8 22 2b ee ed 36 d5 bd be 55 64 8f c2 95 8a cb cd 00 f8 df 4f f1 ca bc 57 cc 1d d9 27 c6 33 \n05 16 a2 35 aa 30 7b 08 44 e6 43 02 f0 f9 80 17 7e d1 23 1c 75 60 98 ca a5 a4 be e2 f7 7f 8a 12 \nc6 38 ac 26 7a 94 29 3b 3e cf 0f 6e 0e 16 f8 10 2b eb 67 20 87 d1 c3 6d 27 21 37 75 6c a8 a2 76 \n1e fe 90 30 4e 30 3f ec a3 f3 7d db 85 aa 13 f7 ae cc 90 03 70 32 f8 25 aa fa 61 f5 e2 2f d5 51 \n51 b8 64 18 54 9b 53 55 44 b2 6d 8f 8f ff 31 d0 10 91 f8 14 24 fa 4c 17 81 0d b0 0a 61 08 97 a0 <\/code><\/pre>\n\n\n\n<p>La valeur chiffr\u00e9e de <code>k1<\/code> est ensuite envoy\u00e9e \u00e0 <code>edge-co.haiserve.com<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>SSL_write()\nSSL_write, num : 311\nSSL_write, buf:\n           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF\n00000000  86 a1 69 cf 07 6d c2 b4 22 58 40 48 a7 74 69 6d  ..i..m..\"X@H.tim\n00000010  65 6f 75 74 cd 13 88 a2 63 72 c5 01 00 ad e3 bd  eout....cr......\n00000020  55 6b 51 64 46 88 30 b7 d7 3b c9 0b 86 39 c8 57  UkQdF.0..;...9.W\n00000030  e7 ce f0 19 39 36 b5 e8 2b 28 fa 20 66 6e 76 00  ....96..+(. fnv.\n00000040  87 84 d5 12 8e ab 51 04 31 08 fe d1 64 d8 44 56  ......Q.1...d.DV\n00000050  45 3b df 74 6c 84 30 61 71 93 41 2d 64 86 32 45  E;.tl.0aq.A-d.2E\n00000060  c3 87 63 72 ff 4d 23 98 ad c5 81 41 fc 18 56 95  ..cr.M#....A..V.\n00000070  a9 aa cb ce 84 3d db 9b 67 86 ff 68 f9 2d a8 22  .....=..g..h.-.\"\n00000080  2b ee ed 36 d5 bd be 55 64 8f c2 95 8a cb cd 00  +..6...Ud.......\n00000090  f8 df 4f f1 ca bc 57 cc 1d d9 27 c6 33 05 16 a2  ..O...W...'.3...\n000000a0  35 aa 30 7b 08 44 e6 43 02 f0 f9 80 17 7e d1 23  5.0{.D.C.....~.#\n000000b0  1c 75 60 98 ca a5 a4 be e2 f7 7f 8a 12 c6 38 ac  .u`...........8.\n000000c0  26 7a 94 29 3b 3e cf 0f 6e 0e 16 f8 10 2b eb 67  &amp;z.);&gt;..n....+.g\n000000d0  20 87 d1 c3 6d 27 21 37 75 6c a8 a2 76 1e fe 90   ...m'!7ul..v...\n000000e0  30 4e 30 3f ec a3 f3 7d db 85 aa 13 f7 ae cc 90  0N0?...}........\n000000f0  03 70 32 f8 25 aa fa 61 f5 e2 2f d5 51 51 b8 64  .p2.%..a..\/.QQ.d\n00000100  18 54 9b 53 55 44 b2 6d 8f 8f ff 31 d0 10 91 f8  .T.SUD.m...1....\n00000110  14 24 fa 4c 17 81 0d b0 0a 61 08 97 a0 a2 65 73  .$.L.....a....es\n00000120  91 aa 41 45 53 32 35 36 2d 47 43 4d a2 70 76 02  ..AES256-GCM.pv.\n00000130  a1 75 ce 00 09 cb 90                             .u.....<\/code><\/pre>\n\n\n\n<p>L&rsquo;appel \u00e0 <code>CryptoUtils.c<\/code> est r\u00e9alis\u00e9 dans la fonction <code>TcpManager.r<\/code>.<\/p>\n\n\n\n<p>Dans cette fonction, la cl\u00e9 <code>k1<\/code> est la variable <code>bArr2<\/code>, g\u00e9n\u00e9r\u00e9e par un appel \u00e0 une m\u00e9thode du champ <code>b<\/code> de l&rsquo;objet <code>CryptoUtils<\/code>, qui est un <code>SecureRandom()<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1033\" height=\"436\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/k1_generation_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-271\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/k1_generation_seatalk.png 1033w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/k1_generation_seatalk-300x127.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/k1_generation_seatalk-768x324.png 768w\" sizes=\"auto, (max-width: 1033px) 100vw, 1033px\" \/><\/figure>\n\n\n\n<p>La cl\u00e9 RSA utilis\u00e9e est une des deux cl\u00e9s stock\u00e9es dans la classe <code>CipherPubKey<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1837\" height=\"921\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/classe_CipherPubKey_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-272\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/classe_CipherPubKey_seatalk.png 1837w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/classe_CipherPubKey_seatalk-300x150.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/classe_CipherPubKey_seatalk-768x385.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/classe_CipherPubKey_seatalk-1536x770.png 1536w\" sizes=\"auto, (max-width: 1837px) 100vw, 1837px\" \/><\/figure>\n\n\n\n<p>Apr\u00e8s un peu de nettoyage, les cl\u00e9s utilis\u00e9es sont :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1zx9QLwrZyTPGPJOYQJ\n3VZP2MBn1GHIoyh86\/aSahLls\/yXOoF2f73v7ZIeqBLvSXB+01jmOPgZQCV7BOSd\nsrfAcd10CNRHtjbRibhSVfHRAxMkQh5o0XiTKocmKhSbNa9tEnVo8ChCfl1TA0\/Q\nxgCSDTY4\/AZt09+tA9OwJfo+Kz3f9nUBVWutZUi1zOznw\/khM1b9wegnWH4gCeNA\nOl9salqB8f99vDbBEtmmJGtAtzwXyEdE3kh\/AzrmhRBsexMDnC+swwsvKlEh8JLp\nwm2o2jCCi2vL0DzubEA3oleF5UJxB4xjel9MJ3s6q4ho1bHVQccgejOBJfiUl8Rw\ndQIDAQAB\n-----END PUBLIC KEY-----<\/code><\/pre>\n\n\n\n<p>et<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApfUjEnCtvJR4z\/WowV\/O\nHZNdreAmYz6etsdbJMJ3YIot3MTx6+HqrI2MwUICt1JLgGMskgqhSHWZvEKuRyNP\n3A0kS0P2tHn8kMdo+u1eR1tW1HWTLZsXZwXwfR8PTy2O91Nkmxz3qIi\/dvFE2Fh3\nEuG4e4tCM414KEBv3FjBiCPLQ9VLmEWmSbfbcPZuO26gFPLLV9FykcYdHOA+JOPg\nVrphK\/IlnCRvIHorwBC6NVHthG6rMyTLwEOoSkHeqCiMMDkhNX7gsbJ7o0cES3tC\nDyqGikXTe4+ojtu5BT85xwP3MHcFgakr1N2wWe8OUb2TniNTmO\/y6RDTGZmQmWIq\nfQIDAQAB\n-----END RSA PUBLIC KEY-----<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>R\u00e9capitulatif de la g\u00e9n\u00e9ration de la cl\u00e9 de chiffrement des messages textes<\/strong><\/h4>\n\n\n\n<p>Le chiffrement des messages est donc r\u00e9alis\u00e9 par une cl\u00e9 <code>k2<\/code> obtenue par le proc\u00e9d\u00e9 suivant :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dans <code>TcpManager.r<\/code>, le client g\u00e9n\u00e8re une cl\u00e9 <code>k1<\/code> (appel \u00e0 <code>CryptoUtils.b<\/code>, qui est un <code>SecureRandom()<\/code>),<\/li>\n\n\n\n<li>imm\u00e9diatemment apr\u00e8s, le client transmet au serveur la valeur de <code>k1<\/code> chiffr\u00e9e avec une cl\u00e9 RSA pr\u00e9sente en dur dans l&rsquo;application,<\/li>\n\n\n\n<li>Le serveur g\u00e9n\u00e8re et chiffre son <code>serverRandom<\/code> avec la cl\u00e9 <code>k1<\/code> en AES-GCM et envoie le r\u00e9sultat au client (pr\u00e9c\u00e9d\u00e9 par le tag <code>nr<\/code>),<\/li>\n\n\n\n<li>La cl\u00e9 <code>k2<\/code> est calcul\u00e9e comme le r\u00e9sultat de l&rsquo;appel \u00e0 <code>CryptoUtils.e<\/code> (qui appelle \u00e0 son tour <code>CryptoUtils.d<\/code>) avec les arguments suivants :<\/li>\n\n\n\n<li>un <code>bArr<\/code> \u00e9gal \u00e0 <code>k1<\/code><\/li>\n\n\n\n<li>Un <code>serverFactor<\/code> \u00e9gal au <code>serverRandom<\/code><\/li>\n<\/ul>\n\n\n\n<p>La cl\u00e9 <code>k2<\/code> est ensuite utilis\u00e9e pour chiffrer le message en AES-GCM via <code>CryptoUtils.b<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Les APIs de chiffrement sont-elles bien utilis\u00e9es ?<\/strong><\/h4>\n\n\n\n<p>Quelques \u00e9l\u00e9ments de r\u00e9ponse :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Le nonce GCM est g\u00e9n\u00e9r\u00e9 \u00e0 partir de <code>SecureRandom()<\/code> dans <code>CryptoUtils.b()<\/code>,<\/li>\n\n\n\n<li>La m\u00e9thode <code>CryptoUtils.a()<\/code> v\u00e9rifie bien le tag GCM est bien v\u00e9rifi\u00e9,<\/li>\n<\/ul>\n\n\n\n<p>Un.e cryptologue tatillon.ne froncera peut-\u00eatre les sourcils en voyant la cl\u00e9 <code>k1<\/code> utilis\u00e9e \u00e0 la fois comme cl\u00e9 de chiffrement du <code>serverRandom<\/code> puis comme \u00e9l\u00e9ment d&rsquo;entr\u00e9e de la fonction <code>CryptoUtils.e<\/code>, et en observant que la boucle <code>while (true) {...}<\/code> de <code>CryptoUtils.d<\/code> ne r\u00e9alise &#8211; lors de la d\u00e9rivation de <code>k2<\/code> en tout cas &#8211; qu&rsquo;un appel \u00e0 HMAC_SHA256.<\/p>\n\n\n\n<p>Ceci mis \u00e0 part, la d\u00e9rivation de <code>k2<\/code> ne comporte pas de probl\u00e8me \u00ab\u00a0catastrophique\u00a0\u00bb, si ce n&rsquo;est l&rsquo;absence de perfect forward secrecy : Si la cl\u00e9 publique chiffrant <code>k1<\/code> est compromise, l&rsquo;\u00e9difice s&rsquo;\u00e9croule puisqu&rsquo;il est alors possible d&rsquo;obtenir la valeur de <code>k1<\/code>, du <code>serverRandom<\/code>, et donc de <code>k2<\/code>.<\/p>\n\n\n\n<p>Notons tout de m\u00eame que cela n\u00e9cessite de compromettre la cl\u00e9 priv\u00e9e en question, mais aussi d&rsquo;acc\u00e9der au trafic de handshake, qui est \u00e9chang\u00e9 au dessus du canal TLS \u00e9tabli entre le terminal et le backend de l&rsquo;application.<\/p>\n\n\n\n<p>De m\u00eame, aucune faille cryptographique n&rsquo;a \u00e9t\u00e9 observ\u00e9e dans le chiffrement\/d\u00e9chiffrement des messages textes, si ce n&rsquo;est l&rsquo;absence de chiffrement de bout en bout : Les administrateur.rices du backend de l&rsquo;application peuvent th\u00e9oriquement d\u00e9chiffrer les messages de deux personnes utilisant SeaTalk.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Stockage des donn\u00e9es<\/strong><\/h4>\n\n\n\n<p>Toute personne soucieuse de la s\u00e9curit\u00e9 de son application de messagerie instantan\u00e9e s&rsquo;interrogera non seulement sur les m\u00e9canismes de chiffrement d\u00e9ploy\u00e9s pour \u00e9changer les messages, mais aussi sur la fa\u00e7on dont l&rsquo;application prot\u00e8ge sur le terminal les messages \u00e9chang\u00e9s.<\/p>\n\n\n\n<p>Un premier test \u00e9l\u00e9mentaire pour se faire un avis sur la question est de rechercher dans le r\u00e9pertoire de l&rsquo;application une occurrence d&rsquo;un message \u00e9chang\u00e9 auparavant :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>H8765:\/data\/data\/com.seagroup.seatalk # grep -nre \"Bibifoc le roi des phoques\" .\/\nBinary file .\/databases\/641936-wal matches\nBinary file .\/databases\/641936 matches\nH8765:\/data\/data\/com.seagroup.seatalk # file databases\/641*\n641936      641936-shm  641936-wal\nH8765:\/data\/data\/com.seagroup.seatalk # file databases\/641936*\ndatabases\/641936:     data\ndatabases\/641936-shm: data\ndatabases\/641936-wal: data<\/code><\/pre>\n\n\n\n<p>Le test est donc un \u00e9chec, puisque les messages texte ne font l&rsquo;objet d&rsquo;aucune protection sp\u00e9cifique.<\/p>\n\n\n\n<p>Les fichiers o\u00f9 les messages \u00e9chang\u00e9s sont observ\u00e9s sont des bases de donn\u00e9e SQLite :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide has-small-font-size\"><code>$ file 641936\n641936: SQLite 3.x database, user version 114, last written using SQLite version 3022000<\/code><\/pre>\n\n\n\n<p>Cette base de donn\u00e9e comporte une table <code>buddy_message<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1905\" height=\"846\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-273\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_seatalk.png 1905w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_seatalk-300x133.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_seatalk-768x341.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_seatalk-1536x682.png 1536w\" sizes=\"auto, (max-width: 1905px) 100vw, 1905px\" \/><\/figure>\n\n\n\n<p>ainsi qu&rsquo;une autre table <code>buddy_message_index<\/code> :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1285\" height=\"859\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_index_seatalk.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-274\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_index_seatalk.png 1285w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_index_seatalk-300x201.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/buddy_message_index_seatalk-768x513.png 768w\" sizes=\"auto, (max-width: 1285px) 100vw, 1285px\" \/><\/figure>\n\n\n\n<p>qui contiennent toutes les deux les messages texte, ainsi que diff\u00e9rentes m\u00e9tadonn\u00e9es pour la premi\u00e8re.<\/p>\n\n\n\n<p>Bref, si vous utilisez SeaTalk et que votre t\u00e9l\u00e9phone tombe entre les mains d&rsquo;une personne susceptible d&rsquo;en dumper le contenu, vous \u00eates fichu.e !<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Whisper mode<\/strong><\/h4>\n\n\n\n<p>SeaTalk permet d&rsquo;envoi des message eph\u00e9m\u00e8res, cens\u00e9s s&rsquo;autod\u00e9truire apr\u00e8s une dur\u00e9e valant par d\u00e9faut 60 secondes.<\/p>\n\n\n\n<p>Dans l&rsquo;image ci-dessous, Alice envoie un message eph\u00e9m\u00e8re \u00e0 Bob :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"550\" height=\"305\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperingModeEnvoi.png\" alt=\"\" class=\"wp-image-277\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperingModeEnvoi.png 550w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperingModeEnvoi-300x166.png 300w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/figure>\n\n\n\n<p>Examinons maintenant les bases de donn\u00e9es sqlite de l&rsquo;application apr\u00e8s expiration du d\u00e9lai de 60 secondes.<\/p>\n\n\n\n<p>Cot\u00e9 \u00e9metteur :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/data\/data\/com.seagroup.seatalk # grep -nre \"lira ceci\" .\/\nBinary file .\/databases\/641926 matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches\nBinary file .\/databases\/641926-wal matches<\/code><\/pre>\n\n\n\n<p>Et cot\u00e9 r\u00e9cepteur :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>H8765:\/data\/data\/com.seagroup.seatalk # grep -nre \"Waf\" .\/\nBinary file .\/databases\/641936-wal matches\nBinary file .\/databases\/641936 matches\nH8765:\/data\/data\/com.seagroup.seatalk # grep -nre \"lira ceci\" .\/\nBinary file .\/databases\/641936-wal matches\nBinary file .\/databases\/641936 matches<\/code><\/pre>\n\n\n\n<p>Si l&rsquo;on examine la table <code>buddy_message<\/code> du r\u00e9cepteur apr\u00e8s expiration de la dur\u00e9e de vie du message eph\u00e9m\u00e8re, on constate donc qu&rsquo;il est toujours l\u00e0\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image size-full has-custom-border\"><img loading=\"lazy\" decoding=\"async\" width=\"1918\" height=\"876\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperMode_bof.png\" alt=\"\" class=\"has-border-color has-foreground-border-color wp-image-278\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperMode_bof.png 1918w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperMode_bof-300x137.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperMode_bof-768x351.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2024\/12\/whisperMode_bof-1536x702.png 1536w\" sizes=\"auto, (max-width: 1918px) 100vw, 1918px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Conclusions<\/strong><\/h4>\n\n\n\n<p>SeaTalk n&rsquo;utilise pas de chiffrement de bout en bout pour les messages textes, chose que l&rsquo;on peut raisonnablement attendre d&rsquo;une application de messagerie instantan\u00e9e \u00e0 l&rsquo;\u00e9tat de l&rsquo;art : Quelqu&rsquo;un capable de compromettre l&rsquo;infrastructure de SeaTalk pourra \u00e9galement mettre la main sur vos messages texte.<\/p>\n\n\n\n<p>Une faille plus prosa\u00efque est pr\u00e9sente dans SeaTalk : Les messages eph\u00e9m\u00e8res ne le sont pas vraiment !<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00c0 cot\u00e9 des messageries ayant aujourd&rsquo;hui pignon sur rue comme WhatsApp, Signal, iMessage ou Telegram, on peut trouver sur Google Play tout un maquis d&rsquo;applications similaires mais plus confidentielles. J&rsquo;ai choisi de m&rsquo;int\u00e9resser \u00e0 une de ses applications, SeaTalk : Au moment de la r\u00e9daction de cet article (fin 2024\/d\u00e9but 2025), l&rsquo;application a fait l&rsquo;objet [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-246","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/246","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/comments?post=246"}],"version-history":[{"count":2,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/246\/revisions"}],"predecessor-version":[{"id":279,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/246\/revisions\/279"}],"wp:attachment":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/media?parent=246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/categories?post=246"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/tags?post=246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}