venomous0x / WhatsAPI

Interface to WhatsApp Messenger
2.59k stars 2.14k forks source link

discussing a way to new Login/Auth/Challenge/KeyStream Mechanism #422

Closed assegaf closed 10 years ago

assegaf commented 11 years ago

hi,

just notice about this function

protected function sendMessageReceived($msg) .... $messageHash["type"] = "chat"; ....

I see this also executed doing many message notification, like this one (receive profile image change) : rx [message from="628151XXXXXX@s.whatsapp.net" id="1172606828" type="notification" t="1374144652" ] rx [notify xmlns="urn:xmpp:whatsapp" name="Nadia Makes"][/notify] rx [request xmlns="urn:xmpp:receipts"][/request] rx [notification type="picture"] rx [set jid="628151XXXXXX@s.whatsapp.net" id="1374144652"][/set] rx [/notification] rx [/message]

is it that we must return the same type as it, so as "notification" not "chat"

Dynogic commented 10 years ago

Also... regarding the ports. It does a rotation between 433 and 5222. 433 is a fail over (i guess some carriers block non HTTP/S ports).

Dynogic commented 10 years ago

@assegaf , Okay. I have updated authentication.

Authentication blob sends UserAgent and MCC/MNC now too.

Challenge for i+1 is now saved, and reloaded when the connection is reestablished.

Let's see if I don't get banned soon crosses fingers

assegaf commented 10 years ago

ahh Math again,

tx [start to="s.whatsapp.net" resource="Android-2.10.222"][/start] tx [stream:features] tx [receipt_acks][/receipt_acks] tx [w:profile:picture type="all"][/w:profile:picture] tx [/stream:features]

tx [auth user="XXXXXXXXX" xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="WAUTH-1"]?Z`?Z??KY?+??;?{???Oh???????vm???f=?G٢?9??[/auth]

?Z`?Z??KY?+??;?{???Oh???????vm???f=?G٢?9??

you are saying those line is made of F(UserAgent+MCC/MNC+Somethingelse)= ?Z`?Z??KY?+??;?{???Oh???????vm???f=?G٢?9??

?

so F() still mistery ..

shirioko commented 10 years ago

So now the client sends the challenge node to the server? What has the world come to...

Dynogic commented 10 years ago

@shirioko Server sends a challenge after login, then the client remembers it and applies it for the i+1 login. This obviously chains.

@assegaf, yes. May or may not have UserAgent/MMC+MNC ... WP client ideally tries to send both.

assegaf commented 10 years ago

is that (as far as my finding) : Server send a challenge first time after register, and login for first time. after that client remembers it, and applies it (F() function as above ) for the i+1 (next) login.

first time challenge can be used as keystream to decrypt rx AFAIK.

Dynogic commented 10 years ago

@assegaf , yes, something on the lines of that.

I will try logging in tomorrow again... and crosses fingers my account isn't banned. xD

I fucked up the challenge on my primary phone today.. now I have to wait like 200min or something before I can send another SMS.

^^ I hope this solves our problem. I'm getting sick of these bans.

Dynogic commented 10 years ago

@assegaf , I've also rewrote the majority of ProcessNextTree to better reflect the behaviour of the modern client. A lot of testing and a few extra IQ handlers still need to be implemented.

Dynogic commented 10 years ago

@shirioko , what is GetClientConfig and ServerConfig exactly for?!

shirioko commented 10 years ago

getClientConfig I'm not sure getServerProperties returns the server properties:

Array
(
    [timeout] => 300
    [library] => 0
    [audio] => 0
    [checkmarks] => 0
    [newmedia] => 0
    [image_max_kbytes] => 10240
    [image_quality] => 75
    [image_max_edge] => 800
    [media] => 16
    [broadcast] => 50
    [max_subject] => 25
    [max_participants] => 51
    [max_groups] => 50
)
assegaf commented 10 years ago

@Dynogic any logs on tx decypted using official client ?

I believe getServerProperties function on whatsapi tx already correct, but getClientConfig not yet,

cause still got error 404 or something.

shirioko commented 10 years ago

@Dynogic you were right about the ports and the challenge node, just found this in 523:

login.LoggedIn += delegate(object sender, FunXMPP.Login.LoginEventArgs args)
            {
                byte[] nextChallenge = args.NextChallenge;
                Log.WriteLineDebug("Logged in.");
                Settings.NextChallenge = nextChallenge;
                Settings.LastGoodPortIndex = (int)((ulong)FunRunner.CurrentPort % (ulong)((long)FunRunner.Ports.Length));
shirioko commented 10 years ago

EUREKA The next challenge node is sent to you in the success node:

rx  <success t="1374568789" xmlns="urn:ietf:params:xml:ns:xmpp-sasl" kind="free" status="active" creation="1367522924" expiration="1399058924">ˆ¸ãó    3•%“’ޏæÞ,ʸl·</success>
private void ParseSuccessNode(FunXMPP.ProtocolTreeNode node)
                {
                    ...
                    bla bla bla
                    ...
                    if (this.LoggedIn != null)
                    {
                        this.LoggedIn.Invoke(this, new FunXMPP.Login.LoginEventArgs
                        {
                            NextChallenge = node.data
                        });
                    }
                }

EDIT @assegaf don't bother with getClientConfig, I've looked it up and it's never actually called by WhatsApp There's also a SendClientConfig method which sends the platform, mcc and lc to the server, with an overload specifically for WP7 which sends a crapload of additional lines like notifications push URI, group settings and image previews

assegaf commented 10 years ago

oh God, how can my eyes miss it, I got it too but my eyes (four of them) didnot notice

rx [success t="1374406602" xmlns="urn:ietf:params:xml:ns:xmpp-sasl" kind="free" status="active" creation="1373524843" expiration="1405060843"]?؋?y?w8+??,$??q?b[/success]

Yeeeeahhh, ready to sniff again ...

any idea on F() function ?

shirioko commented 10 years ago

POOF! F() byte[] nonce is nextChallenge you received in

internal byte[] GetAuthBlob(byte[] nonce)
{
    byte[] key = FunXMPP.Login.KeyStream.KeyFromPasswordAndNonce(base.Password, nonce);
    base.TreeNodeReader.SetInputKey(new FunXMPP.Login.KeyStream(key));
    this.outputKey = new FunXMPP.Login.KeyStream(key);
    List<byte> list = new List<byte>(1024);
    for (int i = 0; i < 4; i++)
    {
        list.Add(0);
    }
    list.AddRange(Encoding.get_UTF8().GetBytes(Settings.ChatID));
    list.AddRange(nonce);
    list.AddRange(Encoding.get_UTF8().GetBytes(((long)(DateTime.get_UtcNow() - FunXMPP.UnixEpoch).get_TotalSeconds()).ToString()));
    list.AddRange(Encoding.get_UTF8().GetBytes(AppState.GetUserAgent()));
    try
    {
        CELL_INFO cellInfo = NativeInterfaces.Misc.GetCellInfo();
        list.AddRange(Encoding.get_UTF8().GetBytes(string.Format(" MccMnc/{0}{1}", cellInfo.Mcc.ToString().PadLeft(3, '0'), cellInfo.Mnc.ToString().PadLeft(3, '0'))));
    }
    catch (Exception)
    {
    }
    byte[] array = list.ToArray();
    this.outputKey.EncodeMessage(array, 0, 4, array.Length - 4);
    return array;
}

I'll see what I get from GetCellInfo() when I get home from work, I only have my HTC Titan on me which isn't developer unlocked :/

EDIT CELL_INFO doesn't seem too exciting:

    public struct CELL_INFO
    {
        public uint Mcc;
        public uint Mnc;
        public bool Is2G;
        public bool Is3GPlus;
        public bool Roaming;
    }

Adding this stuff to WhatsAPI right now...

assegaf commented 10 years ago

some how keyStream cannot be used to decrypt next rx after found rx in hex[000000]

not sure what rx 000000 means

tried to use next+1 challenge to make new KeyStream, didnot work neither.

so from sniffing POV, we lost challenge string.

I wonder from whatsapi POV (point of view).

shirioko commented 10 years ago

No we didn't

tx  <stream:features>
tx    <receipt_acks></receipt_acks>
tx    <status></status>
tx  </stream:features>

tx  <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="WAUTH-1" user="3164*******">–º·½ÛÆ“Bù?xñU#  CÐe…šîðM¬_ŽË7~¥òyŠ¶yb–ÄÅ/ÜçG£»’v~­C@¸0aVhÕÐËo^9©†î¬åIË~5Ÿ-~Âeaêßgáý„3µôWO</auth>

rx  <start from="s.whatsapp.net"></start>

rx  <stream:features>
rx    <receipt_acks></receipt_acks>
rx  </stream:features>

rx  <success t="1374595527" xmlns="urn:ietf:params:xml:ns:xmpp-sasl" kind="free" status="active" creation="1367522924" expiration="1399058924">΢¼Š^¹PÇܪjd¤×à#b}</success>

rx  <presence from="s.whatsapp.net" status="dirty" xmlns="w"></presence>

I'll check it in when I've integrated it properly into WhatsProt class

assegaf commented 10 years ago

Ok i will try to prepare sender for long test.

I see you implemented F function successfully.

I assume you will save next challenge on text file in binary or hexstring.

sent using android mobiles

shirioko commented 10 years ago

https://github.com/shirioko/WhatsAPI/commit/cefc05ceda6c85fbf99dae867328ddf83d32a743 I'll push it upstream when you guys confirm it's working

Dynogic commented 10 years ago

@shirioko ... looks right. That's exactly what I've implemented yesterday. =)

WhatsAPI needs a rewritten ProcessNextTree ... to support all those weird ACK replies. I'm busy rewriting it... and will send you the code on completion.

Dynogic commented 10 years ago

@shirioko , day two with this new implementation and still not banned. hopefully this goes well =)

assegaf commented 10 years ago

just managed to run using this new code. this time I register using official android client, even different version that @shirioko wrote as USERAGENT at those new code.

catch after missvenom got password, then turn off the android fastly. pretty sure those not login yet.

still managed to login, and everything as good. congrats ..

I will check to act like normal people doing, sending message (different each), maybe every 30 minute. and sendActive inActive, like smartphone doing ..

the missing thing is maybe I skip sync contact, I see on sniff official doing like huge TCP tx and rx, looks like contact Sync. each tx rx +- 1k data, maybe like 30 times

assegaf commented 10 years ago

USERAGENT:WhatsApp/2.10.768 Android/4.0.4 Device/samsung-GT-I9100 REQUEST: https://v.whatsapp.net/v2/exist?cc=XX&in=XXXXXXXX&lg=en&lc=GB&id=%15%9c%b8FY%dc%2d%9f%bam%87%7eu8%fb%c8%c6%cf%2d%ef

@shirioko any idea those id=%15%9c%b8FY%dc%2d%9f%bam%87%7eu8%fb%c8%c6%cf%2d%ef means ?

its on missvenom, %15 string cannot be decoded as URLDecode.

shirioko commented 10 years ago

Surely it can be decoded into a byte. It's a URL encoded binary string which includes non readable characters

Dynogic commented 10 years ago

@assegaf , I can look tonight for you in my Java deobfuscated version to figure out exactly how ID is composed. I don't really care about it though, as you never have to use exist request.

Dynogic commented 10 years ago

@shirioko , wanna go on Google Talk? :P

assegaf commented 10 years ago

you are right .. even php urldecode do the jobs :+1:

$php test.php urlencode [%15%9c%b8FY%dc%2d%9f%bam%87%7eu8%fb%c8%c6%cf%2d%ef] bin [??FY?-??m?~u8????-?] hex [ 159cb84659dc2d9fba6d877e7538fbc8c6cf2def ]

ok @Dynogic , will know for sure what is that, I think its an Function too, maybe based on IMEI or UniqueID from Android java .., Pretty sure this decide which one an existing account which one new to send new sms.

ups just got this error (on running whatsapi)

rx [stream:error] rx [system-shutdown xmlns="urn:ietf:params:xml:ns:xmpp-streams"][/system-shutdown] rx [/stream:error]

no weird tx sent to this, just came are they restarting server ? ... ...

Update :

I got failed login after those, but tried to reregister, its said my number is NEW. so I got new SMS again, after that. sniff again password, and relogin, everything good again. will try and monitor again ..

More Update :

4 hour passed, still good

shirioko commented 10 years ago

It's just a URL encoded SHA raw hash string of some device specific parameters and phone number. Sniffing it out on MissVenom is a lot easier than trying to reconstruct it by piecing all required device parameters together. You can use any SHA hash for registration regardless of the input, there's no way for WhatsApp to validate it as you cannot decode a hash. Only thing they can do is check if it's 20 byte long.

@Dynogic I was actually going to bed, it's past midnight here already..

Dynogic commented 10 years ago

Gee... this is interesting:

SendNormalizePhoneNumber(string cc, string phone)

Looks like WhatsApp is using libphonenumber on their servers.

assegaf commented 10 years ago

after restarting the api / relogin (auth success, and got next challenge) I still got this rx [iq from="g.us" id="4" type="result"][/iq]

rx [stream:error] rx [system-shutdown xmlns="urn:ietf:params:xml:ns:xmpp-streams"][/system-shutdown] rx [/stream:error]

and seem looks like after that, everything else not working (tx/rx) , tx cause write error socket, pollMessage just looks like not resulting anything.

if only me, I might missing something

Update :

after that, exit, try to relogin, failed. Weird thing is,

Dynogic commented 10 years ago

@assegaf , banned again.

This is very weird. It might be because I'm requesting photos to frequently. I have disabled this... and now I'm yet on another account. When are we going to figure this out. >.<

Dynogic commented 10 years ago

And... there goes another account. I don't get it >.<

assegaf commented 10 years ago

@Dynogic dont sure yet, its banned, if you codeRequest again, and said blocked, that means banned.

assegaf commented 10 years ago

@Dynogic use checkCredential with the same id you codeRequest(), I see it generate new password, and next login its okay again ... (after auth-fail).

Update : Forget it, this time after doing something, banned too .. I always love this number ..

Array (     [cc] => XX     [in] => XXXXXXX     [lg] => en     [lc] => GB     [id] => %15%9c%b8FY%dc%2d%9f%bam%87%7eu8%fb%c8%c6%cf%2d%ef ) stdClass Object (     [status] => fail     [reason] => blocked )

Dynogic commented 10 years ago

Hey @assegaf ,

Okay... looks like a few things changed with the protocol of how messages are sent (very slightly). so I've updated these.

I have made two accounts. I simply logged in with the one account, and pulled the connection. On the other account, I logged in... sent several messages (with composing/paused), received a few... and then logged off. Both these connections DID send presence and subscription updates.

So, I'll relogin tomorrow... and crosses fingers hopefully both aren't banned.

assegaf commented 10 years ago

just noticed something strange when we put parameter just as official client do (and 2.10.768) [auth user="622144212088" passive="true" ... ]

passive="true" true means, we dont get any rx, notificationClient, and Message In, must be new feature. not sure if using this mode, how to get some rx above.

Dynogic commented 10 years ago

@assegaf , @shirioko : both of those accounts I tested last night still work

screen shot 2013-07-24 at 10 03 04 am screen shot 2013-07-24 at 10 04 34 am

:

assegaf commented 10 years ago

Actually I run another number for days, with more even process. like sync more than 1000 contacts, send subcription each, get every profile picture changes, load the profile pic, send random active/offline. everything just fine. conversation to myself (more than 100 message/day), just fine

once managed to send to different number (1000 contacts above), not even touch 10 msg, blocked.

login part, getConfig, etc .. just fine.

I believe more now, tx of sending message on official client is the most suspected part, or in the middle after login and before starting sending message, keystream must be changed. (the only prove is sniff it).

I will try sniff again when got time.

Dynogic commented 10 years ago

@assegaf , been doing a major revamp of my codebase to more closely reflect how the official client operates.

I'm having great luck so far ... zero bans.

assegaf commented 10 years ago

@Dynogic still lucky ?

I just noticed something that bothering me about how socket works. sendGetServerProperties said, to set timeout to 300, I believe its 5 minutes. When I tried to sniff it, off course not exactly 5 minute, but when there is data like 1 minute after, it will send socket data. and make new connection, wait timeout again 5 minute.

on whatsapi it set to 2 seconds,

is that kinda like brute force for php to do socket connection to server ?

shirioko commented 10 years ago

No, it's because PHP doesn't use multithreading and your script would lock up for 5 minutes when calling socket_read

assegaf commented 10 years ago

ah I see, so its true. just searched google: php dont support threading.

so on java (or wp/iphone) it just open 1 socket, thread number (1) just listen forever, but other thread would just send tx freely when there is any event without waiting thread number (1) to finish 5 minute.

Dynogic commented 10 years ago

@assegaf depends on how the network stack is written on an OS.

All modern OS's allow you to listen in on the TCP connection on one thread, and write on the same TCP connection on another. Since PHP doesn't support multi-threading, you can't do this. PHP should more be used as a duct-tape language than anything else these days imho.

assegaf commented 10 years ago

we have php. net and py. I am planning to make java version. php not again sufficient to mimic phone client

Dynogic commented 10 years ago

I hate Java >.< Used the language for way to long => it's ugly in comparison to C#. And it's an absolute pain to do async programming with Java -.-

Python is really nice... but I've never really found a need to use it. Hmmm...

CODeRUS commented 10 years ago

@shirioko @Dynogic can you check Yowsup code? https://github.com/CODeRUS/yowsup/blob/master/src/Yowsup/Auth/mechanisms/wauth.py i see nextChallenge already implemented here.

shirioko commented 10 years ago

Sorry, I don't do python

But it's not rocket science. First:

On next connection:

Did I miss anything? :)

CODeRUS commented 10 years ago

well. seems exact same code already implemented in Yowsup long time ago :)

shirioko commented 10 years ago

No it's not? The code you linked only contains standard handshake login.

CODeRUS commented 10 years ago

okay i see now, this is added functionality: https://github.com/venomous0x/WhatsAPI/blob/cefc05ceda6c85fbf99dae867328ddf83d32a743/src/php/whatsprot.class.php#L1304