yowsup icon indicating copy to clipboard operation
yowsup copied to clipboard

Backup Token

Open galdudi opened this issue 3 years ago • 52 comments

I'm trying to register (request sms) with my number and got an error:

{"login":"xxxxxxxxxxxxx","param":"backup_token","reason":"bad_param","status":"fail"}

It was working fine until last week

galdudi avatar Feb 13 '22 08:02 galdudi

I got this error too, with both sms and call request.

yowsup.common.http.warequest - Passing Config to WARequest is deprecated, pass a YowProfile instead yowsup.common.http.warequest - b'{"login":"xxxxxxxxxxxx","param":"backup_token","reason":"bad_param","status":"fail"}\n' status: b'fail' reason: b'bad_param' param: b'backup_token' login: b'xxxxxxxxxxxx'

mrhghhgh avatar Feb 13 '22 15:02 mrhghhgh

Same a problem for me!

YazinAlhamdi avatar Feb 14 '22 16:02 YazinAlhamdi

This is a new parameter added lately and is required.

GM12Tick avatar Feb 15 '22 09:02 GM12Tick

I have same problem... Is there any solution?

acharyadev12 avatar Feb 15 '22 11:02 acharyadev12

Same here

yowsup-cli v3.2.1 yowsup v3.3.0 consonance v0.1.5 dissononce v0.34.3 python-axolotl v0.2.2 cryptography v2.5 protobuf v3.0.0

{"login":"xxx","param":"backup_token","reason":"bad_param","status":"fail"}

lauriolasw avatar Feb 16 '22 01:02 lauriolasw

I'm working on a solution

SamuelScheit avatar Feb 17 '22 13:02 SamuelScheit

Alright after setting up the reverse engineering environment I was able to debug the WhatsApp application and decompiled the smali source code to java for easier readability. (I'll later publish a setup guide for other contributors) The backup_token is generated the first time the user registers and then saved in a local backup_token file.

I've written a guide on how to generate this backup_token with JS examples, someone else need to implement them in python and create a PR for this library. I couldn't test the example scripts against WhatsApp, so if something fails please let me know.

  1. Generate the base string by joining the result of the following two parts into a single string

    1. Constant: A\u0004\u001d@\u0011\u0018V\u0091\u0002\u0090\u0088\u009f\u009eT(3{;ES (Extracted from class X.0BF.A0L)

      const constant =
      	"A\u0004\u001d@\u0011\u0018V\u0091\u0002\u0090\u0088\u009f\u009eT(3{;ES";
      let backup_token_constant = "";
      
      for (let i = 0; i < constant.length; i++) {
      	backup_token_constant += String.fromCharCode(constant.charCodeAt(i) ^ 18);
      }
      return backup_token_constant;
      

      Result: S\x16\x0FR\x03\nD\x83\x10\x82\x9A\x8D\x8CF:!i)WA

    2. The last 4 digits of the users phone number

      "11234567890".slice(-4);
      

      Result: 7890

  2. Convert the base string into a buffer

    const backup_token_constant =
    	"S\x16\x0FR\x03\nD\x83\x10\x82\x9A\x8D\x8CF:!i)WA";
    const last_digits = "7890";
    
    const base = backup_token_constant + last_digits;
    
    const buffer = Buffer.from(base);
    

    Result: <Buffer 53 16 0f 52 03 0a 44 c2 83 10 c2 82 c2 9a c2 8d c2 8c 46 3a 21 69 29 57 41 37 38 39 30> or as decimal binary:[83, 22, 15, 82, 3, 10, 68, 131, 16, 130, 154, 141, 140, 70, 58, 33, 105, 41, 87, 65, 55, 56, 57, 48]

  3. Generate a 16 bytes long sha1 key with 16 iterations and a 4 bytes long salt using the Password-Based Key Derivation Function 2 (PBKDF2) based on the previous buffer and saltA value.

    WhatsApp on Android uses the javax.crypto.SecretKeyFactory with PBKDF2WithHmacSHA1And8BIT (Extracted from class X.0BA.A06()). In Java you have to use PBKDF2WithHmacSHA1 and a keylength of 128.

    The NodeJS equivalent crypto.pbkdf2Sync() can be used to generate the key:

    const crypto = require("crypto");
    
    // buffer from step 2
    const salt = crypto.randomBytes(4); // <Buffer ee 1b 75 2e>
    const iterations = 16;
    const keylength = 16; // in java is the keylen 128
    const key = crypto.pbkdf2Sync(buffer, salt, iterations, keylength, "sha1");
    
  4. Generate an aes-128-ofb cipher based on the previous sha1 key and 16 bytes long IV and random 20 bytes buffer as input

    const crypto = require("crypto");
    
    const IV = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-128-ofb', key, IV);
    const encrypted = cipher.update(crypto.randomBytes(20))
    
  5. Construct the final backup_token by combining [0, 2], salt, IV, encrypted

    Buffer.concat([Buffer.from([0, 2]), salt, IV, encrypted])
    

Final Script

const crypto = require("crypto");

const constant = "A\u0004\u001d@\u0011\u0018V\u0091\u0002\u0090\u0088\u009f\u009eT(3{;ES";
let backup_token_constant = "";

for (let i = 0; i < constant.length; i++) {
    backup_token_constant += String.fromCharCode(constant.charCodeAt(i) ^ 18);
}
const last_digits = "11234567890".slice(-4);

const base = backup_token_constant + last_digits;
const buffer = Buffer.from(base);

const salt = crypto.randomBytes(4); 
const iterations = 16;
const keylength = 16; // in java is the keylen 128
const key = crypto.pbkdf2Sync(buffer, salt, iterations, keylength, "sha1");
const IV = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-ofb', key, IV);
const encrypted = cipher.update(crypto.randomBytes(20))

const backup_token = Buffer.concat([Buffer.from([0, 2]), salt, IV, encrypted])
console.log(backup_token.toString("hex"))

Example backup token: 0002cf3550ec083dd5b7618b54746c9c8dfaabbd8e1dd21d1fb8e942ce3ab81c4a0ff1e03e8c8fb98e5a

SamuelScheit avatar Feb 18 '22 22:02 SamuelScheit

import secrets
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import OFB
from cryptography.hazmat.primitives.ciphers.base import Cipher

BACKUP_TOKEN_CONSTANT = b"S\x16\x0FR\x03\nD\x83\x10\x82\x9A\x8D\x8CF:!i)WA"
BACKUP_TOKEN_HEADER = b'\x00\x02'


def get_backup_token(phone: str):
    tail = phone[-4:].encode("ascii")
    base = BACKUP_TOKEN_CONSTANT + tail
    salt = secrets.token_bytes(4)
    key = PBKDF2HMAC(
        algorithm=hashes.SHA1(),
        length=16,
        salt=salt,
        iterations=16
    ).derive(base)
    iv = secrets.token_bytes(16)
    cipher = Cipher(AES(key), mode=OFB(iv))
    encryptor = cipher.encryptor()
    payload = secrets.token_bytes(20)
    encrypted = encryptor.update(payload)
    result = b"".join([BACKUP_TOKEN_HEADER, salt, iv, encrypted])
    return result.hex()

Converted to python, checked on non-random salt, IV and payload (random 20 bytes)

salt = [0, 1, 2, 3]
IV = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
payload = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

The results in JS code and Python code match ('000200010203000102030405060708090a0b0c0d0e0f67fa2d28f830f0333d868a43d35da3e75118141a') starting working on PR

leotrubach avatar Feb 19 '22 02:02 leotrubach

@Flam3rboy do we know how backup token is passed? I tried base64 and hex and insert it as ,"backup_token" but it didn't work. Are there frameworks in java or js like yowsup where it was implemented?

leotrubach avatar Feb 19 '22 03:02 leotrubach

more effort converting java to pyton, we already pissed off converting smali to java ...

assegaf avatar Feb 19 '22 06:02 assegaf

@leotrubach I'm currently investigating how the backup token is passed to WhatsApp and will let you know when I find anything 👍

SamuelScheit avatar Feb 19 '22 08:02 SamuelScheit

When the registration request is constructed the following parameters are passed to the JniBridge image The backup_token is probably named something else, because I can't find any property named backup_token

SamuelScheit avatar Feb 19 '22 14:02 SamuelScheit

Okay, i decoded some parts of hashmap, so probably backup token isn't in that hashmap, which means it could be one of v6 or v7

offline_ab: '{"exposure":[],"metrics":{}}'
network_radio_type: seems like it is now 3 bytes - '111'
hasinrc - '1'
sim_operator_name: "T-Mobile"
pid: '3744'
sim_state: '5'
token: 'HQL+1iodePFzduaN3VrQUIh4vks=' which is bytes[20] '1d 02 fe d6 2a 1d 78 f1 73 76 e6 8d dd 5a d0 50 88 78 be 4b' or [29, 2, 254, 214, 42, 29, 120, 241, 115, 118, 230, 141, 221, 90, 208, 80, 136, 120, 190, 75]
rc: '0'
client_metrics: '{"attempts":11,"was_activated_from_stub":false}'
mistyped: '7'
simnum: '0'
read_phone_permission_granted: '0'
network_operator_name: 'Android"

leotrubach avatar Feb 19 '22 16:02 leotrubach

The backup_token is generated the first time the user registers and then saved in a local backup_token file.

Where is the location in project and the format that we should save this file?

mrhghhgh avatar Feb 22 '22 15:02 mrhghhgh

@mrhghhgh It is saved as binary in /data/data/com.whatsapp/files/backup_token

SamuelScheit avatar Feb 22 '22 15:02 SamuelScheit

@mrhghhgh It is saved as binary in /data/data/com.whatsapp/files/backup_token

Thanks, but is this an android directory? I was using yowsup in desktop ubuntu before this error be appeared and in yowsup project folder or ubuntu there isn't any directory like this. Please help me again

mrhghhgh avatar Feb 23 '22 09:02 mrhghhgh

The real question is not where it saved, but how it is sent during registration?

galdudi avatar Feb 23 '22 13:02 galdudi

so is there a solution or this is not usable any more ?

guy111a avatar Feb 24 '22 21:02 guy111a

so is there a solution or this is not usable any more ?

I'm wondering the same

brunoaduarte avatar Feb 24 '22 21:02 brunoaduarte

well its easy if you can follow how its generated and sent (encoded) in apk file, use jadx-gui to find out

assegaf avatar Feb 25 '22 01:02 assegaf

Thank you .. if I could understand what to do I would not have asked …

Guy A

On 25 Feb 2022, at 3:00, assegaf @.***> wrote:

 well its easy if you can follow how its generated and sent (encoded) in apk file, use jadx-gui to find out

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you commented.

guy111a avatar Feb 25 '22 07:02 guy111a

register using real android phone and find all data in /data/data/com.whatsapp to be used by yowsyup

assegaf avatar Feb 25 '22 10:02 assegaf

Thank you.

I don’t have android. Can it be done by computer ( windows / linux ) or by iphone ?

Guy A

On 25 Feb 2022, at 12:58, assegaf @.***> wrote:

 register using real android phone and find all data in /data/data/com.whatsapp to be used by yowsyup

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you commented.

guy111a avatar Feb 25 '22 12:02 guy111a

Can any one just add the line how to call the function? What fields should be passes.

Guy A

On 25 Feb 2022, at 12:58, assegaf @.***> wrote:

 register using real android phone and find all data in /data/data/com.whatsapp to be used by yowsyup

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you commented.

guy111a avatar Feb 25 '22 12:02 guy111a

Has anyone found a fix?

Whomakes avatar Mar 01 '22 13:03 Whomakes

its common encoding parameter aes hash SecretKeySpec secretKeySpec = new SecretKeySpec(new SecretKeySpec(C015602t.m20338A06(obj.getBytes(), A0D, 16, 128).getEncoded(), "AES").getEncoded(), "AES/OFB/NoPadding"); etc etc ..

but the main problem its using Android BackupAgentHelper mechanism, which is upload your backup_token to google cloud. so basically if you register and able to generate the hash, its a trap isnt in cloud there is no your backup_token anywhere.

assegaf avatar Mar 01 '22 14:03 assegaf

Has anyone know where on android client saves: expid, fdid, id ?

galdudi avatar Mar 02 '22 07:03 galdudi

has anyone solved this ? i am trying this for first time.

dhirennjaypal avatar Mar 03 '22 04:03 dhirennjaypal

i will be happy if you add it here ...

On Thu, Mar 3, 2022 at 10:33 PM varknov @.***> wrote:

i found the way and how is sent, registration working. interested ones can contact me: @.***

— Reply to this email directly, view it on GitHub https://github.com/tgalal/yowsup/issues/3123#issuecomment-1058462799, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJTXH2TYTKCVDPSSC33QOW3U6EO2ZANCNFSM5OIRWN5Q . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

--

~Guy

guy111a avatar Mar 03 '22 20:03 guy111a

Ya .. well. Good luck with btc lol ..

Guy A

On 3 Mar 2022, at 22:33, varknov @.***> wrote:

 i found the way and how is sent, registration working. interested ones can contact me: @.***

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you commented.

guy111a avatar Mar 03 '22 21:03 guy111a