Password wallet recovery wasabi
#16
I had a quick glance at that tool from github (nopara73 etc) and did develop a quick perl POC:
Code:
#!/usr/bin/env perl

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

# credits also go to:
# https://github.com/nopara73/WasabiEncryptedSecretTester
# https://github.com/lontivero/WasabiPasswordFinder
# https://github.com/MetacoSA/NBitcoin/

use strict;
use warnings;

use Digest::SHA qw (sha256);

use Bitcoin::Crypto::Base58 qw (decode_base58check);
use Bitcoin::Crypto::Key::Private;

use Crypt::ECB;
use Crypt::ScryptKDF qw (scrypt_raw);


#
# Constants:
#

my $SCRYPT_N   = 16384;
my $SCRYPT_R   =     8;
my $SCRYPT_P   =     8;
my $SCRYPT_LEN =    64;


#
# Examples:
#

# Example 1:

# my $secret_string = "6PYSeErf23ArQL7xXUWPKa3VBin6cuDaieSdABvVyTA51dS4Mxrtg1CpGN";
# my $pass = "";

# Example 2:

my $secret_string = "6PYSJ71rbacdSS2htBcpSccutEEEJqGHq3152FuT357ha6iat6BkENGwUB";
my $pass = "pato";

# Example 3:

# my $secret_string = "6PYL1uxQPXwNV6BMruXaSbAcpBEDPuA8biUfvy3g1vc8V54t5rn4f6Z1na";
# my $pass = "MariaN3ll4&";


#
# Start:
#

my $data = decode_base58check ($secret_string);

if (substr ($data, 0, 2) ne "\x01\x42") # Base58Type.ENCRYPTED_SECRET_KEY_NO_EC
{
  print STDERR "not a valid secret string\n";

  exit (1);
}

my $type = substr ($data, 2, 1);

# (x & 0x20) div 2^5 == (x & 0x20) / 32 == (x >> 5) & 1 (extract the 6th rightmost bit)

my $is_compressed = ((ord ($type)) & 0x20) >> 5;

my $salt      = substr ($data, 3,  4);
my $encrypted = substr ($data, 7, 32); # to the very end: substr ($data, 7)


#
# Hashing (most demanding task of the whole algorithm):
#

my $hash_buf = scrypt_raw ($pass, $salt, $SCRYPT_N, $SCRYPT_R, $SCRYPT_P, $SCRYPT_LEN);



# AES256 decrypt:

my $derived_half1 = substr ($hash_buf,  0, 32);
my $derived_half2 = substr ($hash_buf, 32, 32);

my $aes = Crypt::ECB->new ({
  key         => $derived_half2,
  cipher      => "Crypt::Rijndael",
  # iv        => "\x00" x 16,
  literal_key => 1,
  header      => "none",
  padding     => "null",
});

my $out = $aes->decrypt ($encrypted);

my $h = "";

for (my $i = 0; $i < 32; $i++)
{
  my $x = substr ($out,           $i, 1);
  my $y = substr ($derived_half1, $i, 1);

  $h .= chr (ord ($x) ^ ord ($y));
}


#
# Adress conversion (bitcoin ECC):
#

# priv key to pub key to legacy address:

my $priv = Bitcoin::Crypto::Key::Private->from_bytes ($h);

my $pub = $priv->get_public_key ();

$pub->set_compressed ($is_compressed);

my $address = $pub->get_legacy_address ();



#
# Double SHA256 the address and verify checksum:
#

my $hash = sha256 (sha256 ($address));

if (substr ($hash, 0, 4) eq $salt)
{
  print "Password found: '$pass'\n";

  exit (0);
}

print "FAILED\n";

exit (1);

license: public domain with credits go to philsmd, hashcat and the authors of the referenced tools.


As expected, the code is quite demanding... giving this secret_string from wallet.json, you need to hash the password and salt with scrypt (N = 16384, r = 8, p = 8, i.e. quite high params for a GPU attack, maybe faster on CPU depending on your hardware). After that the secret string (data) is decrypted with AES256 with the key from scrypt (last 32 bytes) and mixed/xored with the remaining 32 bytes from scrypt (first 32 bytes).

The result is the Bitcoin private key for which we need to get the (compressed or uncompressed) public key and generate the legacy address from the pub key. The first 4 bytes of the double sha256 hash of the address is compared against the salt (that means that the salt, 4 bytes, used for scrypt are directly related to the bitcoin address).

I think it would be quite a difficult task to get this algo working with hashcat (ECC pub key, addresses needed, base58 conversions and high scrypt parameters) and it might not be very performant with GPUs (probably for the time being faster on some modern CPUs). That said, the tools written in C# might of course not be the fastest ones and there is of course room to improve the speed of password recovery (with a minimal ANSI C scrypt/parallelized or even implementing it in a hashcat module <- quite difficult). Of course, I don't want to blame anybody here... that C# code was of course also only meant to show how one could verify the correctness of a password (POC).

I hope you appreciate the research, was quite a lot to understand and debug until the C# code made sense to me and could be "converted" to a standalone perl proof of concept. Thanks
Reply


Messages In This Thread
Password wallet recovery wasabi - by pauppaul - 03-29-2020, 12:39 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 12:41 PM
RE: Password wallet recovery wasabi - by philsmd - 03-29-2020, 12:50 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 01:14 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 01:16 PM
RE: Password wallet recovery wasabi - by philsmd - 03-29-2020, 01:19 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 01:26 PM
RE: Password wallet recovery wasabi - by philsmd - 03-29-2020, 01:35 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 01:39 PM
RE: Password wallet recovery wasabi - by Mem5 - 03-29-2020, 02:29 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 02:36 PM
RE: Password wallet recovery wasabi - by undeath - 03-29-2020, 03:29 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 03:38 PM
RE: Password wallet recovery wasabi - by undeath - 03-29-2020, 03:50 PM
RE: Password wallet recovery wasabi - by pauppaul - 03-29-2020, 04:01 PM
RE: Password wallet recovery wasabi - by philsmd - 04-02-2020, 05:56 PM
RE: Password wallet recovery wasabi - by Mem5 - 04-02-2020, 06:58 PM