haakonnessjoen / MAC-Telnet

Open source MAC Telnet client and server for connecting to Mikrotik RouterOS routers and Posix devices using MAC addresses
http://lunatic.no/2010/10/routeros-mac-telnet-application-for-linux-users/
GNU General Public License v2.0
393 stars 125 forks source link

Compatibility with RouterOS 6.43 #42

Closed eworm-de closed 2 years ago

eworm-de commented 6 years ago

Starting with RouterOS 6.43rc17 the protocol changed. This is the relevant changelog entry:

*) mac-telnet - require at least v6.43 MAC Telnet client when connecting to v6.43 or later version server;

Please update the wire protocol to support latest RouterOS.

StellaTeam commented 6 years ago

I don't know if that can help, but according to my knowledge the changes might be related to the removal of login challenge.

Starting from 6.43, none of mikrotik logins will be based on challenge but replaced by cleartext credentials (because of internal password no more stored in cleartext but hashed).

If change is similar to API, the difference, at login, will be:

instead of receiving challenge at connect, and use it for /login send credentials directly with /login, in cleartext

Update: I think the change should happen in / If we receive encryptionkey, transmit auth data back / if (cpkt.cptype == MT_CPTYPE_ENCRYPTIONKEY) { memcpy(encryptionkey, cpkt.data, cpkt.length); send_auth(username, password); } where we'll no more receive encryptionkey but something else to know router is ready, then, send the login+password in clear.

haakonnessjoen commented 6 years ago

Thanks for the heads up. I have not had time to check this out. But I will do some digging into the traffic as soon as possible. Sounds strange to send it in clear text. I would think they'd rather send the internal seed, or do som other encryption. Do they really want people to send passwords in cleartext over l2? I would think the api change would kind of push people over to tls protected api access.

Well, I will check soon. If anyone else wants to do it in the meantime, please keep me updated.

StellaTeam commented 6 years ago

Thanks @haakonnessjoen

That sounds weird to send passwords in cleartext, but in 6.43, API will work like this, without TLS, except if you use API-SSL. A really big change for a minor release... moreover even with TLS, there is no guarantee (except using own certificates) to be sure the device you're talking to is really your router and not a "yes bot" recording your cleartext tries. :(

I'll make some captures, maybe today, if that can help i'll send my conclusions here.

StellaTeam commented 6 years ago

I just made two captures, one with pre 6.43 and on with latest 6.43RC.

The differences i found was:

I didn't find how was hashed password.

On my captures, made with default admin/"" credentials, i got (according to wireshark decoding) client key: 61646d696e002750a93030e5b7c186daa75f33a2c0121f73188d9c3fec202d86b4f41ff5f38101 server key: 0da6f6996aabdbd6c5359d3f98797127e6fd5fab77691b1137e2390aefc55fd9000c96a2cba25dbcccd3a94cf53807f1be client password hash: cdfd993c78f37d513e5f66fe66858ecc0b3e7f0ec3ba7fa3c4e6796055c8d037

Attached are the pre and post 643 pcap captures.

pcap-captures.zip

StellaTeam commented 6 years ago

I made some more tests, that is quite difficult for me to understand what Mikrotik does for its new hash.

I made 3 tests for two logins, one is valid "admin -> empty password" , one is invalid "toto -> empty password"

The client sends, as "key" the login followed by the real key. The server sends a key.

What is strange is the key length is no more fix... but seems to depends on login size...

If anybody has any idea...

I asked Mikrotik support, they refused to tell anything about their proprietary protocol...

LOGIN "admin"

Client key
61:64:6d:69:6e:00:
73:89:d0:78:16:00:0d:d5:b4:83:f8:74:f1:02:73:02:53:5c:df:c2:12:9f:17:2b:f1:73:8c:b7:aa:94:b4:c7:01
Server key
12:f1:a5:82:82:68:2d:72:8c:ac:56:d6:10:7e:b7:61:76:b0:05:c8:a8:b5:55:67:90:a5:46:e1:3c:b0:33:c3:00:cd:02:95:3c:60:ed:44:75:17:fb:2e:45:b3:50:cd:4d
Password hash (empty)
9f:8f:50:04:97:71:2f:8e:5e:7e:f3:02:9a:1f:e4:a4:3f:de:30:09:f8:bc:a3:44:c9:64:15:12:af:b5:68:30

Client key
61:64:6d:69:6e:00:
32:c2:52:bc:30:20:c6:ad:df:60:7e:6d:83:fb:87:49:4e:55:0e:0f:33:1b:b1:9f:be:4b:4d:0b:d9:93:cb:1b:01
Server key
14:cb:88:a2:a9:06:7d:be:31:93:ae:da:da:92:59:6c:21:c5:55:8e:58:1b:eb:05:15:9d:37:10:2f:2c:41:5f:00:cd:02:95:3c:60:ed:44:75:17:fb:2e:45:b3:50:cd:4d
Password hash (empty)
6f:a2:25:36:fc:d9:d2:38:71:c6:9e:f9:c4:a6:45:bd:5a:a9:ba:d7:56:f9:3d:39:0c:ab:14:5f:c2:70:3d:f6

Client key
61:64:6d:69:6e:00:
77:44:7a:6a:35:d5:d6:49:bf:23:8b:f3:2e:8b:c4:08:b2:04:7b:76:b3:d8:c1:83:7e:ba:d8:7c:8b:02:7e:af:01
Server key
1c:c4:08:25:23:13:ec:9c:3e:11:98:7d:4e:a8:41:a9:ed:cb:2c:98:21:ae:c3:86:e8:88:f3:af:c4:56:a1:c9:00:cd:02:95:3c:60:ed:44:75:17:fb:2e:45:b3:50:cd:4d
Password hash (empty)
b1:c3:90:43:d9:37:6b:95:1a:e2:2c:dc:f5:0d:7d:1e:46:7a:4e:69:a8:4b:6e:d2:83:c2:b0:ff:e3:97:8b:5a

LOGIN "toto"

Client key
74:6f:74:6f:00:
3e:cd:da:cf:48:bd:cd:8e:31:40:96:79:17:1b:1d:2e:41:1f:b9:83:f1:e3:26:1a:6e:f9:5e:6b:bd:a7:40:b3:01
Server key
3a:2e:a1:04:e2:74:58:fb:bf:af:80:6c:5a:40:02:3c:44:c6:bf:d1:51:30:98:8e:a4:eb:fb:5b:cb:05:bf:c8:01
Password hash (empty)
24:a9:4e:eb:f9:6e:7b:6c:64:4c:33:5f:2f:f5:7c:28:4d:6a:24:15:3f:f7:2a:20

Client key
74:6f:74:6f:00:
18:a5:15:2b:a4:40:a6:e0:4f:0f:73:64:ba:1c:33:1c:9e:6a:40:f9:0c:19:14:a0:49:83:5f:de:c9:4d:63:24:00
Server key
5f:3f:2b:3d:d1:38:0c:9c:ca:86:d0:f9:1e:55:90:aa:8b:b9:2f:ff:21:43:28:b8:59:a9:d0:b9:81:c2:29:43:00
Password hash (empty)
6c:dc:1c:bd:84:f5:59:6a:2e:73:55:a5:f0:ce:a8:6a:7e:ec:3c:1f:7d:7d:b2:82

Client key
74:6f:74:6f:00:
4f:d8:63:62:06:7a:4f:94:6c:20:8b:19:a3:ca:46:09:65:4c:eb:62:fd:c6:24:d1:b6:35:75:88:53:9e:49:28:00
Server key
5f:4c:87:e8:5c:e3:bd:5b:7d:9b:64:72:5b:7e:de:fd:54:7b:ef:b0:13:0b:c1:bb:d7:f8:10:72:cd:86:93:78:01
Password hash (empty)
3e:6f:32:c6:ec:51:a5:34:b8:72:9b:fa:f2:95:24:bc:59:0e:0b:96:d1:32:c0:5b
haakonnessjoen commented 6 years ago

Thanks for all your research. I will do some testing to see if I can get the same hashes.

I also reached out to Mikrotik, and ironically (since, they are heavily dependant upon open source), they don't want to disclose the information..

Thank you very much for the comments. Currently mac-telnet is proprietary protocol, and we do not want to disclose additional information at the moment.

BrainSlayer commented 5 years ago

if the key has variable length depending on the login size, its likelly that the login is also included in the same string. moreover it seems not to be a hash but a encrypted key with some sort of algorithm since hash would always have a fixed length

Enrico204 commented 5 years ago

@BrainSlayer RSA? The winbox.exe binary is using RSA at some point

BrainSlayer commented 5 years ago

@Enrico204 hard to say. i dont know yet what they changed in the protocol. i'm still using it as option in my dd-wrt firmware with some modifications since this project here had some bugs with multiple interfaces. as soon as i find out how it works with the changed protocol i do my best to integrate it . just had no mood yet to disassemble winbox to see what has been changed

StellaTeam commented 5 years ago

Don't worry, they changed their mind, and went back to original behavior with mac-telnet, so the code is still working like a charm...

Hoping they won't get change their mind again...

We may consider this issue as closed... until they get back with these (or other) changes

BrainSlayer commented 5 years ago

i believe they changed it because of some security issues i was reading about. i dont know if it belongs to the protocol itself.

eworm-de commented 5 years ago

RouterOS used to store passwords in plain text, that changed with version 6.43. Due to password being hashed on the device the login mechanism had to change.

Just tried to login to RouterOS 6.43.12 and 6.44rc1 with current mactelnet... And succeeded. So wondering when, why and how that changed. I can't believe they changed their mind and keep clear text passwords...

eworm-de commented 5 years ago

Well the same changelog contained:

*) user - all passwords are now hashed and encrypted, plaintext passwords are kept for downgrade (will be removed in later upgrades);

Perhaps they decided to allow old and new login mechanism until final removal?

eworm-de commented 5 years ago

Latest changelog for version 6.45beta54 contains:

!) user - removed insecure password storage;
haakonnessjoen commented 5 years ago

:(

eworm-de commented 5 years ago

Manual:Security MikroTik Wiki lists security related changes for RouterOS v6.45 and later. It includes:

MAC telnet uses EC-SRP5 for authentication, to connect to newer server, client needs to be upgraded;

Enrico204 commented 5 years ago

I can't find info about EC-SRP5. Does anyone has any clue about this? I'm willing to study it (in my free time) and try to see if I can make a pull request to have the compatibility again

haakonnessjoen commented 5 years ago

As far as I remember, I found some info sbout it in a SIP rfc. But there they use a SIP uri as part of the key if I remember correctly. So the big question is how the key would look like on mac-telnet. Maybe someone could do some reverse engineering of terminal.exe if it still exists, or winbox when using mac-telnet.

Chupaka commented 5 years ago

EC-SRP5 is defined in IEEE 1363.2

KoenDeMol commented 5 years ago

Indeed, I tried to implement following the IEEE 1363.2 (https://tools.ietf.org/html/draft-liu-sipcore-ec-srp5-00), but they do not exactly follow the standard.

First message from client to "server" is Username-0x00-32bytehash (which I suppose is the public key). Then "server" replies with 32bytehash-0x00orOx01-16bytealwaysthesame (which I suppose is the server public key and shared secret) Then the client should response with a proof. Following the standard this could be a long hash since it's calculated with eliptic curve, but in case of Mikrotik it's always a 32 byte hash (I guess they do some SHA256 with it).

So, it's not completely clear what is being communicated, I can only guess for now. And also not clear what the final 32byte hash is representing and more importantly how to calculate it.

I didn't have much time yet and won't have in the next week, but if nobody else found it by then I will continue looking for it....

BrainSlayer commented 5 years ago

Am 10.08.2019 um 00:36 schrieb Koen De Mol:

Indeed, I tried to implement following the IEEE 1363.2 (https://tools.ietf.org/html/draft-liu-sipcore-ec-srp5-00), but they do not exactly follow the standard.

First message from client to "server" is Username-0x00-32bytehash (which I suppose is the public key). Then "server" replies with 32bytehash-0x00orOx01-16bytealwaysthesame (which I suppose is the server public key and shared secret) Then the client should response with a proof. Following the standard this could be a long hash since it's calculated with eliptic curve, but in case of Mikrotik it's always a 32 byte hash (I guess they do some SHA256 with it).

So, it's not completely clear what is being communicated, I can only guess for now. And also not clear what the final 32byte hash is representing and more importantly how to calculate it.

I didn't have much time yet and won't have in the next week, but if nobody else found it by then I will continue looking for it....

32 bytes? likelly just sha256 maybe with some voodoo init seeds. should be easy to reverse engineer once you find the right code to disassemble

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/haakonnessjoen/MAC-Telnet/issues/42?email_source=notifications&email_token=AB2WNE3HUZHNQRA73PXP3J3QDXWQVA5CNFSM4FBKLCS2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD376CEI#issuecomment-520085777, or mute the thread https://github.com/notifications/unsubscribe-auth/AB2WNEYECTKS4R4OFVQ7QZTQDXWQVANCNFSM4FBKLCSQ.

m0sia commented 4 years ago

Edited based on @ius and @jacob-baines comments.

As Mikrotik hinted us with the name "EC-SRP5" protocol used for Winbox and mac-telnet should be similar to SRP with original Diffie-Hellman (https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol). Instead of using multiplicative group of integers modulo p as in original DH it should be using elliptic curves("EC"). An elliptical Weierstrass curve Wei25519 makes 32 byte length messages that perfectly correlates with collected packets.

Crypto parameters assumptions

Step 0. Server

s = random() #salt x_s = H(salt,H(username+':'+password)) stored in the /rw/store/user.dat username, salt and hash password are now stored on the server instead of cleartext password V = x_s * G # V = verifier, G - Base point of the elliptic curve

Step 1. Client start by sending username and ephemeral public key A

I = username a = random() A = a * G Send(I, 0x00, A, 0x01)

Client packet 1 {username} {\x00} {32 bytes of public key data} {\x01}

Step 2. Server response by sending ephemeral public key B and salt

b = random() B = k V + b G Send(B,0x00,salt) #salt for user I

Server packet 1 {32 bytes of public key}{an extra byte}{16 bytes of salt}

Step 3. Client response by sending hash of session key

u = H(A, B) x_c = H(salt,H(username+':'+password)) K_c = (a + ux_c) (B - k x_c G) Send(K_c) or Send(H(K_c))

Client packet 2 {32 bytes of verification code} or {24 bytes if server did not return salt}.

Step 4. Server verifies received hash of session key

u = H(A, B) K_s = b (A + u V) Server compares calculated session key K_s with received K_c.

PS In general this protocol looks closer to ed25519/SPAKE2 more than SIP EC-SRP5.

PPS

I will do some testing to see if I can get the same hashes.

It is impossible due to unknown random values used by both client and server to generate public keys.

Following the standard this could be a long hash since it's calculated with eliptic curve, but in case of Mikrotik it's always a 32 byte hash (I guess they do some SHA256 with it).

Elliptic curve 25519 with sha256 gives you 32 byte messages

PPPS Protocol is vulnerable to user enumeration attack due to the fact that server doesn't reply with salt for non-existing usernames. Besides that protocol should be pretty bullet-proof.

ius commented 4 years ago

I've spent quite some time trying to replicate ECSRP::generateSRPResponse by reverse engineering WinBox (i.e. client only), some additonal observations:

I'm actually quite close to being able to reproduce the protocol from a client perspective, but I've gotten stuck on the last few bits of curve maths, that is: I fail to reproduce the output of what should be calls to WCurve::add (0x4b017d) and WCurve::mul (0x4b029d).

I'd have to clean up my notes (and Sage code) for it to be remotely useful to anyone. If someone happens to understand those two WCurve operations I might be able to piece it all together..


Somewhat related: looks like @jacob-baines's WinBox protocol reimplementation might benefit from this as well, by the way.

m0sia commented 4 years ago

Interesting observations, yeah no problem to use other curves like the Weierstrass one instead. I suspected they can use different base point, but bringing non-standard curve is even beyond that. Typical Mikrotik I would say :)

Please share your notes. If the curve math is the only remaining part you are really close. Also you might have the expected In/Out of addition/multiplication on the curve that will save time.

m0sia commented 4 years ago

One of the quick and dirty implementations of the curve math https://github.com/seb-m/wcurve/blob/master/wcurve.py

It is done for standard secp256r1 but it should work for Wei25519 just by changing a/b/Gx/Gy

jacob-baines commented 4 years ago

I'm unfamiliar with the interface that mac-telnet interacts with (and, unfortunately, mac-telnet in general), but as @ius mentioned above I sort of maintain a winbox implementation (although its more for offensive security purposes). Either way, by some dumb luck I'd actually been poking at this over the last two days. I'm happy to share what I know... which is precious little and potentially incorrect.

New Winbox establishes authentication/encryption via four packets. The first packet is from the client and it looks like this:

C: {length byte} {Handler in mproxy (\x06)} {username} {\x00} {32 bytes of public key data} {Useless trailing byte}

The public key, as mentioned, is a Curve25519 key. I've been using https://github.com/agl/curve25519-donna as my curve implementation. I previously used that library to get authentication with the web interface working again, so I thought it was probably a safe bet.

The server accepts the client's message and looks up the user's salt in /rw/store/user.dat. The server then generates its own key pair and replies with:

S: {length byte}{Handler in mproxy (\x06)}{32 bytes of public key}{an extra byte}{16 bytes of salt}

I'm not really sure what the Client does with this at the moment. My focus has been entirely on the server implementation. So, suffice to say the Client does something and then sends this:

C: {length byte}{Handler in mproyx{\x06}}{32 bytes of validation code}

So this is the part I'm working on. The server needs to generate the validation code on its own. The following is what I think it does / I want it to do, although I know for a fact that I'm not generating one variable correctly at the moment... so I could be totally wrong!

The server:

  1. Generates a SHA256 over both public keys (from packet 1 and packet 2).
  2. Extract the verify key from user.dat. This is generated when the user is added to the system. It's just the public key generated from the private key SHA256(salt,SHA256(username+':'+password)).
  3. Generate a session key using curve_donna(session, sha256(publicC, publicS), verify key) ... So my problem right now is I'm not getting the session key that I'm expecting. I'm actually a little confused with how the server is translating the verify key in this spot. Either way, once the correct value is generated there are a few additional SHA256 hashes to generate the matching verification code and then you can generate your session keys.

I'll share any code if I actually figure it out.

Edit: Oh and its worth mentioning that much of the Winbox logic is happening in /nova/bin/user. I'm not sure if the libucrypt.so ECSRP functions were inlined or what.

m0sia commented 4 years ago

@jacob-baines saw your Defcon presentation so I would say I am more than familiar with your work :)

mac-telnet is definitely using very similar algorithm from what you are describing and you've added couple missing parts on the server side that I missed In my assumption above.

  1. Generates a SHA256 over both public keys (from packet 1 and packet 2)

Exactly as my Step 3

  1. Extract the verify key from user.dat. This is generated when the user is added to the system. It's just the public key generated from the private key SHA256(salt,SHA256(username+':'+password)).

So you are saying that user.dat stores salt and SHA256(username+':'+password)? In the step 0 I assumed it is just password, but if you are sure I will update the description.

  1. Generate a session key using curve_donna(session, sha256(publicC, publicS), verify key)

/nova/bin/user is dynamically linked with libucrypt.so and imports all the necessary crypto functions. As @ius mentioned what you think is Curve25519 is most probably Wei25519 curve.

The calculation for session key is as follows with addition and multiplication on the Weierstrass curve. K_c = (a + ux_c) (B - k x_c G)

curve25519_donna API implementation was designed for both sides knowing the cleartext password to generate the session key. So I am not sure if it is straightforward to reuse curve_donna for required calculations.

jacob-baines commented 4 years ago

So you are saying that user.dat stores salt and SHA256(username+':'+password)? In the step 0 I assumed it is just password, but if you are sure I will update the description.

To clarify, for each user usr.dat stores a salt and curve25519 public key generated by creating a key pair with SHA(salt,SHA256(username:password)). Here's a snippet of code I wrote to implement this (forgive me, I never planned on sharing this code):

    std::string username("test");
    std::string sep(":");
    std::string password("lolwat");

    SHA256 test;
    test.init();
    test.update((const unsigned char*)username.c_str(), username.size());
    test.update((const unsigned char*)sep.c_str(), sep.size());
    test.update((const unsigned char*)password.c_str(), password.size());

    std::array<unsigned char, 32> result = { 0 };
    test.final(&result[0]);

    std::cout << "SHA(user:pass):" << std::endl;
    for (int i = 0; i < SHA256::DIGEST_SIZE; i++)
    {
        std::cout << std::hex << ((int)result[i] & 0xff) << " ";
    }
    std::cout << std::endl;

    SHA256 final;
    final.init();
    std::string salt("\x31\x7d\x14\x2c\x02\x2f\xf7\x8d\xd7\xf3\x03\xc0\x1d\x69\xef\xe1", 16);
    final.update((const unsigned char*)salt.c_str(), salt.size());
    final.update(&result[0], SHA256::DIGEST_SIZE);
    final.final(&result[0]);

    std::cout << "SHA(salt, SHA(user:pass)):" << std::endl;
    for (int i = 0; i < SHA256::DIGEST_SIZE; i++)
    {
        std::cout << std::hex << ((int)result[i] & 0xff) << " ";
    }
    std::cout << std::endl;

    //! Our curve25519 private key
    std::array<unsigned char, 32> verifier;
    std::array<unsigned char, 32> basepoint = {9};
    std::reverse(result.begin(), result.end());
    curve25519_donna(&verifier[0], &result[0], &basepoint[0]);
    std::reverse(verifier.begin(), verifier.end());
    std::cout << "user.dat public key:" << std::endl;
    for (int i = 0; i < 32; i++)
    {
        std::cout << std::hex << ((int)verifier[i] & 0xff) << " ";
    }
    std::cout << std::endl;
albinolobster@ns1:~/routeros1/poc/login/build$ ./login_test -i 192.168.1.50 -u admin --password lolwat
SHA(user:pass):
7b 43 17 60 ce 6a 2b 17 84 e3 14 2c 95 c8 da 2e 12 30 d4 90 42 80 6b ce 44 18 60 a5 30 95 7f f7 
SHA(salt, SHA(user:pass)):
1e 9f d0 96 f3 e6 19 86 23 22 88 ac 95 c7 f1 fe 91 e4 ed fb 6f 36 1e a1 7c 4e 31 58 d2 9e da 10 
user.dat public key:
2b cc f 24 1c 69 d3 c3 c6 dd 85 79 38 58 ec 1b d1 13 8d c7 55 61 46 4b 1b 22 b7 de a5 f2 ea f3
Screen Shot 2019-10-10 at 8 57 55 PM
m0sia commented 4 years ago

Interesting! Let me think about it.

m0sia commented 4 years ago

@ius and @jacob-baines this is what I came with:

Crypto parameters assumptions

Step 0. Server

s = random() #salt i = H(salt,H(username+':'+password)) v = i*G # v = verifier, G - Base point of the elliptic curve, multiplication on the Curve25519 https://github.com/haakonnessjoen/MAC-Telnet/issues/42#issuecomment-540857063

Salt and Verifier are stored in the /rw/store/user.dat

Step 1. Client starts by sending username and ephemeral public key Wc

Tc = random() Wc = Tc*G Send(username, 0x00, Wc , 0x01)

Client packet 1 {username} {\x00} {32 bytes of public key data} {\x01}

Step 2. Server responses by sending password entangled public key Ws and salt

Ts = random() Ws = CPEPKGP-SRP5-SERVER(Ts, v) Send(Ws,0x00,salt) #salt for user I

Server packet 1 {32 bytes of public key}{an extra byte}{16 bytes of salt}

Step 3. Client responses by sending confirmation value Cc

ic = H(salt,H(username+':'+password)) vc = ic * G Z = ECSVDP-SRP5-CLIENT (Tc, Password , A , Ws) Cc=H(0x04, Wc, Ws, Z, vc)

Client packet 2 {32 bytes of confirmation value Cc } or by some reason{24 bytes if server did not return salt}.

Step 4. Server verifies received confirmation value Cc with calculated Cc’

Zs = ECSDVP-SRP5-SERVER(Ts, v, Wc, Ws) Cc'=H(0x04, Wc, Ws, Zs, v) Server compares Cc’ with received Cc

m0sia commented 4 years ago

And here is python implementation, but for different domain/curve P-256

https://github.com/multiSnow/secure-gappproxy/blob/65776688cce58831386b1e6c67a7d7526ad5dbe8/fetchserver/bpkaspak/__init__.py

m0sia commented 4 years ago

IEEE p1363 describes it even better. Finally found it online:

https://web.archive.org/web/20131228182531/http://grouper.ieee.org/groups/1363/passwdPK/submissions/p1363ecsrp.pdf

m0sia commented 4 years ago

Regarding Wei25519 in fact it is just an alternative representation of points of Curve25519. I suck at ECC so got myself easily confused. Curve25519, Ed25519 and Wei25519 is just a representation form of the same elliptic curve. Different forms just provide benefits in doing different types of scalar multiplications. That is why Montgomery form of this curve multiplications worked for user.dat while Mikrotik is using math in Weierstrass form.

csurf commented 4 years ago

what's the status on this? any progress being made? I'd be willing to help out with testing. Unfortunately, I can't be of much help with the reverse engineering of the encryption algo, but I'd be willing lend an extra set of hands in order to help get this worked out.

bmiro commented 4 years ago

Another one with the same issue :(

mactelnet not working on 6.45.7 (current stable)

I think that many Mikrotik network administrator uses this tool maybe Mikrotik should give a hand...

BrainSlayer commented 4 years ago

Mikrotik give a hand :-) made my day. the only think mikrotik never cared of are third party developers and free software. mikrotik wants that you only use mikrotik software and hardware. welcome to this jail

Khisrav1 commented 4 years ago

Hello everybody! Few month ago I had a task to make Windows application which can connect to Mikrotik device by mac-telnet. After spending a lot of time serching and following this issue, I used other solution. We use second device Mikrotik with IP address, fortunately our customers usually have it in their networks. My application connects to it by SSH and from this device to other by mac-telnet, like this we can set IP address etc. Yes, this is not the best and optimal solution, but mac-telnet "device-device" will work, whatever Router-OS version. I hope this solution will be useful for someone.

eworm-de commented 4 years ago

Or you use CHR in a virtual machine... As bandwidth is not an issue even free license will do. Still annoying, just using mac-telnet would be much more convenient.

bmiro commented 4 years ago

I know that there are workarounds but I think we should keep this issue to improve the mac-telnet implementaton maintained by @haakonnessjoen, not to talk about the workarrounds without this software.

samm-git commented 4 years ago

Looks like i found oss implementation for the winbox. https://github.com/tenable/routeros/commit/8014a2b3c81f472c2ae3c9dc34a090c34fb0833b . Hopefully they using something +- same for other tools

m0sia commented 4 years ago

I guess that is the implementation from @jacob-baines or his colleagues for the web part of RouterOS 6.43..6.45 described in https://github.com/haakonnessjoen/MAC-Telnet/issues/42#issuecomment-540843415

jacob-baines commented 4 years ago

Yes, unfortunately, my routeros repo is not relevant to this blocker.

samm-git commented 4 years ago

...thats sad, also found that. Same problem affects my btest OSS implementation.

dbeinder commented 4 years ago

If anyone is still working on this: The android app uses a native library for crypto, so we have a x86 shared library that should be usable on linux: https://apkpure.com/mikrotik/com.mikrotik.android.tikapp/variant/1.3.12-XAPK Everything is in libnative.so (xapk contains > config.x86.apk contains > lib), it exports much more library functions than used by Java, it has symbols and is quite readable in IDA. Could be a used as a easy starting point.

Exported functions ```ECSRP::ECSRP(string const&,string const&,vector const&) 0005963C ECSRP::generateMSCHAPResponse(vector const&) 0005808A ECSRP::generateResponse(vector const&) 00059A1E ECSRP::generateSRPResponse(vector const&,vector const&) 000584AE ECSRP::getChallenge(void) 000599B4 ECSRP::verifyReply(vector const&) 00059B4C ECSRP::~ECSRP() 0005CD48 SecureProxy::SecureProxy(void) 0005B33C SecureProxy::disconnect(string const&) 0005C7C2 SecureProxy::getOutBuffer(void) 0005CCB8 SecureProxy::getState(void) 0005B64A SecureProxy::handle(nv::message &) 0005BDDA SecureProxy::isStateGood(void) 0005B71A SecureProxy::processFrame(uint,uchar const*,uint) 0005C9B4 SecureProxy::recvChallenge(uchar const*,uint) 0005C122 SecureProxy::recvResponse(uchar const*,uint) 0005C7D8 SecureProxy::sendChallenge(void) 0005BCA0 SecureProxy::sendFrame(int,uchar *,uint) 0005B548 SecureProxy::setState(nv::message const&) 0005B72A SecureProxy::startKeyExchange(string const&,string const&) 0005BACA SecureProxy::~SecureProxy() 0005B448 SecureProxy::~SecureProxy() 0005B516 WCurve::Point::Point(RedNum &&,RedNum &,RedNum &) 00055730 WCurve::Point::Point(WCurve::Point const&) 00059BC6 WCurve::Point::Point(void) 00055C40 WCurve::Point::operator=(WCurve::Point const&) 0005534E WCurve::WCurve(BigNum const&,BigNum const&,Reducer *) 00054FA0 WCurve::add(WCurve::Point &,WCurve::Point const&,RedNum *) 000550FE WCurve::add(WCurve::Point const&,WCurve::Point const&) 00059188 WCurve::dbl(WCurve::Point &,RedNum *) 000553AA WCurve::inv(WCurve::Point &&) 000592BC WCurve::map(ECPoint const&) 000555E6 WCurve::map(WCurve::Point &&) 000561D4 WCurve::mul(BigNum const&,WCurve::Point const&) 000557EE WCurve::mul2add(BigNum const&,WCurve::Point const&,BigNum const&,WCurve::Point const&) 00055D18 WCurve::~WCurve() 000567F4 Curve25519::Curve25519(void) 000565AC Curve25519::fromBin(vector const&) 000573D8 Curve25519::getX(vector const&) 000575EA Curve25519::getY(RedNum const&,uint,BigNum &) 00056838 Curve25519::isValid(WCurve::Point const&) 000572DE Curve25519::map(BigNum const&,uint) 00056EA4 Curve25519::map(WCurve::Point &&,uint *) 00057076 Curve25519::toBin(WCurve::Point &&) 000574A8 Curve25519::xBin(WCurve::Point &&) 00058426 Java_com_mikrotik_android_tikapp_JNILib_createProxy 000403ED Java_com_mikrotik_android_tikapp_JNILib_getRelayID 00040943 Java_com_mikrotik_android_tikapp_JNILib_handle 000407DF Java_com_mikrotik_android_tikapp_JNILib_oldDoHandshaking 00040C56 Java_com_mikrotik_android_tikapp_JNILib_oldGetRelayID 00040953 Java_com_mikrotik_android_tikapp_JNILib_oldHandle 00040A78 Java_com_mikrotik_android_tikapp_JNILib_oldInit 00040A24 Java_com_mikrotik_android_tikapp_JNILib_oldIsConnected 0004097C Java_com_mikrotik_android_tikapp_JNILib_oldJniLogin 000409A6 Java_com_mikrotik_android_tikapp_JNILib_oldProcessFrame 00040CCD Java_com_mikrotik_android_tikapp_JNILib_oldSetRelayID 00040966 Java_com_mikrotik_android_tikapp_JNILib_oldStartHandshake 00040BA4 Java_com_mikrotik_android_tikapp_JNILib_processFrame 000405B5 Java_com_mikrotik_android_tikapp_JNILib_proxyState 00040909 Java_com_mikrotik_android_tikapp_JNILib_rc4encrypt 00040EAE Java_com_mikrotik_android_tikapp_JNILib_setRelayID 00040930 Java_com_mikrotik_android_tikapp_JNILib_startKeyExchange 00040410 AES128::AES128(uchar const*) 00050A4C AES128::blockLength(void) 0005137A AES128::decrypt(uchar const*,uchar *) 00050EC0 AES128::encrypt(uchar const*,uchar *) 00050C20 AES128::~AES128() 00051352 AES192::AES192(uchar const*) 000511E6 AES192::blockLength(void) 000513B2 AES192::decrypt(uchar const*,uchar *) 00051260 AES192::encrypt(uchar const*,uchar *) 00051226 AES192::~AES192() 0005138A AES256::AES256(uchar const*) 0005129A AES256::blockLength(void) 000513EA AES256::decrypt(uchar const*,uchar *) 00051318 AES256::encrypt(uchar const*,uchar *) 000512DE AES256::~AES256() 000513C2 BigNum::BigNum(asn1::blob) 000515C0 BigNum::addToDigit(uint,uint) 000518A6 BigNum::bin(void) 000517B4 BigNum::bits(void) 00051888 BigNum::clamp(void) 00051772 BigNum::modBy2Pow(uint) 0005276A BigNum::operator*=(BigNum const&) 00052466 BigNum::operator*=(uint) 00052518 BigNum::operator+=(BigNum const&) 00051B52 BigNum::operator+=(uint) 00051CA6 BigNum::operator-=(BigNum const&) 00051CE0 BigNum::operator<(BigNum const&) 00051B06 BigNum::operator<<=(uint) 00051C42 BigNum::operator=(uint) 0005309A BigNum::operator>>=(uint) 0005269A BigNum::reduce(BigNum const&,uint) 00052826 BigNum::shiftLeftByDigits(uint) 00051934 BigNum::shiftRightByDigits(uint) 0005197A BigNum::str(void) 00051A1E BigNum::~BigNum() 000548E2 BlockCipher::~BlockCipher() 00050A40 0005AD04 CBC_MAC_Cipher::ciphertextLength(uint) 0005AD44 CBC_MAC_Cipher::decrypt(uchar *,uint,uchar *,uint,uchar *) 0005AF3E CBC_MAC_Cipher::encrypt(uchar *,uint,uchar *,uint,uchar *) 0005ADEE CBC_MAC_Cipher::setIV(uchar *,uint) 0005AD96 CBC_MAC_Cipher::~CBC_MAC_Cipher() 0005B0AA CBC_MAC_Cipher::~CBC_MAC_Cipher() 0005B0FE CBC_decrypt(BlockCipher &,uchar *,uchar const*,uint,uchar *) 0005A9C6 CBC_encrypt(BlockCipher &,uchar *,uchar const*,uint,uchar *) 0005A914 CFB128_decrypt(BlockCipher &,uchar *,uchar const*,uint,uchar *) 0005ABB7 CFB128_encrypt(BlockCipher &,uchar *,uchar const*,uint,uchar *) 0005AA64 DES::DES(uchar const*) 000503B0 DES::blockLength(void) 00050A30 DES::decrypt(uchar const*,uchar *) 00050812 DES::encrypt(uchar const*,uchar *) 00050518 DES::~DES() 00050A08 ECPoint::ECPoint(BigNum const&&,BigNum const&) 00056458 GCMCipher::ciphertextLength(uint) 0005A798 GCMCipher::decrypt(uchar *,uint,uchar *,uint,uchar *) 0005A4B0 GCMCipher::encrypt(uchar *,uint,uchar *,uint,uchar *) 0005A290 GCMCipher::setIV(uchar *,uint) 00059DAE GCMCipher::~GCMCipher() 0005A6E8 GCMCipher::~GCMCipher() 0005A73C GHASH::GHASH(uchar *) 00059EE2 GHASH::blockLength(void) 0005A8B0 GHASH::digest(uchar *) 0005A8D0 GHASH::digestLength(void) 0005A8C0 GHASH::update(void const*,uint) 0005A7D0 GHASH::~GHASH() 0005A7A8 HKPDF(Hash &,vector const&,vector const&,vector const&,uint) 0005B15C HMAC::HMAC(Hash *,char const*) 0004F0F0 HMAC::HMAC(Hash *,string const&) 0004F130 HMAC::HMAC(Hash *,void const*,uint) 0004F044 HMAC::blockLength(void) 0004F34A HMAC::digest(uchar *) 0004F258 HMAC::digestLength(void) 0004F374 HMAC::init(void) 0004F166 HMAC::update(void const*,uint) 0004F206 HMAC::~HMAC() 0004F3C6 Hash::blockLength(void) 0004F3B6 Hash::counterSize(void) 0004F39E Hash::digest(void) 0004EE10 Hash::~Hash() 0004F3EE HashImpl::finish(bool) 0004EF1A HashImpl::update(void const*,uint) 0004EE8A HashImpl::~HashImpl() 0004F3AE MD4::digest(uchar *) 0004F3F8 MD4::digestLength(void) 0004F5B8 MD4::init(void) 0004F442 MD4::transform(void) 0004F46A MD4::~MD4() 0004F590 MD5::digest(uchar *) 0004F5F0 MD5::digestLength(void) 0004F7BE MD5::init(void) 0004F5C8 MD5::transform(void) 0004F63A MD5::~MD5() 0004F796 MSCHAPv2::MSCHAPv2(string const&,string const&,vector const&,vector const&) 00057660 MSCHAPv2::getMasterKey(void) 00057B12 MSCHAPv2::getReply(void) 00057C26 Mod25519Reducer::mul(BigNum &,BigNum const&,BigNum const&) 00056526 Mod25519Reducer::mul(BigNum &,uint) 000564E4 Mod25519Reducer::reduce(BigNum &) 000564B2 Mod25519Reducer::sqr(BigNum &,BigNum const&) 0005656A Mod25519Reducer::~Mod25519Reducer() 00057634 MontgomeryReducer::MontgomeryReducer(BigNum const&) 000547BA MontgomeryReducer::fromBigNum(BigNum &&) 00054AAA MontgomeryReducer::fromBigNum(BigNum const&) 0005490A MontgomeryReducer::reduce(BigNum &) 00054B32 MontgomeryReducer::toBigNum(RedNum &&) 00054AE0 MontgomeryReducer::~MontgomeryReducer() 00054F28 MontgomeryReducer::~MontgomeryReducer() 00054F5E OldSecureProxy::OldSecureProxy(uint) 00041062 OldSecureProxy::disconnect(string const&) 00041DBE OldSecureProxy::doHandshaking(uchar const*,uint) 00041EB4 OldSecureProxy::getBufChar(void) 00042456 OldSecureProxy::getBufLen(void) 000424AC OldSecureProxy::getOutBuffer(void) 000423F0 OldSecureProxy::handle(nv::message &) 000414C0 OldSecureProxy::isConnected(void) 000423D8 OldSecureProxy::processFrame(uint,uchar const*,uint) 0004212A OldSecureProxy::sendFrame(int,uchar *,uint) 000413AC OldSecureProxy::startHandshake(void) 000418CA OldSecureProxy::~OldSecureProxy() 000412EE OldSecureProxy::~OldSecureProxy() 0004137A Prng::decrypt(uchar *,uchar *,uint) 00041ABA Prng::encrypt(uchar *,uchar *,uint) 00041600 Prng::init(uchar *,uchar *,uint) 000411A8 RC4::encrypt(uchar const*,uchar *,uint) 000514F2 RC4::gen(void) 000513FC RC4::setKey(uchar const*,uint) 00051454 RC4::skip(uint) 00051568 RedNum::emptyReducer 000FF444 RedNum::mod1 000FF43C RedNum::neg(void) 00059C58 RedNum::operator+=(RedNum const&) 00054C9A RedNum::operator-=(RedNum const&) 00054CF2 RedNum::operator=(RedNum const&) 00053F1C RedNum::operator>>=(uint) 00055590 Reducer::fromBigNum(BigNum &&) 00054660 Reducer::fromBigNum(BigNum const&) 00054624 Reducer::mul(BigNum &,BigNum const&,BigNum const&) 000546E0 Reducer::mul(BigNum &,uint) 00054724 Reducer::reduce(BigNum &) 00054EF6 Reducer::sqr(BigNum &,BigNum const&) 00054778 Reducer::toBigNum(RedNum &&) 0005469C Reducer::~Reducer() 00054C90 Reducer::~Reducer() 00054F00 SHA1::digest(uchar *) 0004F9BC SHA1::digestLength(void) 0004FA30 SHA1::init(void) 0004F7D0 SHA1::transform(void) 0004F802 SHA1::~SHA1() 0004FA08 SHA256::digest(uchar *) 0004FA40 SHA256::digestLength(void) 0004FD36 SHA256::init(void) 0004FA8C SHA256::transform(void) 0004FAC2 SHA256::~SHA256() 0004FD0E SHA384::digestLength(void) 0004FDE2 SHA384::init(void) 0004FD48 SHA384::~SHA384() 0004FDAA SHA512::blockLength(void) 0004FDD2 SHA512::counterSize(void) 0004FD9A SHA512::digest(uchar *) 0004FDF4 SHA512::digestLength(void) 000503A0 SHA512::init(void) 0004FE58 SHA512::transform(void) 0004FEAA SHA512::~SHA512() 00050378 SignedBigNum::add(BigNum const&,bool) 0005408C ```
BrainSlayer commented 4 years ago

@dbeinder good start for reverse engineering, but cannot be reused here. first for licensing issues, second its not cross platform compatible and requires also the android libc which is not compatible with glibc

brettus78 commented 3 years ago

/bump /subscribe

Harvie commented 3 years ago

Any news? Seems that mactelnet does no longer send passwords in plaintext.

samm-git commented 3 years ago

@Harvie just asking questions will not help. If you have skills and motivations to do that - make that and do PR. If not - this would not change. I personally not feeling myself motivated enough to spent days of work on replicating proprietary algo which could change anytime.

HarveyDanger007 commented 2 years ago

This cipher may provide useful to unlock the missing piece here: RSA/ECB/OAEPWithSHA-256AndMGF1Padding

object = new OAEPParameterSpec("SHA-256", "MGF1", MGF1Parameterspec.SHA1. PSource.PSpecified.DEFAULT)

Standard crypto libraries can be used for this.

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); keyPairGenerator,initialize((AlgorithmParamaterSpec)builder.setDigests(new String[]{"SHA-256", "SHA-512"}.setEncryptionPaddings(new String[]{"OAEPPadding"}.setUserAuthenticationRequired(true).build()).

Make sure to decode strings from Base64 as well.

comed-ian commented 2 years ago

@jacob-baines did you modify your donna curve implementation for the script you posted in: https://github.com/haakonnessjoen/MAC-Telnet/issues/42#issuecomment-540857063 ? I am trying to replicate your results in C++ to validate the curve equation and I'm computing a different public key using your pasted script and curve25519-donna.cpp from your RouterOS repo. The SHA hashes are the same, so the only thing I can think of is a different implementation of the elliptic curve.

edit - for reference:

SHA(user:pass):
7b 43 17 60 ce 6a 2b 17 84 e3 14 2c 95 c8 da 2e 12 30 d4 90 42 80 6b ce 44 18 60 a5 30 95 7f f7
SHA(salt, SHA(user:pass)):
1e 9f d0 96 f3 e6 19 86 23 22 88 ac 95 c7 f1 fe 91 e4 ed fb 6f 36 1e a1 7c 4e 31 58 d2 9e da 10
user.dat public key:
4f 5e c3 c2 b1 df 38 e0 19 11 b5 82 9b e7 57 34 98 f9 30 88 a0 91 b0 1a e2 21 5f 1a 83 df 3e 72