Telegram and hashcat.
#1
Lightbulb 
Hello,
Is it possible to crack the Telegram local password with hashcat?

Thank you.
Reply
#2
do you mean this: https://github.com/hashcat/hashcat/issues/1534 ?
The Telegram Android/IOS APP hash ?

We have recently added this format https://github.com/hashcat/hashcat/pull/2282 , it's still in beta: https://hashcat.net/beta/
. You would need to use telegram2john.py to convert the xml file, but also need to make some modification to the output as explained here: https://github.com/hashcat/hashcat/pull/2282

you can always use
Code:
hashcat -m 22301 --example-hashes
to see how the format is and to try to crack the example hashes.

Again, you would need to use the beta version (at the time of this wrinting), because the release version currently doesn't support it.
Reply
#3
(01-16-2020, 11:44 AM)philsmd Wrote: do you mean this: https://github.com/hashcat/hashcat/issues/1534 ?
The Telegram Android/IOS APP hash ?

We have recently added this format https://github.com/hashcat/hashcat/pull/2282 , it's still in beta: https://hashcat.net/beta/
. You would need to use telegram2john.py to convert the xml file, but also need to make some modification to the output as explained here: https://github.com/hashcat/hashcat/pull/2282

you can always use
Code:
hashcat -m 22301 --example-hashes
to see how the format is and to try to crack the example hashes.

Again, you would need to use the beta version (at the time of this wrinting), because the release version currently doesn't support it.
Thank you.
I know "telegram2john.py" can crack the Telegram local password, but I wanted to crack it via hadhcat because it is faster.
Reply
#4
that's what I already told you. The beta version of hashcat has this new hash algorithm support:

Code:
-m 22301 = Telegram client app passcode (SHA256)

This means that with the beta version of hashcat you can use -m 22301 to crack the telegram hash (the format of the hash is similar to john... as I already explained and you can extract the hash with telegram2john.py. I think there is a huge misunderstanding that the extraction script telegram2john is not cracking at all, but just extracting the data !)
Reply
#5
My hash has the format $telegram$1*4000 * xxxxxxxxxxx .... xx * xxxxxxxx ... xxxx. Is it possible to convert it to a form that hashcat supports?
Reply
#6
looks exactly like the format hashcat expects.
Reply
#7
(01-20-2020, 07:07 PM)undeath Wrote: looks exactly like the format hashcat expects.

the hash format that telegram2john.py generates differs from the required.
the hash contains the value localencryptitercount = 4000 , salt and encrypted_key (288byte)






Reply
#8
@zvonko I guess this is not the mobile app you are using, but the Desktop Application.

We had this github issue https://github.com/hashcat/hashcat/issues/1534 and unfortunately nobody mentioned/explained that there are actually 2 formats (and algorithms ?) : https://github.com/magnumripper/JohnTheR...#L177-L179

I must admit that I didn't know anything about this other format and still don't know what the differences are and what those tdfs files are etc Sad
I think somebody needs to research the differences (hash format, input needed, algorithm details, differences compared to the already supported format, ... ) and mention their findings on a new github issue (feature request).
Reply
#9
okay, I just spend a couple of free minutes to come up with this perl script (POC):
telegram_desktop.pl
Code:
#!/usr/bin/env perl

# Author: philsmd
# Date: February 2020
# License: public domain, credits go to philsmd and hashcat

use strict;
use warnings;

use Crypt::PBKDF2;
use Digest::SHA1 qw (sha1);
use Crypt::Mode::ECB;

#
# Constants:
#

my $AES256_IGE_BLOCK_SIZE = 16;

#
# Helper functions:
#

sub exclusive_or
{
  my $in1 = shift;
  my $in2 = shift;

  # MIN () function (should always be 16 for us):
  # my $len = (length ($in1) <= length ($in2)) ? length ($in2) : length ($in1);

  # padding if input not multiple of block size:
  # $in1 .= "\x00" x ($AES256_IGE_BLOCK_SIZE - $len);
  # $in2 .= "\x00" x ($AES256_IGE_BLOCK_SIZE - $len);

  my $out = "";

  for (my $i = 0; $i < $AES256_IGE_BLOCK_SIZE; $i++) # $i < $len
  {
    $out .= chr (ord (substr ($in1, $i, 1)) ^ ord (substr ($in2, $i, 1)));
  }

  return $out;
}

sub aes256_decrypt_ige
{
  my $key = shift;
  my $iv  = shift;
  my $in  = shift;

  my $x_prev = substr ($iv,                      0, $AES256_IGE_BLOCK_SIZE);
  my $y_prev = substr ($iv, $AES256_IGE_BLOCK_SIZE, $AES256_IGE_BLOCK_SIZE);

  my $m = Crypt::Mode::ECB->new ('AES', 0);

  my $out = "";

  for (my $i = 0; $i < length ($in); $i += $AES256_IGE_BLOCK_SIZE)
  {
    my $x = substr ($in, $i, $AES256_IGE_BLOCK_SIZE);

    my $y_xor = exclusive_or ($x, $y_prev);

    my $y_final = $m->decrypt ($y_xor, $key);
    # $y_final .= "\x00" x ($AES256_IGE_BLOCK_SIZE - length ($y_final));

    my $y = exclusive_or ($y_final, $x_prev);

    $x_prev = $x;
    $y_prev = $y;

    $out .= $y;
  }

  return $out;
}

#
# Example:
#

# examples from JTR: https://github.com/magnumripper/JohnTheRipper/blob/b7cea20625354e8a009a3a66e9061792babefef2/src/telegram_common_plug.c#L14-L15

# Example 1:

# $telegram$1*4000*e693c27ff92fe83a5a247cce198a8d6a0f3a89ffedc6bcddbc39586bb1bcb50b*d6fb7ebda06a23a9c42fc57c39e2c3128da4ee1ff394f17c2fc4290229e13d1c9e45c42ef1aee64903e5904c28cffd49498358fee96eb01888f2251715b7a5e71fa130918f46da5a2117e742ad7727700e924411138bb8d4359662da0ebd4f4357d96d1aa62955e44d4acf2e2ac6e0ce057f48fe24209090fd35eeac8a905aca649cafb2aade1ef7a96a7ab44a22bd7961e79a9291b7fea8749dd415f2fcd73d0293cdb533554f396625f669315c2400ebf6f1f30e08063e88b59b2d5832a197b165cdc6b0dc9d5bfa6d5e278a79fa101e10a98c6662cc3d623aa64daada76f340a657c2cbaddfa46e35c60ecb49e8f1f57bc170b8064b70aa2b22bb326915a8121922e06e7839e62075ee045b8c82751defcba0e8fb75c32f8bbbdb8b673258:openwall123

my $iterations = 4000;

my $word = "openwall123";

my $salt = pack ("H*", "e693c27ff92fe83a5a247cce198a8d6a0f3a89ffedc6bcddbc39586bb1bcb50b");
my $data = pack ("H*", "d6fb7ebda06a23a9c42fc57c39e2c3128da4ee1ff394f17c2fc4290229e13d1c9e45c42ef1aee64903e5904c28cffd49498358fee96eb01888f2251715b7a5e71fa130918f46da5a2117e742ad7727700e924411138bb8d4359662da0ebd4f4357d96d1aa62955e44d4acf2e2ac6e0ce057f48fe24209090fd35eeac8a905aca649cafb2aade1ef7a96a7ab44a22bd7961e79a9291b7fea8749dd415f2fcd73d0293cdb533554f396625f669315c2400ebf6f1f30e08063e88b59b2d5832a197b165cdc6b0dc9d5bfa6d5e278a79fa101e10a98c6662cc3d623aa64daada76f340a657c2cbaddfa46e35c60ecb49e8f1f57bc170b8064b70aa2b22bb326915a8121922e06e7839e62075ee045b8c82751defcba0e8fb75c32f8bbbdb8b673258");


# Example 2:

# $telegram$1*4000*e693c27ff92fe83a5a247cce198a8d6a0f3a89ffedc6bcddbc39586bb1bcb50b*7c04a5becb2564fe4400c124f5bb5f1896117327d8a21f610bd431171f606fa6e064c088aacc59d8eae4e6dce539abdba5ea552f5855412c26284bc851465d6b31949b276f4890fc212d63d73e2ba132d6098688f2a6408b9d9d69c3db4bcd13dcc3a5f80a7926bb11eb2c99c7f02b5d9fd1ced974d18ed9d667deae4be8df6a4a97ed8fae1da90d5131a7536535a9bfa8094ca7f7465deabef00ab4c715f151d016a879197b328c74dfad5b1f854217c741cf3e0297c63c3fb4d5d672d1e31d797b2c01cb8a254f80a37b6c9a011d864c21c4145091f22839a52b6daf23ed2f350f1deb275f1b0b4146285ada0f0b168ce54234854b19ec6657ad0a92ffb0f3b86547c8b8cc3655a29797c398721e740ed606a71018d16545c78ee240ff3635:öye

# my $iterations = 4000;
#
# my $word = "öye";
#
# my $salt = pack ("H*", "e693c27ff92fe83a5a247cce198a8d6a0f3a89ffedc6bcddbc39586bb1bcb50b");
# my $data = pack ("H*", "7c04a5becb2564fe4400c124f5bb5f1896117327d8a21f610bd431171f606fa6e064c088aacc59d8eae4e6dce539abdba5ea552f5855412c26284bc851465d6b31949b276f4890fc212d63d73e2ba132d6098688f2a6408b9d9d69c3db4bcd13dcc3a5f80a7926bb11eb2c99c7f02b5d9fd1ced974d18ed9d667deae4be8df6a4a97ed8fae1da90d5131a7536535a9bfa8094ca7f7465deabef00ab4c715f151d016a879197b328c74dfad5b1f854217c741cf3e0297c63c3fb4d5d672d1e31d797b2c01cb8a254f80a37b6c9a011d864c21c4145091f22839a52b6daf23ed2f350f1deb275f1b0b4146285ada0f0b168ce54234854b19ec6657ad0a92ffb0f3b86547c8b8cc3655a29797c398721e740ed606a71018d16545c78ee240ff3635");

#
# Start:
#

my $pbkdf = Crypt::PBKDF2->new
(
  hash_class => 'HMACSHA1',
  iterations => $iterations,
  output_len => 136 # 256
);

my $authkey = $pbkdf->PBKDF2 ($salt, $word);

my $data_a = "\x00" x 48;
my $data_b = "\x00" x 48;
my $data_c = "\x00" x 48;
my $data_d = "\x00" x 48;

my $message_key = substr ($data, 0, 16);
my $message     = substr ($data, 16);

substr ($data_a,  0, 16) = $message_key; # memcpy ()
substr ($data_b, 16, 16) = $message_key;
substr ($data_c, 32, 16) = $message_key;
substr ($data_d,  0, 16) = $message_key;

substr ($data_a, 16, 32) = substr ($authkey,   8, 32);
substr ($data_b,  0, 16) = substr ($authkey,  40, 16);
substr ($data_b, 32, 16) = substr ($authkey,  56, 16);
substr ($data_c,  0, 32) = substr ($authkey,  72, 32);
substr ($data_d, 16, 32) = substr ($authkey, 104, 32);

my $sha1_a = sha1 ($data_a);
my $sha1_b = sha1 ($data_b);
my $sha1_c = sha1 ($data_c);
my $sha1_d = sha1 ($data_d);

my $aes_key = substr ($sha1_a, 0,  8) . #  8 + 12 + 12      = 32
              substr ($sha1_b, 8, 12) .
              substr ($sha1_c, 4, 12);

my $aes_iv = substr ($sha1_a,  8, 12) . # 12 +  8 +  4 +  8 = 32
             substr ($sha1_b,  0,  8) .
             substr ($sha1_c, 16,  4) .
             substr ($sha1_d,  0,  8);


# AES256 IGE decrypt:

my $decrypted_data = aes256_decrypt_ige ($aes_key, $aes_iv, $message);


# "checksum" of raw data:

my $hash = sha1 ($decrypted_data);

$hash = substr ($hash, 0, 16);


# compare:

if ($hash eq $message_key)
{
  print "Password found: '$word'\n";
}


the only "problem" is the non-widely use AES IGE mode of operation (basically mostly used in Telegram only):
aes256_ige.pl (translated to perl from https://mgp25.com/AESIGE/#php-implementation)
Code:
#!/usr/bin/env perl

# Author: philsmd
# Date: February 2020
# License: public domain, credits go to philsmd and hashcat

use strict;
use warnings;

use Crypt::Mode::ECB;

#
# Constants:
#

my $AES256_IGE_BLOCK_SIZE = 16;

#
# Helper functions:
#

sub exclusive_or
{
  my $in1 = shift;
  my $in2 = shift;

  # MIN () function (should always be 16 for us):
  # my $len = (length ($in1) <= length ($in2)) ? length ($in2) : length ($in1);

  # padding if input not multiple of block size:
  # $in1 .= "\x00" x ($AES256_IGE_BLOCK_SIZE - $len);
  # $in2 .= "\x00" x ($AES256_IGE_BLOCK_SIZE - $len);

  my $out = "";

  for (my $i = 0; $i < $AES256_IGE_BLOCK_SIZE; $i++) # $i < $len
  {
    $out .= chr (ord (substr ($in1, $i, 1)) ^ ord (substr ($in2, $i, 1)));
  }

  return $out;
}

sub aes256_encrypt_ige
{
  my $key = shift;
  my $iv  = shift;
  my $in  = shift;

  my $x_prev = substr ($iv, $AES256_IGE_BLOCK_SIZE, $AES256_IGE_BLOCK_SIZE);
  my $y_prev = substr ($iv,                      0, $AES256_IGE_BLOCK_SIZE);

  my $m = Crypt::Mode::ECB->new ('AES', 0);

  my $out = "";

  for (my $i = 0; $i < length ($in); $i += $AES256_IGE_BLOCK_SIZE)
  {
    my $x = substr ($in, $i, $AES256_IGE_BLOCK_SIZE);

    my $y_xor = exclusive_or ($x, $y_prev);

    my $y_final = $m->encrypt ($y_xor, $key);
    # $y_final .= "\x00" x ($AES256_IGE_BLOCK_SIZE - length ($y_final));

    my $y = exclusive_or ($y_final, $x_prev);

    $x_prev = $x;
    $y_prev = $y;

    $out .= $y;
  }

  return $out;
}

sub aes256_decrypt_ige
{
  my $key = shift;
  my $iv  = shift;
  my $in  = shift;

  my $x_prev = substr ($iv,                      0, $AES256_IGE_BLOCK_SIZE);
  my $y_prev = substr ($iv, $AES256_IGE_BLOCK_SIZE, $AES256_IGE_BLOCK_SIZE);

  my $m = Crypt::Mode::ECB->new ('AES', 0);

  my $out = "";

  for (my $i = 0; $i < length ($in); $i += $AES256_IGE_BLOCK_SIZE)
  {
    my $x = substr ($in, $i, $AES256_IGE_BLOCK_SIZE);

    my $y_xor = exclusive_or ($x, $y_prev);

    my $y_final = $m->decrypt ($y_xor, $key);
    # $y_final .= "\x00" x ($AES256_IGE_BLOCK_SIZE - length ($y_final));

    my $y = exclusive_or ($y_final, $x_prev);

    $x_prev = $x;
    $y_prev = $y;

    $out .= $y;
  }

  return $out;
}

#
# Examples:
#

# take from https://mgp25.com/AESIGE/#test-vectors:

# Example 1:

my $key         = pack ("H*", "000102030405060708090a0b0c0d0e0f");
my $iv          = pack ("H*", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
my $plain_text  = pack ("H*", "0000000000000000000000000000000000000000000000000000000000000000");
my $cipher_text = pack ("H*", "1a8519a6557be652e9da8e43da4ef4453cf456b4ca488aa383c79c98b34797cb");

# Example 2:

# my $key         = pack ("H*", "5468697320697320616e20696d706c65");
# my $iv          = pack ("H*", "6d656e746174696f6e206f6620494745206d6f646520666f72204f70656e5353");
# my $plain_text  = pack ("H*", "99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b");
# my $cipher_text = pack ("H*", "4c2e204c6574277320686f70652042656e20676f74206974207269676874210a");

#
# Start
#

# encrypt:

print unpack ("H*", aes256_encrypt_ige ($key, $iv, $plain_text)) . "\n";

# decrypt:

print unpack ("H*", aes256_decrypt_ige ($key, $iv, $cipher_text)) . "\n";

so now I've already managed to perform the most important/difficult part to come up with a POC perl script (that we could copy-paste to our test modules in tools/test_modules/)... the missing parts now would be to see what are some input restrictions (data length, salt, iter min/max values) and write the host and kernel code in hashcat. We would probably need that somebody explains this in a github issue with some further examples and facts ... after that a developer could easily start implementing it (if there is enough interest in this algo !?).
Reply
#10
The Desktop algorithm was implemented with https://github.com/hashcat/hashcat/pull/2355.

I find it quite disturbing that I got no answers here... it's much more motivating to get some responses and to get confirmation that this is really what users *need* and we are not wasting our time (on the wrong algorithm etc)... Anyway, I implemented it nonetheless just to make the Mobile/Desktop Telegram support feature complete.
Reply