MikroTik Authentication Revealed — Margin Research
MikroTik Authentication Revealed

MikroTik Authentication Revealed

Ian Dupont
by Ian Dupont
Joe Lothan
by Joe Lothan
Feb 10, 2022

In August 2019, MikroTik issued a RouterOS software update to version 6.45.1 which removed plaintext password storage on all routers that upgraded to the new firmware. While enhancing router security, this came as a blow for researchers, network administrators, and tinkerers who used customized tooling with MikroTik proprietary protocols such as MAC Telnet and Winbox. MikroTik has since failed to detail the new authentication procedure despite user requests for assistance. Margin Research is excited to illuminate the authentication procedure and offer Python proof of concept (POC) implementations for Winbox and MAC Telnet authentication.

SRP Overview

MikroTik's new authentication process uses a variation of Secure Remote Password (SRP). SRP is a type of Password Authentication Key Exchange protocol which incorporates the password within key exchange calculations. Knowledge of the correct password is required to compute the shared secret, so identical secrets indicate successful authentication. The SRP specification dates back over two decades and mainstream use gained traction in the recent past, highlighted by its use in OpenSSL. However, MikroTik does not implement this SRP protocol. Rather, RouterOS employs elliptic curve SRP (EC-SRP), specifically EC-SRP5. This protocol, which is much less prevalent, is defined by the following sequence:

User registration

  1. A user registers with the system, providing a username and password
  2. The router generates a 16-byte user salt and calculates the user’s password verifier, v, according to the equation v = ECDH(SHA2(salt | SHA2(username | “:” | password)))[1]
  3. The router stores username, salt, and v’s x coordinate, vx, in memory in /rw/store/user.dat

Figure 1: User registration providing username and password followed by the server generating a salt and password verifier v.

Client Authentication

  1. The client generates an ephemeral private key, Tc, and computes its ephemeral public key point Wc = ECDH(Tc). The client sends an authentication request including username and the point’s x coordinate, Wcx, as its ephemeral public key

    • Since each elliptic curve x coordinate has two y solutions, (x, y) and (x, -y), the client also transmits the parity of the generated point’s y coordinate, Wcy, to ensure the server calculates the identical point and not its negation
  2. The server retrieves the associated client’s salt and vx. The server then generates an ephemeral private key, Ts, and computes a password-entangled public key point, Ws = ECDH(Ts) + plot(SHA2(vx))[2]. Finally, the server responds with Wsx and salt

    • The server similarly calculates and transmits the parity of its public point’s y coordinate, Wsy

    Figure 2: Client generation of ephemeral private and public keys, including transmission of public x coordinate and y coordinate parity to the server. Server generation of password-entangled public key and response including the x coordinate and y coordinate parity, along with the password's salt.

  3. The client, now knowing the user’s salt and the server’s public key, can compute the shared secret and confirmation code as follows:

    • Calculates v's private key, vp = SHA2(salt | SHA2(username | “:” | password))
    • Plots v as the server did during registration and retrieves the x coordinate, vx
    • Calculates pseudo-random point e = plot(SHA2(vx)), as the server computed in the preceding step
    • Computes the server public point Ws using plot(Wsx)
    • Performs point subtraction to disentangle the password, calculating u = Ws - e
    • Calculates hash h = SHA2(Wc + Ws)
    • Performs scalar addition and multiplication to compute Tc + h.vp
    • Calculates the shared point, z, using elliptic curve multiplication with the previously calculated u point to find z = u(Tc + h.v)
    • Computes a client-side confirmation code, Cc = SHA2(h + zx)
  4. The client completes this handshake step by transmitting Cc

  5. The server computes the shared secret point, z, as follows:

    • Calculates hash h = SHA2(Wc + Ws)
    • Multiplies h.v over the elliptic curve
    • Computes plot(Wcx) on the elliptic curve, generating Wc
    • Adds Wc + h.v over the elliptic curve
    • Performs a final elliptic curve scalar multiplication using the server’s private key to yield z = Ts(Wc + h.v)
  6. The server validates the username and password by similarly calculating Cc and checking against the client provided value.[3]

Figure 3: Server validation of username and password using the client-computed hash h and client confirmation code Cc.

Encryption

The server and client can encrypt data once confirming an equivalent shared secret. MikroTik employs a MAC-then-encrypt strategy, leveraging AES-CBC as the encryption algorithm and HMAC as the authentication algorithm. RouterOS also uses separate send and receive keys for both HMAC and AES, labeled below as AESs, AESr, HMACs, HMACr. Both server and client generate these keys using “magic” strings and the HMAC key derivation function, HKDF. Messages prepend encrypted data with the unique random initialization vector, IV, used in AES-CBC mode.

Figure 4: Encryption and decryption of packets using server and client HMAC and AES keypairs.

A representative Winbox packet is shown below.

Figure 5: An intercepted Winbox data packet and delineation of its components: packet length, destination handler, message length, AES-CBC IV, and the appended encrypted message.

MikroTik Intricacies

This is undoubtedly a complicated procedure, and involves many more steps than standard elliptic curve Diffie Hellman or previous RouterOS protocols that simply checked password hashes. But the difficulty does not end there; this protocol is additionally complicated by MikroTik’s elliptic curve choices. RouterOS authentication uses a standard Montgomery curve, such as the popular donna25519 library, for some calculations, but not all. And it unfortunately does not use the NaCl/libsodium default Ed25519 Edwards curve. Rather, for either added efficiency or obscurity (or a bit of both), RouterOS performs most calculations over the standard Weierstrass curve: Y2=X3+aX+b. Furthermore, it calculates points in weighted projective space P(2,3,1) for even greater computation efficiency. This yields the Weierstrass equation: y2=x3+axz4+bz6, where Y=y/z3 and X=x/z2.

3-D plot of affine space
Figure 6: A Montgomery elliptic curve plotted in affine form where Z=1[4]
3-D plot showing projective transformation
Figure 7: Three-dimensional space which includes the projective plane at Z=1. (x, y, z) points are “projected” into affine form (X, Y) by calculating the intersection between the Z=1 plane and a line passing through the origin and point (x, y, z)[4]

Confused? Understandably so. The result of everything previously detailed is an authentication protocol with copious elliptic curve calculations across three different curve varieties: Montgomery curves, Weierstrass curves in affine form (Z=1), and Weierstrass curves in weighted projective space. This requires conversions to and from different curves and projective spaces, which makes following the authentication flow extremely difficult. The Python POC examples detail the sequence specifically, though the following high-level overview might be better suited for some interested readers:

  • The ECDH() equation generates a public key, pub, by multiplying private key, priv, by base point g on the Weierstrass curve in weighted projective space. This returns the point (xw : yw : zw). The function then converts the point to Weierstrass affine form and further converts to Montgomery form; specifically, the function calculates the Weierstrass affine coordinate X=x/z2 and converts X to Montgomery form

    • This is odd because the private key is multiplied over the Weierstrass curve, but the public key returned is the x coordinate in Montgomery form
  • The plot() function plots a given x coordinate on the Weierstrass curve in affine form. Therefore, it returns a Weierstrass point (X : Y : 1)

    • Interestingly, it actually performs this operation by first plotting the point on the Montgomery curve and converting the x coordinate by adding constant a/3 (where a is the Montgomery curve constant from the curve’s equation Y2=X3+aX2+X)
  • All point multiplication and addition over the Weierstrass curve are performed in weighted projective space. Thus, any returned parameters (e.g., shared secret point z, public keys Wc and Ws) must be converted to Weierstrass affine form

The picture becomes a bit clearer with those considerations in mind. The subtle choice of using the Weierstrass curve for public key calculations and converting all transmitted points to Montgomery form makes an obscure protocol even more challenging to understand. Considering all the intricacies outlined, it is no surprise that this authentication scheme was not reproduced in a public implementation until now.

Still With Us?

We hope so. This cryptographic reverse engineering effort had a little bit of everything: an obscure protocol, multiple elliptic curve definitions, and conversions from weighted projective space. If you are hungry for more, or want to put yourself through further technical torture, please see the source code and additional commentary in our GitHub repo. But wait! For those who are truly insatiable, there's more. The single best resource we used in reverse engineering was an unfinished IEEE submission draft courtesy of the Wayback Machine. In fact, MikroTik's implementation is nearly identical to the draft's proposed protocol. See if you can spot the minor nuances and marvel (as we did) that the shared secret remains the same.

[1] ECDH() accepts a private key as input and returns the associated elliptic curve public key, as in elliptic curve Diffie Hellman. This is replicated by the gen_public_key method in elliptic_curves.py’s WCurve class

[2] plot() accepts an x coordinate and returns the corresponding point on the elliptic curve. This is replicated by the lift_x method in elliptic_curves.py’s WCurve class

[3] The server may complete the authentication handshake by sending a server confirmation code, Cs, for the client to validate the server’s authenticity. In the case of the Winbox protocol, Cs = SHA2(h + Cc + zx)

[4] source: https://crypto.stackexchange.com/questions/40947/what-is-the-projective-space


Share this article:

arrow-up icon