02-28-2020, 04:45 PM
okay, I just spend a couple of free minutes to come up with this perl script (POC):
telegram_desktop.pl
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)
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 !?).
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 !?).