Tolino 16.0 password crack attempts

Tolino e-book readers have a debug mode that can be activated by entering a secret password into the search field. Every new firmware version changes this password.

Firmware 14.2.0

Download EPubProd.apk from the Tolino with adb pull and open it in jadx-gui. Now open de.telekom.epub.debug.DebugOptionsActivity and you will find the following code:

public static final String SHOW_DEBUG_OPTIONS_PASS_CODE_FULL = "124816";
public static final String SHOW_DEBUG_OPTIONS_PASS_CODE_MINIMAL = "180914";

This was easy.

The "full" passcode are the first 5 numbers of the x² sequence.

Firmware 15.2.0

After downloading the epub reading application EPubProd.apk and opening it in jadx-gui, we find new code in de.telekom.epub.debug.DebugOptionsActivity:

private static final String HASH_SALT = "schokopups512";
public static final String PASS_CODE_FULL = "c5af5651b57b1db0fe9f373e95cd4042997ccfb7bd2c5fa0b0b45ebc7717d83a9b71fb695e22478c1d4e9dd3d85330e3d385c5c49a892972484e45d5533532c6";
public static final String PASS_CODE_MINIMAL = "f11e6ad70202da7eb56f2b932926ef53c4a4b9742bc28e00c3cb7b13d3e39ac795f774417c32c639a4873a044d7bd07dc369b9633a875f61097aa0b9b01e8464";
 
public static int checkOpenDebugCode(String str) {
    try {
        String bigInteger = new BigInteger(1, MessageDigest.getInstance("SHA-512").digest(str.concat(this.HASH_SALT).getBytes())).toString(16);
        while (bigInteger.length() < 32) {
            bigInteger = "0" + bigInteger;
        }
        if (bigInteger.equals(this.PASS_CODE_FULL)) {
            ApplicationPreferencesManager.getInstance().setDebugSettingsLastOpenCode(this.PASS_CODE_FULL);
            return 1;
        } else if (!bigInteger.equals(this.PASS_CODE_MINIMAL)) {
            return -1;
        } else {
            ApplicationPreferencesManager.getInstance().setDebugSettingsLastOpenCode(this.PASS_CODE_MINIMAL);
            return 0;
        }
    } catch (Exception e) {
        e.printStackTrace();
        return -1;
    }
}

The Tolino developers decided to make it more complicated to find the password and found a nice method: They calculate a hash from the combination of the entered password and "schokopups512" and compare the result with the pre-computed hashes of the original passwords.

That way the passwords are not in plain text in the code anymore, and the salt ("schokopups512") prevents people from looking up the hash in a database of pre-computed SHA-512 hashes.

John to the rescue

Luckily there is a tool for cracking passwords: John the Ripper. It takes a file with hashed passwords and tries all combinations of letters, numbers and special characters until it finds the password. Word list files are also supported; passwords contained in them will be found much faster.

At first we need to tell john the password format. The Tolino debug passcode is sha512($password + $hash). john in Debian 11's repository does not support that, but the jumbo version does! I found .deb package files for jumbo-john at http://http.kali.org/pool/main/j/john/. Verification:

$ john --help
John the Ripper 1.9.0-jumbo-1+bleeding-aec1328d6c 2021-11-02 10:45:52 +0100 OMP [linux-gnu 64-bit x86_64 AVX2 AC]

Now let's find out if it supports SHA512 with salt:

$ john --list=subformats|grep sha512
Format = dynamic_80  type = dynamic_80: sha512($p)
Format = dynamic_81  type = dynamic_81: sha512($s.$p)
Format = dynamic_82  type = dynamic_82: sha512($p.$s)
Format = dynamic_83  type = dynamic_83: sha512(sha512($p))
Format = dynamic_84  type = dynamic_84: sha512(sha512_raw($p))
Format = dynamic_85  type = dynamic_85: sha512(sha512($p).$s)
Format = dynamic_86  type = dynamic_86: sha512($s.sha512($p))

dynamic_82 is the format we want.

Now we have to find out how the password needs to be stored. After some trial and error I had the following line format for password files:

name:hash$salt

.. which gives us this password file for the Tolino 15.2.0 main passcode:

tolino152-1:c5af5651b57b1db0fe9f373e95cd4042997ccfb7bd2c5fa0b0b45ebc7717d83a9b71fb695e22478c1d4e9dd3d85330e3d385c5c49a892972484e45d5533532c6$schokopups512

Now we let john do its work:

$ john --incremental=digits --fork=4 --format=dynamic_82 passwords
Using default input encoding: UTF-8
Loaded 1 password hash (dynamic_82 [sha512($p.$s) 256/256 AVX2 4x])
Press 'q' or Ctrl-C to abort, almost any other key for status
1123581321       (tolino152-1)
1g 0:00:00:01 DONE (2022-11-10 22:48) 0.7692g/s 4758Kp/s 4758Kc/s 4758KC/s 1234561990..095664525
Use the "--show --format=dynamic_82" options to display all of the cracked passwords reliably
Session completed.

The first password 1123581321 was found within a second when telling john only to try numbers and no letters. It consists of numbers 2-9 of the Fibonacci numbers.

Firmware 16.0

The password check code is the same as in firmware 15.2:

private static final String HASH_SALT = "CbqSbfnk9YC%";
public static final String PASS_CODE_FULL = "d820afedd912b83340429595ad855b893815bb85661b2b50e0892f7b3d720e6c8ebad365623f8896ba946957c1ff70a3fcf51a2127041804fb17c46c671c37c0";
public static final String PASS_CODE_MINIMAL = "d93b0fb26c52927f543103ab6cf2270947c55c6ed00995018eab313f57393c7c0a1dc4cc4bcb3c114ed0b0c469b1973ddebc4c55002eaf10145ddb626e2595a";
 
public static int checkOpenDebugCode(String str) {
    try {
        String bigInteger = new BigInteger(1, MessageDigest.getInstance("SHA-512").digest(str.concat(this.HASH_SALT).getBytes())).toString(16);
        while (bigInteger.length() < 32) {
            bigInteger = "0" + bigInteger;
        }
        if (bigInteger.equals(this.PASS_CODE_FULL)) {
            ApplicationPreferencesManager.getInstance().setDebugSettingsLastOpenCode(this.PASS_CODE_FULL);
            return 1;
        } else if (!bigInteger.equals(this.PASS_CODE_MINIMAL)) {
            return -1;
        } else {
            ApplicationPreferencesManager.getInstance().setDebugSettingsLastOpenCode(this.PASS_CODE_MINIMAL);
            return 0;
        }
    } catch (Exception e) {
        e.printStackTrace();
        return -1;
    }
}

Let's put all the known hashes in a single file:

tolino152-1:c5af5651b57b1db0fe9f373e95cd4042997ccfb7bd2c5fa0b0b45ebc7717d83a9b71fb695e22478c1d4e9dd3d85330e3d385c5c49a892972484e45d5533532c6$schokopups512
tolino152-2:f11e6ad70202da7eb56f2b932926ef53c4a4b9742bc28e00c3cb7b13d3e39ac795f774417c32c639a4873a044d7bd07dc369b9633a875f61097aa0b9b01e8464$schokopups512
tolino160-1:d820afedd912b83340429595ad855b893815bb85661b2b50e0892f7b3d720e6c8ebad365623f8896ba946957c1ff70a3fcf51a2127041804fb17c46c671c37c0$CbqSbfnk9YC%
tolino160-2:0d93b0fb26c52927f543103ab6cf2270947c55c6ed00995018eab313f57393c7c0a1dc4cc4bcb3c114ed0b0c469b1973ddebc4c55002eaf10145ddb626e2595a$CbqSbfnk9YC%

I let john work on the file with different options but failed in the end. The passwords are not:

Integer sequences

I thought that the second passcode would also be a known number sequence, and so I wanted to get a list of all known sequences. The project On-Line Encyclopedia of Integer Sequences (OEIS) collected them, and they can be downloaded from their wiki.

After extracting the 69 MiB stripped file I mangled it a bit to extract the first 2 numbers from every sequence and stored it in a file:

grep -v '^#' stripped |cut -d',' -f2-3 | sed 's/,//g' | sort | uniq > sort-2-3

This was extended up to fields 2..12, 3..13, 4..14 and 5..15. All of them together were given john as password file, but 3 seconds later I found that the work bore no fruit.

Fin

I tried finding passwords consisting of ASCII characters, but stopped john after it ran for nearly two days on 4 CPU cores:

$ john --fork=4 --format=dynamic_82 --min-length=6 passwords
...
3 0g 1:19:53:51  3/3 0g/s 1242Kp/s 2484Kc/s 3726KC/s bmpa5s9r..bmpa5d1@
4 0g 1:19:53:51  3/3 0g/s 1245Kp/s 2491Kc/s 3736KC/s 2lscie1...2lscidr4
1 0g 1:19:53:51  3/3 0g/s 1246Kp/s 2493Kc/s 3739KC/s rbadwhrhb..rbadwxyza
Waiting for 3 children to terminate
2 0g 1:19:53:51  3/3 0g/s 1243Kp/s 2486Kc/s 3729KC/s 77f1ngy..77f1cs7
Session aborted

A fail for now, but maybe this helps the next tinkerer to go further.

The password

Update 2023-11-15: Someone sent me the link to a gist that contains the Tolino 16 firmware debug password: 112358132fb. Source is this forum post from 2023-08-09 by AaronDewes.

Written by Christian Weiske.

Comments? Please send an e-mail.