{"id":403,"date":"2026-09-11T21:58:26","date_gmt":"2026-09-11T21:58:26","guid":{"rendered":"https:\/\/tolva.fr\/?p=403"},"modified":"2026-09-11T21:58:26","modified_gmt":"2026-09-11T21:58:26","slug":"keepassdx","status":"publish","type":"post","link":"https:\/\/tolva.fr\/index.php\/2026\/09\/11\/keepassdx\/","title":{"rendered":"KeepassDX"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">KeepassDX est un gestionnaire de mot de passe, c&rsquo;est-\u00e0-dire l&rsquo;\u00e9quivalent num\u00e9rique d&rsquo;un coffre-fort : vous y mettez tous vos mots de passe et KeepassDX les retient pour vous !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le seul mot de passe \u00e0 m\u00e9moriser est le mot de passe du coffre-fort en question, c&rsquo;est-\u00e0-dire la base de donn\u00e9e KeepassDX.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Utilisant quotidiennement le gestionnaire de mot de passe keepassDX sur mon t\u00e9l\u00e9phone, j&rsquo;ai cherch\u00e9 \u00e0 comprendre comment cette application prot\u00e8ge mes mots de passe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nous allons donc regarder de pr\u00e8s le fonctionnement de ce petit outil.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nous \u00e9tudions la version 4.5.2 de keepassDX. Pour cela, nous cr\u00e9eons une base de donn\u00e9e avec le contenu suivant :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>+-----------------+-------------+-----------------+----------------------+\n|      Titre      | Utilisateur |  Mot de passe   |          URL         |\n+-----------------+-------------+-----------------+----------------------+\n|     poulet      |   cotcot    | Vitcotcodec123! |  https:\/\/poulet.fr   |\n+-----------------+-------------+-----------------+----------------------+\n| soci\u00e9t\u00e9 secr\u00e8te |   zorglub   |   Zorglub123!   | https:\/\/zorglub.info |\n+-----------------+-------------+-----------------+----------------------+\n|     semoule     |   couscous  |      Fr!te      |                      |\n+-----------------+-------------+-----------------+----------------------+<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le mot de passe-ma\u00eetre sera \u00ab\u00a0Plouf123!\u00a0\u00bb.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nous cr\u00e9ons cette base de donn\u00e9e tout de suite apr\u00e8s l&rsquo;installation de l&rsquo;application.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une pratique de s\u00e9curit\u00e9 usuelle dans une application android est d&rsquo;interdire la prise de capture d&rsquo;\u00e9cran des activit\u00e9s exposant une donn\u00e9e sensible. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si KeepassDX interdit bien les captures d&rsquo;\u00e9cran des mots de passe stock\u00e9s dans la base de donn\u00e9e, il est amusant de noter qu&rsquo;il reste possible de prendre une capture d&rsquo;\u00e9cran du choix du master password (alors que c&rsquo;est le mot de passe le plus important !).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"486\" height=\"1080\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-486x1080.png\" alt=\"\" class=\"wp-image-404\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-486x1080.png 486w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-135x300.png 135w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-768x1707.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-691x1536.png 691w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password-922x2048.png 922w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/screenshot_master_password.png 1080w\" sizes=\"auto, (max-width: 486px) 100vw, 486px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Premi\u00e8res manipulations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 ce stade nous n&rsquo;avons aucune id\u00e9e du fonctionnement de la logique de chiffrement de la base de donn\u00e9e. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il est donc possible qu&rsquo;elle implique un secret stock\u00e9 dans une enclave mat\u00e9rielle du t\u00e9l\u00e9phone, ou plus simplement, un secret stock\u00e9 dans un fichier de configuration de l&rsquo;application. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un bon moyen de trancher cette question est de tenter d&rsquo;ouvrir la base de donn\u00e9e sur un autre terminal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Commen\u00e7ons par retrouver la base de donn\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # find \/data -name keepass.kdbx -exec md5sum {} \\; 2&gt;\/dev\/null\n5855166608dcfee3ecd82ef3ff21a374  \/data\/media\/0\/Download\/keepass.kdbx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On la rappatrie ensuite par un simple <code>adb pull<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il s&rsquo;av\u00e8re qu&rsquo;il est bien possible d&rsquo;ouvrir la base de donn\u00e9e sur un pc :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/db_open_on_pc.png\" alt=\"\" class=\"wp-image-405\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/db_open_on_pc.png 1920w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/db_open_on_pc-300x169.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/db_open_on_pc-768x432.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/db_open_on_pc-1536x864.png 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Le chiffrement de la base de donn\u00e9e repose donc uniquement sur le mot de passe et \u00e9ventuellement des \u00e9l\u00e9ments stock\u00e9s en clair dans la base de donn\u00e9e.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Examen sur t\u00e9l\u00e9phone<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Regardons un peu l&rsquo;application depuis un shell adb sur un t\u00e9l\u00e9phone root\u00e9 :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On commence par identifier l&rsquo;uid de l&rsquo;application :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>lynx:\/ # ps -elf | grep -i keepass | grep -v grep\nu0_a286      13372  1013 1 19:44:07 ?     00:00:54 com.kunzisoft.keepass.free\nlynx:\/ # ps -elfn | grep -i keepass | grep -v grep \n10286        13372  1013 1 19:44:06 ?     00:00:54 com.kunzisoft.keepass.free<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Chose qui nous permet de localiser les donn\u00e9es de l&rsquo;application :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lynx:\/ # find \/data\/data\/ -type d -user u0_a286 2&gt;\/dev\/null\n\/data\/data\/com.kunzisoft.keepass.free\n\/data\/data\/com.kunzisoft.keepass.free\/cache\n\/data\/data\/com.kunzisoft.keepass.free\/cache\/oat_primary\n\/data\/data\/com.kunzisoft.keepass.free\/cache\/oat_primary\/arm64\n\/data\/data\/com.kunzisoft.keepass.free\/code_cache\n\/data\/data\/com.kunzisoft.keepass.free\/shared_prefs\n\/data\/data\/com.kunzisoft.keepass.free\/databases\n\/data\/data\/com.kunzisoft.keepass.free\/files\n\/data\/data\/com.kunzisoft.keepass.free\/no_backup<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On r\u00e9cup\u00e8re ensuite cette base de donn\u00e9e (toujours via <code>adb pull<\/code>), afin de la regarder de plus pr\u00e8s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On trouve une base de donn\u00e9e sqlite<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">(fichier <code>\/data\/data\/com.kunzisoft.keepass.free\/databases\/com.kunzisoft.keepass.database<\/code>), <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">que l&rsquo;on examine avec <code>sqlitebrower<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On y trouve une table file_database_history : <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1248\" height=\"262\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/file_database_history.png\" alt=\"\" class=\"wp-image-407\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/file_database_history.png 1248w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/file_database_history-300x63.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/file_database_history-768x161.png 768w\" sizes=\"auto, (max-width: 1248px) 100vw, 1248px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Une table room_master_table : <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1073\" height=\"227\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/room_master_table.png\" alt=\"\" class=\"wp-image-408\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/room_master_table.png 1073w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/room_master_table-300x63.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/room_master_table-768x162.png 768w\" sizes=\"auto, (max-width: 1073px) 100vw, 1073px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 premi\u00e8re vue, ces deux tables ne contiennent rien de tr\u00e8s int\u00e9ressant.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deux autres tables, cipher_database et android_metadata, vides, sont pr\u00e9sentes dans le fichier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deux fichiers xml sont aussi contenus dans les shared_prefs :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ ls -l\n\/data\/data\/com.kunzisoft.keepass.free\/shared_prefs:\ntotal 8\n-rw-rw---- 1 u0_a286 u0_a286 1259 2026-08-19 19:49 com.kunzisoft.keepass.free_preferences.xml\n-rw-rw---- 1 u0_a286 u0_a286  552 2026-08-19 19:48 kdbxeducation.xml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le premier semble porter la configuration utilisateur de l&rsquo;application :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ cat shared_prefs\/com.kunzisoft.keepass.free_preferences.xml \n&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;\n&lt;map&gt;\n    &lt;boolean name=\"lock_database_back_root_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_username_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_recycle_bin_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"clipboard_otp_notifications_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_regex_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_password_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"read_only_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"show_recent_files_key\" value=\"true\" \/&gt;\n    &lt;set name=\"search_option_selected_tags_key\" \/&gt;\n    &lt;string name=\"app_timeout_key\"&gt;300000&lt;\/string&gt;\n    &lt;boolean name=\"lock_database_show_button_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_other_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"user_verification_mode_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_url_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_otp_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_templates_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"remember_hardware_key_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"remember_keyfile_locations_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_tag_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"enable_screenshot_mode_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"allow_no_password_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"enable_auto_save_database_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"settings_credential_provider_enable_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"magic_keyboard_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_case_sensitive_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_application_id_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"remember_database_locations_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_uuid_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_title_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_expired_key\" value=\"true\" \/&gt;\n    &lt;string name=\"clip_timeout_key\"&gt;20000&lt;\/string&gt;\n    &lt;boolean name=\"hide_broken_locations_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"search_option_searchable_group_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"enable_keep_screen_on_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"user_verification_device_credential_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"subdomain_search_key\" value=\"false\" \/&gt;\n    &lt;long name=\"timeout_backup_key\" value=\"1788462778608\" \/&gt;\n    &lt;boolean name=\"search_option_current_group_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"auto_focus_search_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"lock_database_screen_off_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"search_option_note_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"allow_copy_password_first_time_key\" value=\"false\" \/&gt;\n    &lt;boolean name=\"delete_entered_password_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"allow_copy_password_key\" value=\"true\" \/&gt;\n&lt;\/map&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le deuxi\u00e8me ne contient lui non plus rien qui nous int\u00e9resse :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ cat shared_prefs\/kdbxeducation.xml \n&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;\n&lt;map&gt;\n    &lt;boolean name=\"education_lock_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_create_db_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_search_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_biometric_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_unlock_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_select_db_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_read_only_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_entry_new_field_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_password_generator_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_entry_edit_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_user_verification_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_add_attachment_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_new_node_key\" value=\"true\" \/&gt;\n    &lt;boolean name=\"education_sort_key\" value=\"true\" \/&gt;\n&lt;\/map&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reste un fichier, <code>profileInstalled<\/code>, qui ne semble pas contenir grand chose non plus :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ hexdump -vC files\/profileInstalled \n00000000  00 00 00 01 00 00 00 01  00 00 01 a0 73 e3 32 53  |............s.2S|\n00000010  00 00 00 00 00 00 00 00                           |........|\n00000018<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">L&rsquo;\u00e9tude de la sandbox de l&rsquo;application ne donne donc rien, nous allons continuer en m\u00e9lant instrumentation dynamique avec frida, analyse du code-source et lecture de la sp\u00e9cification du format des bases de donn\u00e9e keepass.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Analyse avec frida, documentation et code-source<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">KeepassDX chiffre et d\u00e9chiffre des donn\u00e9es. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une m\u00e9thode d&rsquo;analyse qui n&rsquo;est peut-\u00eatre pas particuli\u00e8rement raffin\u00e9e mais est tout de m\u00eame relativement efficace est de commencer par tracer un peu au hasard les m\u00e9thodes java r\u00e9alisant des op\u00e9rations cryptographiques. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L&rsquo;important est d&rsquo;arriver \u00e0 trouver une ou des fonctions qui manipulent des donn\u00e9es int\u00e9ressantes (typiquement le mot de passe ma\u00eetre, ou bien, \u00e0 l&rsquo;autre bout de la cha\u00eene, les mots de passe d\u00e9chiffr\u00e9s). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les fonctions ainsi identifi\u00e9es serviront de point d&rsquo;entr\u00e9e pour comprendre un peu mieux le code-source de l&rsquo;application.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On commence donc par tenter de tracer les m\u00e9thodes AES-quelque chose (apr\u00e8s tout, il est plus que probable que keepass fasse du d\u00e9chiffrement de donn\u00e9e, d\u00e9chiffrement qui impliquera probablement AES) en lan\u00e7ant <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>frida-trace -U -p 10211 -j '*!*AES*'<\/code> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">(10211 \u00e9tant alors le pid de l&rsquo;application). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rien de particuli\u00e8rement explicite ne se d\u00e9gage des donn\u00e9es dump\u00e9es par frida-trace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une autre hypoth\u00e8se naturelle est que keepassDX va tr\u00e8s certainement d\u00e9river une cl\u00e9 sym\u00e9trique \u00e0 partir du mot de passe ma\u00eetre. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une telle op\u00e9ration fera probablement intervenir une fonction de hachage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On lance donc un filet relativement l\u00e2che en ex\u00e9cutant :<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>frida-trace -U -p 10211 -j '*!*256*'<\/code> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">qui pourrait capturer les appels \u00e0 SHA256. Cette commande donne un retour assez volumineux, sur lequel on reviendra.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>frida-trace -U -p 10211 -j '*!*512*'<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"> ne donne \u00e0 contrario par grand chose, et <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">frida-trace -U -p 10211 -j &lsquo;*!<em>128<\/em>&lsquo; non plus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un premier coup d&rsquo;oeil \u00e0 la sp\u00e9cification du format de base de donn\u00e9e kdbx (<a href=\"https:\/\/keepass.info\/help\/kb\/kdbx.html\">https:\/\/keepass.info\/help\/kb\/kdbx.html<\/a>) laisse penser qu&rsquo;une \u00e9tape de chiffrement avec Salsa20 ou Chach20 pourrait \u00eatre impliqu\u00e9e, m\u00eame si \u00e0 ce stade on ne sait pas si le format d\u00e9crit dans ce document est bien le format de la base de donn\u00e9e qui nous int\u00e9resse ici.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On trace donc les appels potentiels \u00e0 Salsa20 :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ frida-trace -U -p 10211 -j '*!*alsa*'\nInstrumenting...\nSalsa20Engine.salsaCore: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/org.bouncycastle.crypto.engines.Salsa20Engine\/salsaCore.js\"\nHashManager.getSalsa20: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.encrypt.HashManager\/getSalsa20.js\"\nStarted tracing 2 functions. Web UI available at http:\/\/localhost:43229\/\n           \/* TID 0x281d *\/\n 17978 ms  HashManager.getSalsa20(&#91;-18,-9,63,44,-29,80,-75,75,65,89,-26,-43,125,-61,69,1,-9,124,109,-44,-32,70,121,61,8,-45,66,-55,-116,-66,-82,-31])\n 17979 ms     | Salsa20Engine.salsaCore(20, &#91;1634760805,1671086938,-154089819,-1048709155,-1009904921,857760878,1258893544,710746263,0,0,2036477234,-534980242,-1657576997,2017375097,1057842232,1797285236], &#91;0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])\n 17984 ms  &lt;= \"&lt;instance: com.kunzisoft.encrypt.StreamCipher&gt;\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cet appel nous donne un nom de classe, <code>com.kunzisoft.encrypt.StreamCipher<\/code>, que l&rsquo;on garde \u00e9galement de cot\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Revenons sur le r\u00e9sultat de <code>frida-trace -U -p 10211 -j '*!*256*'<\/code>, <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">qui paraissait prometteur. Il est temps de r\u00e9cup\u00e9rer le code-source de l&rsquo;application (autant profiter du fait qu&rsquo;elle soit open-source). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On clone donc le d\u00e9p\u00f4t <code>https:\/\/github.com\/Kunzisoft\/KeePassDX<\/code>, et l&rsquo;on se met sur le tag 4.5.2 qui correspond \u00e0 la version \u00e9tudi\u00e9e.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Une recherche de \u00ab\u00a0256\u00a0\u00bb donne plusieurs centaines de r\u00e9sultats. Si l&rsquo;on exclut de ce r\u00e9sultat volumineux :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>tout les fichiers qui ne contiennent pas de code-source<\/li>\n\n\n\n<li>les fichiers contenant du code de test<\/li>\n\n\n\n<li>les fichiers contenant du code natif<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">On aboutit \u00e0 un nombre plus raisonnable de r\u00e9sultats. On raffine cette recherche en recherchant \u00ab\u00a0sha_256\u00a0\u00bb, \u00ab\u00a0sha-256\u00a0\u00bb et \u00ab\u00a0sha256\u00a0\u00bb. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tout ceci finit par nous orienter sur une fonction retrievePasswordKey pr\u00e9sente dans le fichier fichier <code>MasterCredential.kt<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>\/**\n * Retrieve the key from a password.\n * @param key The password to hash.\n * @param encoding The character encoding to use.\n * @return The SHA-256 hash of the password.\n * @throws IOException If the encoding or hashing fails.\n *\/\n @Throws(IOException::class)\n fun retrievePasswordKey(\n    key: CharArray,\n    encoding: Charset\n ): ByteArray {\n      val byteBuffer = encoding.encode(CharBuffer.wrap(key))\n      val bKey = ByteArray(byteBuffer.remaining())\n      byteBuffer.get(bKey)\n\n      val hash = HashManager.sha256(bKey)\n      bKey.clear()\n      if (byteBuffer.hasArray()) {\n          byteBuffer.array().clear()\n      }\n      return hash\n }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Le commentaire avant la fonction indique que le premier argument de cette m\u00e9thode est le mot de passe, visiblement le mot de passe ma\u00eetre, ce qui est de tr\u00e8s bon augure !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tra\u00e7ons donc cette m\u00e9thode :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ frida-trace -U -p 10211 -j '*!*retrievePasswordKey*'\nInstrumenting...\nMasterCredential$CREATOR.retrievePasswordKey: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.keepass.database.element.MasterCredential_CREATOR\/retrievePasswordKey.js\"\nStarted tracing 1 function. Web UI available at http:\/\/localhost:46481\/ \n           \/* TID 0x281f *\/\n  9784 ms  MasterCredential$CREATOR.retrievePasswordKey(&#91;\"P\",\"l\",\"o\",\"u\",\"f\",\"1\",\"2\",\"3\",\"!\"], \"&lt;instance: java.nio.charset.Charset, $className: com.android.icu.charset.CharsetICU&gt;\")\n  9785 ms  &lt;= &#91;-67,44,125,121,69,78,58,-88,-98,55,-105,-36,-19,109,58,25,-92,101,-105,125,-52,-41,-71,74,46,3,-115,-115,12,64,-27,36]\n  9786 ms  MasterCredential$CREATOR.retrievePasswordKey(&#91;\"P\",\"l\",\"o\",\"u\"], \"&lt;instance: java.nio.charset.Charset, $className: com.android.icu.charset.CharsetICU&gt;\")\n  9787 ms  &lt;= &#91;89,-64,-116,-43,84,122,-84,-49,90,-100,-47,-5,10,-115,75,108,-16,-77,-4,-83,-11,109,-38,-103,-63,12,-59,-15,-70,-14,-11,-45]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cette m\u00e9thode re\u00e7oit bien le mot de passe ma\u00eetre en entr\u00e9e !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le site cyberchef (<code>https:\/\/gchq.github.io\/CyberChef\/#recipe=Encode_text('UTF-8%20(65001)')SHA2('256',64,160)&amp;input=UGxvdWYxMjMh)<\/code>) <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">nous permet de conclure que le r\u00e9sultat retourn\u00e9 par retrievePasswordKey est le hach\u00e9 du mot de passe lorsque ce dernier est encod\u00e9 en UTF-8.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c0 ce stade, nous avons r\u00e9ussi une premi\u00e8re \u00e9tape d\u00e9cisive, trouver une extr\u00e9mit\u00e9 de la pelote. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On pourrait d\u2019ores et d\u00e9j\u00e0 commencer \u00e0 la d\u00e9rouler, mais nous avons vu plus haut que salsa20 semble utilis\u00e9 d&rsquo;une mani\u00e8re ou d&rsquo;une autre, et que cela implique une classe <code>com.kunzisoft.encrypt.StreamCipher<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>package com.kunzisoft.encrypt\n\nimport org.bouncycastle.crypto.CipherParameters\nimport org.bouncycastle.crypto.DataLengthException\n\n\/**\n * Stream cipher to process data\n *\/\nclass StreamCipher(private val streamCipher: org.bouncycastle.crypto.StreamCipher) {\n    \/**\n     * Initialise the cipher.\n     *\n     * @param forEncryption if true the cipher is initialised for\n     * encryption, if false for decryption.\n     * @param params the key and other data required by the cipher.\n     * @exception IllegalArgumentException if the params argument is\n     * inappropriate.\n     *\/\n    @Throws(IllegalArgumentException::class)\n    fun init(forEncryption: Boolean, params: CipherParameters?) {\n        streamCipher.init(forEncryption, params)\n    }\n\n    \/**\n     * process a block of bytes from in putting the result into out.\n     *\n     * @param data the input byte array.\n     * @return the output buffer.\n     * @exception DataLengthException if the output buffer is too small.\n     *\/\n    @Throws(DataLengthException::class)\n    fun processBytes(data: ByteArray): ByteArray {\n        val size = data.size\n        val out = ByteArray(size)\n        streamCipher.processBytes(data, 0, size, out, 0)\n        return out\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On y voit une m\u00e9thode processBytes, nnous qui sugg\u00e8re une m\u00e9thode chiffrant ou d\u00e9chiffrant des donn\u00e9es. Nous allons donc tracer cette m\u00e9thode :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>$ frida-trace -U -p 10058 -j '*!*deriveMasterKey*' -j '*!*alsa20*' -j '*!*processBytes*' \nInstrumenting...\nDatabaseKDB.deriveMasterKey: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.keepass.database.element.database.DatabaseKDB\/deriveMasterKey.js\"\nDatabaseKDBX.deriveMasterKey: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.keepass.database.element.database.DatabaseKDBX\/deriveMasterKey.js\"\nHashManager.getSalsa20: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.encrypt.HashManager\/getSalsa20.js\"\nSalsa20Engine.processBytes: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/org.bouncycastle.crypto.engines.Salsa20Engine\/processBytes.js\"\nStreamCipher.processBytes: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/org.bouncycastle.crypto.StreamCipher\/processBytes.js\"\nStreamCipher.processBytes: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.kunzisoft.encrypt.StreamCipher\/processBytes.js\"\nAEADCipher.processBytes: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.crypto.modes.AEADCipher\/processBytes.js\"\nBufferedBlockCipher.processBytes: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.crypto.BufferedBlockCipher\/processBytes.js\"\nBaseBlockCipher$AEADGenericBlockCipher.processBytes: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher_AEADGenericBlockCipher\/processBytes.js\"\nBaseBlockCipher$BufferedGenericBlockCipher.processBytes: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher_BufferedGenericBlockCipher\/processBytes.js\"\nPaddedBufferedBlockCipher.processBytes: Loaded handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher\/processBytes.js\"\nBaseBlockCipher$GenericBlockCipher.processBytes: Auto-generated handler at \"\/home\/thomas\/articles\/keepassDX\/__handlers__\/com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher_GenericBlockCipher\/processBytes.js\"\nStarted tracing 12 functions. Web UI available at http:\/\/localhost:46517\/\n           \/* TID 0x277c *\/\n  8602 ms  DatabaseKDBX.deriveMasterKey(\"&lt;instance: com.kunzisoft.keepass.database.element.MasterCredential&gt;\", \"&lt;instance: kotlin.jvm.functions.Function2, $className: com.kunzisoft.keepass.services.DatabaseTaskNotificationService$$ExternalSyntheticLambda1&gt;\")\n  8672 ms  HashManager.getSalsa20(&#91;-18,-9,63,44,-29,80,-75,75,65,89,-26,-43,125,-61,69,1,-9,124,109,-44,-32,70,121,61,8,-45,66,-55,-116,-66,-82,-31])\n  8675 ms  &lt;= \"&lt;instance: com.kunzisoft.encrypt.StreamCipher&gt;\"\n  8678 ms  StreamCipher.processBytes(&#91;28,-57,23,113,5,-72,-116,116,-46,1,2,79,0,52,120])\n  8678 ms     | Salsa20Engine.processBytes(&#91;28,-57,23,113,5,-72,-116,116,-46,1,2,79,0,52,120], 0, 15, &#91;0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 0)\n  8679 ms     | &lt;= 15\n  8679 ms  &lt;= &#91;86,105,116,99,111,116,99,111,100,101,99,49,50,51,33]\n  8680 ms  StreamCipher.processBytes(&#91;])\n  8680 ms     | Salsa20Engine.processBytes(&#91;], 0, 0, &#91;], 0)\n  8680 ms     | &lt;= 0\n  8680 ms  &lt;= &#91;]\n  8681 ms  StreamCipher.processBytes(&#91;-117,-50,-27,-18,-103,68,67,-3,-7,-114,84])\n  8681 ms     | Salsa20Engine.processBytes(&#91;-117,-50,-27,-18,-103,68,67,-3,-7,-114,84], 0, 11, &#91;0,0,0,0,0,0,0,0,0,0,0], 0)\n  8681 ms     | &lt;= 11\n  8681 ms  &lt;= &#91;90,111,114,103,108,117,98,49,50,51,33]\n  8681 ms  StreamCipher.processBytes(&#91;])\n  8682 ms     | Salsa20Engine.processBytes(&#91;], 0, 0, &#91;], 0)\n  8682 ms     | &lt;= 0\n  8682 ms  &lt;= &#91;]\n  8682 ms  StreamCipher.processBytes(&#91;76,95,98,-81,30])\n  8682 ms     | Salsa20Engine.processBytes(&#91;76,95,98,-81,30], 0, 5, &#91;0,0,0,0,0], 0)\n  8682 ms     | &lt;= 5\n  8682 ms  &lt;= &#91;70,114,33,116,101]\n  8683 ms  StreamCipher.processBytes(&#91;])\n  8683 ms     | Salsa20Engine.processBytes(&#91;], 0, 0, &#91;], 0)\n  8683 ms     | &lt;= 0\n  8683 ms  &lt;= &#91;]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Les tableaux retourn\u00e9s par processBytes contiennent des octets dont la valeur (ici en d\u00e9cimal) est l&rsquo;encodage ascii des caract\u00e8res des mots de passe pr\u00e9sents dans la base de donn\u00e9e : Cette fonction d\u00e9chiffre les mots de passe !<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nous avions d\u00e9j\u00e0 identifi\u00e9 le premier bout de la pelote, la fonction recevant le mot de passe ma\u00eetre. Nous tenons maintenant la deuxi\u00e8me extr\u00e9mit\u00e9, la fonction qui retourne les mots de passe d\u00e9chiffr\u00e9s.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">R\u00e9sultats<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">De fil en aiguille, on reconstitue la quasi-totalit\u00e9 du processus de d\u00e9chiffrement de la base de donn\u00e9e KeepassDX et de son format.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le processus de d\u00e9chiffrement est le suivant :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Calcul du hach\u00e9 SHA256 du mot de passe.<\/li>\n\n\n\n<li>Ce hach\u00e9 est concat\u00e9n\u00e9 avec les autres secrets initiaux \u00e9ventuellement fournis par un fichier et\/ou par une enclave materielle. Cette concat\u00e9nation est hach\u00e9e avec SHA256.<\/li>\n\n\n\n<li>Le hach\u00e9 obtenu est trait\u00e9 par une KDF. Cette KDF ex\u00e9cute un certain nombre d&rsquo;it\u00e9rations d&rsquo;AES en mode ECB, la cl\u00e9 de chiffrement provenant d&rsquo;un en-t\u00eate pr\u00e9sent au d\u00e9but de la base de donn\u00e9e (header de type 5). La premi\u00e8re it\u00e9ration chiffre le hach\u00e9, les suivantes chiffrent le r\u00e9sultat de l&rsquo;it\u00e9ration pr\u00e9c\u00e9dente. Le nombre d&rsquo;i\u00e9rations effectu\u00e9es est sp\u00e9cifi\u00e9 par le header de type6. Ici, 500000 it\u00e9rations sont effectu\u00e9es, ce qui est le nombre par d\u00e9faut pr\u00e9sent dans le code.<\/li>\n\n\n\n<li>La KDF se termine par un appel \u00e0 SHA256 du r\u00e9sultat des it\u00e9rations AES. Ce hach\u00e9 est concat\u00e9n\u00e9 avec le contenu du header de type 4 et un nouvel appel \u00e0 SHA256 est pass\u00e9 sur cette concat\u00e9nation.<\/li>\n\n\n\n<li>La cl\u00e9 AES256 ainsi obtenue est utilis\u00e9e pour d\u00e9chiffrer le contenu proprement dit de la base de donn\u00e9e. Le mode op\u00e9ratoire est CBC, le vecteur d&rsquo;initialisation provenant de l&rsquo;en-t\u00eate de type 7.<\/li>\n\n\n\n<li>Le contenu d\u00e9chiffr\u00e9 est d\u00e9compress\u00e9 avec l&rsquo;algorithme de d\u00e9compression sp\u00e9cifi\u00e9 par l&rsquo;en-t\u00eate n\u00b03 de la base de donn\u00e9e. On retrouve alors un fichier XML, contenant des noeuds <code>Entry<\/code>.<\/li>\n\n\n\n<li>Chaque mot de passe de la base de donn\u00e9e fait l&rsquo;objet d&rsquo;un noeud <code>Entry<\/code>. Un tel noeud contient un mot de passe chiffr\u00e9 puis encod\u00e9 en base64, le nom d&rsquo;utilisateur et l&rsquo;url associ\u00e9s, ainsi que diverses m\u00e9tadonn\u00e9es.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Voici un exemple de noeud <code>Entry<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>&lt;Entry&gt;\n &lt;UUID&gt;1kJz\/BdODNzUaW8O0U7CiQ==&lt;\/UUID&gt;\n &lt;IconID&gt;0&lt;\/IconID&gt;\n &lt;ForegroundColor&gt;&lt;\/ForegroundColor&gt;\n &lt;BackgroundColor&gt;&lt;\/BackgroundColor&gt;\n &lt;OverrideURL&gt;&lt;\/OverrideURL&gt;\n &lt;Times&gt;\n  &lt;LastModificationTime&gt;2026-08-19T17:47:44Z&lt;\/LastModificationTime&gt;\n  &lt;CreationTime&gt;2026-08-19T17:46:59Z&lt;\/CreationTime&gt;\n  &lt;LastAccessTime&gt;2026-08-19T17:47:44Z&lt;\/LastAccessTime&gt;\n  &lt;ExpiryTime&gt;2999-11-28T23:59:59Z&lt;\/ExpiryTime&gt;\n  &lt;Expires&gt;False&lt;\/Expires&gt;\n  &lt;UsageCount&gt;2&lt;\/UsageCount&gt;\n  &lt;LocationChanged&gt;2026-08-19T17:47:44Z&lt;\/LocationChanged&gt;\n &lt;\/Times&gt;\n &lt;String&gt;\n  &lt;Key&gt;Title&lt;\/Key&gt;\n  &lt;Value&gt;poulet&lt;\/Value&gt;\n &lt;\/String&gt;\n &lt;String&gt;\n  &lt;Key&gt;UserName&lt;\/Key&gt;\n  &lt;Value&gt;cotcot&lt;\/Value&gt;\n &lt;\/String&gt;\n &lt;String&gt;\n  &lt;Key&gt;Password&lt;\/Key&gt;\n  &lt;Value Protected=\"True\"&gt;HMcXcQW4jHTSAQJPADR4&lt;\/Value&gt;\n &lt;\/String&gt;\n &lt;String&gt;\n  &lt;Key&gt;URL&lt;\/Key&gt;\n  &lt;Value&gt;https:\/\/poulet.fr&lt;\/Value&gt;\n &lt;\/String&gt;\n &lt;String&gt;\n  &lt;Key&gt;Notes&lt;\/Key&gt;\n  &lt;Value Protected=\"True\"&gt;&lt;\/Value&gt;\n &lt;\/String&gt;\n &lt;AutoType&gt;\n  &lt;Enabled&gt;True&lt;\/Enabled&gt;\n  &lt;DataTransferObfuscation&gt;0&lt;\/DataTransferObfuscation&gt;\n &lt;\/AutoType&gt;\n &lt;History \/&gt;\n&lt;\/Entry&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pour retrouver le mot de passe en question (\u00ab\u00a0Vitcotcodec123!\u00a0\u00bb), il suffit de d\u00e9coder en base64 le contenu de la String \u00ab\u00a0Password\u00a0\u00bb, puis de le d\u00e9chiffrer avec Salsa20.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le d\u00e9chiffrement utilise le SHA256 d&rsquo;une cl\u00e9 pr\u00e9sente dans le header n\u00b08, ainsi qu&rsquo;un nonce fixe pr\u00e9sent dans le code<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>\/**\n * Get a Salsa20 stream cipher instance.\n *\n * @param key Master key to derive the cipher key\n * @return Initialized Salsa20 StreamCipher\n *\/\nfun getSalsa20(key: ByteArray): StreamCipher {\n    \/\/ Build stream cipher key\n    val key32 = sha256(key)\n\n    val keyParam = KeyParameter(key32)\n\n    val salsaIV = byteArrayOf(\n        0xE8.toByte(),\n        0x30,\n        0x09,\n        0x4B,\n        0x97.toByte(),\n        0x20,\n        0x5D,\n        0x2A\n    )\n    val ivParam = ParametersWithIV(keyParam, salsaIV)\n\n    val cipher = Salsa20Engine()\n    cipher.init(true, ivParam)\n\n    return StreamCipher(cipher)\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nous pouvons maintenant d\u00e9gager une vue d&rsquo;ensemble du d\u00e9chiffrement d&rsquo;une base de donn\u00e9e KeepassDX :<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1547\" height=\"714\" src=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/keepassDX.png\" alt=\"\" class=\"wp-image-409\" srcset=\"https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/keepassDX.png 1547w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/keepassDX-300x138.png 300w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/keepassDX-768x354.png 768w, https:\/\/tolva.fr\/wp-content\/uploads\/2026\/09\/keepassDX-1536x709.png 1536w\" sizes=\"auto, (max-width: 1547px) 100vw, 1547px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">On aboutit \u00e9galement \u00e0 un script frida qui trace l&rsquo;int\u00e9gralit\u00e9 des op\u00e9rations impliqu\u00e9es par l&rsquo;ouverture d&rsquo;une base de donn\u00e9e KeepassDX, ainsi qu&rsquo;un script python reproduisant ces calculs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Notons que ce r\u00e9sultat est partiel : La base de donn\u00e9e \u00e9tudi\u00e9e utilise la version 3.1 du format kbdx.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le script python d\u00e9velopp\u00e9 ne peut donc pas en l&rsquo;\u00e9tat ouvrir de base de donn\u00e9e dans d&rsquo;autres versions du format (le plus r\u00e9cent \u00e9tant kbdx 4).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Toutefois, ce format est relativement apparent\u00e9 \u00e0 celui \u00e9tudi\u00e9, et les r\u00e9sultats du pr\u00e9sent article pourront servir de base de d\u00e9part d&rsquo;une analyse de ces autres versions du format kbdx.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Annexes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Les scripts frida et python : <a href=\"https:\/\/github.com\/T0lva\/dekeepass\">https:\/\/github.com\/T0lva\/dekeepass<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>KeepassDX est un gestionnaire de mot de passe, c&rsquo;est-\u00e0-dire l&rsquo;\u00e9quivalent num\u00e9rique d&rsquo;un coffre-fort : vous y mettez tous vos mots de passe et KeepassDX les retient pour vous ! Le seul mot de passe \u00e0 m\u00e9moriser est le mot de passe du coffre-fort en question, c&rsquo;est-\u00e0-dire la base de donn\u00e9e KeepassDX. Utilisant quotidiennement le gestionnaire [&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-403","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/403","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=403"}],"version-history":[{"count":7,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/403\/revisions"}],"predecessor-version":[{"id":415,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/posts\/403\/revisions\/415"}],"wp:attachment":[{"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/media?parent=403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/categories?post=403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolva.fr\/index.php\/wp-json\/wp\/v2\/tags?post=403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}