Decrypting .eslock files

I was feeling nostalgic the other day so I decided to take a trip down memory lane by browsing some old entries on my phone. To my surprise, I found a few encrypted files that I don't remember putting there. After trying a few passwords that made sense, it quickly became apparent that a mental dictionary-based brute force approach was not the way to go.

I turned to Google in search of answers. The first pages mention an app that can decrypt .eslock files, and while it looked promising, I wasn't sure it would be smart to trust a black box of an app with data that I once deemed private enough to encrypt. I had to find another way.

This blog post in particular caught my eye. It states that the password of an encrypted file is hashed with the MD5 algorithm then stored within the file itself ! Since MD5 is considered to be obsolete, surely I would be able to un-hash the password if I were to get my hands on it. With no pointers on how an .eslock file is structured, I figured I'd open it in a hexadecimal editor and see where it goes from there.

I first created an empty file on my phone that I then encrypted using "foo" as a password. Since I know that the MD5 hash of "foo" is acbd18db4cc2f85cedef654fccc4a4d8, all I had to do was look for it in the empty file.

I copied the file over to my laptop then opened it in Vim. I then switched to the hexadecimal mode by entering the :%!xxd command. The hash was apparent at first glance, as you can see in the following screenshot :

It appears that the hash is preceded with two bytes : 0xff and 0x10. But was this always going to be the case ? I was able to confirm this hypothesis after inspecting a couple of other files in the hexadecimal editor. With this in mind, I concluded that in order to extract the password from the .eslock file, all I had to do was find the last occurrence of 0xff10 then take the following 16 bytes. I wrote the following D utility to automate this procedure :

import std.stdio;
import std.file : read;

void main(string[] args)
{
    ubyte[] data = cast(ubyte[]) args[1].read;
    int idx = data.findHash;
    if(idx == -1 || idx >= data.length)
    {
        writeln("Couldn't find the hash.");
        return;
    }
    foreach(i; idx .. idx + 16)
        writef("%x", data[i]);
}

int findHash(const ref ubyte[] data)
{
    foreach_reverse(idx, chunk; data)
        if(idx - 1 >= 0 && data[idx] == 0x10 && data[idx - 1] == 0xff)
            return idx + 1;
    return -1;
}

And that's it. It's far from being optimal but it does the trick. Keep in mind that I'm using an old (3.2.4.1) version of the ES file explorer app, so I wouldn't be surprised if they changed this behavior in newer versions. Update of June 1st 2017 : I tried it with the 4.1.6.4 version of the software and it still works.

I was able to un-hash the password thanks to this website. I assume it contains a database of clear <> hashed key-value pairs, which would mean that someone other than myself used a password that I thought was unique. What a small world !

As to the file itself, I was surprised to find out that its content was actually...

dramatic pause

This blog post.

A glitch in the matrix perhaps ?

Edit : Further testing has made me realize that the hash is always 29 bytes away from the EOF. With that in mind, the following function will extract the hash without loading the entire file in memory :

import std.digest.md;
import std.stdio;
//...
string getHash(const ref string path)
{
    auto fh = File(path, "rb");
    fh.seek(-29, SEEK_END);
    ubyte[16] hash;
    fh.rawRead(hash);
    return hash.toHexString.idup;
}

Commentaires

Posts les plus consultés de ce blog

Porting a Golang and Rust CLI tool to D

Résoudre le problème de téléversement avec Arduino Nano sous Windows 7