RouterOS SSH + RADIUS login: duplicate MS-CHAPv2 authentication before password prompt is answered

Hi,

We have an issue with SSH and radius login.

It is related to this topic:

1. Summary

When AAA/RADIUS is enabled for the login service and a user connects over SSH, RouterOS sends a full,
cryptographically valid MS-CHAPv2 Access-Request to the RADIUS server (which proxies to Active
Directory via ntlm_auth) before the interactive password prompt has been answered by the user. This
first request is consistently rejected by the backend (real NT STATUS_LOGON_FAILURE / 0xC000006D,
not a timeout or malformed packet). A second Access-Request, with a freshly generated
MS-CHAP-Challenge / MS-CHAP2-Response pair, follows a few seconds later (after the user actually
types and submits the password) and is accepted normally.
This causes every single interactive SSH login to generate two logon events against the domain controller:
one failure and one success. This is a real risk for account lockout policies and adds unnecessary
noise/load on the RADIUS and AD infrastructure.

2. Environment

RouterOS versions tested 6.49.1, 6.49.19, 7.8 -> identical behaviour
AAA config use-radius=yes, RADIUS service=login
RADIUS server FreeRADIUS, proxying to Active Directory
Client Windows OpenSSH client (ssh.exe), tested with PubkeyAuthentication=no and forced legacy MACs

Capture method tcpdump on the RADIUS server correlated with a screen capture

3. Steps to reproduce

  1. Configure /radius with service=login pointing to a working FreeRADIUS server (AD backend via
    MSCHAPv2/ntlm_auth).
  2. Enable AAA use-radius=yes for the login service (System > Users > AAA).
  3. Open a fresh terminal (no previous connection attempts, no keys typed in the console beforehand).
  4. Connect: ssh user@domain@routeros-ip
  5. Wait for the password prompt. Do NOT type anything yet.
  6. Watch the RADIUS server log / a tcpdump capture on UDP 1812 in parallel.

4. Observed evidence

4.1 Wireshark capture on the RADIUS server (UDP 1812/1813)

No. Time Source Destination Protocol Info
1 0.000000 10.74.0.6 10.47.102.79 RADIUS Access-Request id=242 (User-Name=XXXXX ....

2 1.255904 10.47.102.79 10.74.0.6 RADIUS Access-Reject id=242 (MS-CHAP-Error: E=691 Authentication ....

3 7.119017 10.74.0.6 10.47.102.79 RADIUS Access-Request id=243 (same User-Name, NEW MS-CHAP-Challenge/......

4 7.332066 10.47.102.79 10.74.0.6 RADIUS Access-Accept id=243....

... pattern repeats identically for two further SSH login attempts (id=246/247 and id=250/251),
always: 1st Access-Request -> Access-Reject, 2nd Access-Request (fresh MS-CHAP-Challenge) -> Access-Accept.

4.2 FreeRADIUS log, same user/timeframe

Thu Sep 17 17:14:52 2026 : Auth: (15907776) Login incorrect (mschap: Program returned code (1) and output
'The attempted logon is invalid. This is either due to a bad username or authentication information. (0xc000006d)'):
[XXXXX@domain.local] (from client X.X.X.X port 0 cli X.X.X.X) from IP X.X.X.X

5. Analysis

The rejected and accepted Access-Requests are not retransmissions of the same packet: each carries a
different, freshly generated MS-CHAP-Challenge and MS-CHAP2-Response, meaning RouterOS
performs two independent full authentication rounds per SSH login. The first round uses a credential
already known to RouterOS at connection time (before the interactive prompt is answered), which is
rejected by the domain controller with a genuine STATUS_LOGON_FAILURE, not a network-level failure.
The second round, seconds later, uses the credential actually typed by the user and succeeds.
The behaviour is identical on 6.49.1, 6.49.19, and 7.8.

6. Why this matters

  • Every interactive SSH login against RADIUS/AD generates one extra, real failed-logon event on the
    domain controller before the user has even typed a password.
  • On environments with a low account-lockout threshold, this materially reduces the number of genuine
    mistyped attempts a user can make before being locked out.
  • It adds avoidable load/noise to RADIUS and AD authentication logs, complicating security monitoring.

7. Request to the community / MikroTik

Has anyone identified the root cause of this behaviour inside RouterOS's SSH/AAA client, or a supported
configuration option to suppress this first, premature authentication round?

Hi,

What AI says about that topic as you prepared whole post with it?

A practice that we still see far too often in stupid labs and outaded working environments:
using legacy or outdated (cracked) versions of RouterOS for testing, proof-of-concepts, or architectural reasoning.

It's time to upgrade to at least RouterOS 7.23... forget about 7.8...

I understand why this happens.
Sometimes, it is just easier to spin up an old CHR template you already have downloaded.
Other times, someone might say, "We are just testing basic routing, so the version doesn't matter."
But the truth is, testing on outdated software is not just inaccurate, it is a stupid waste of time.

Let me explain why.
First, the core routing and switching architectures in MikroTik have fundamentally changed.
If you are reasoning about hardware offloading, bridge VLAN filtering, or container support on a legacy version, you are literally testing the past.
RouterOS v7 introduced a completely new routing engine and kernel.
Testing on v6 to prepare for a v7 deployment means your results, your performance metrics,
and your configurations are obsolete before you even finish.
Second, consider security and stability.
When you build a proof-of-concept, you want to ensure it works reliably.
Legacy versions contain known bugs and vulnerabilities that MikroTik has already patched.
If your test fails on an old version, you might spend hours troubleshooting a bug that doesn't even exist in the current stable release.
Conversely, if it succeeds, you might deploy a configuration that breaks due to a known limitation in the older software.
Finally, we must think about features and syntax.
RouterOS evolves rapidly (disregarding the half-assed versions released... which cause more harm than good).
Commands change (fk!!!), menus move (sht!!!), and new protocols like WireGuard or modern BGP implementations simply do not exist or behave differently in older versions.
If your team is learning and reasoning based on old syntax, they are building technical debt from day one.
Testing is meant to replicate reality.
If our production target is a modern, secure network, our testing environment must reflect that.
Let’s stop wasting hours on "what-ifs" based on old code.

Use current versions instead of starting useless topics about old, abandoned versions.

To fix the extra Access-Request and failed logon event before typing a password, the issue stems from OpenSSH keyboard-interactive and password authentication probing sequence combined with how RouterOS maps SSH authentication methods to RADIUS/MS-CHAPv2.

When connecting via SSH, the SSH client (e.g., Windows OpenSSH) attempts authentication mechanisms offered by the server. If the server advertises keyboard-interactive or password, the client attempts a zero-length or cached/empty response first to check the authentication path, or RouterOS evaluates the initial SSH request by generating a dummy/empty MS-CHAPv2 response using a null password. Because MS-CHAPv2 cannot process an empty string, RADIUS forwards it to Active Directory, triggering 0xc000006d (STATUS_LOGON_FAILURE).

Workarounds and Solutions

1. Disable keyboard-interactive in the SSH Client

If your client automatically offers keyboard-interactive authentication alongside password authentication, force the client to strictly use standard password authentication:

Bash

ssh -o PreferredAuthentications=password user@domain@routeros-ip

  • How to verify success: Monitor the RADIUS server (tcpdump -i any port 1812). You should only see a single Access-Request packet generated after you press Enter on the password prompt.

2. Configure FreeRADIUS to Intercept Empty MS-CHAP Responses

Rather than letting the premature request hit Active Directory (which causes the account lockout counter to increment), configure FreeRADIUS to reject empty or zero-length response fields locally before proxying to ntlm_auth.

Add a check in your FreeRADIUS authorize or authenticate section (e.g., /etc/freeradius/3.0/sites-enabled/default):

Code snippet

if (&MS-CHAP2-Response && (&MS-CHAP2-Response == 0x00...)) {
    reject
}

Or check for empty user credentials before invoking mschap:

Code snippet

if (!User-Password && !MS-CHAP-Response && !MS-CHAP2-Response) {
    reject
}

  • How to verify success: Check /var/log/freeradius/radius.log or run freeradius -X. The first request will be rejected directly by FreeRADIUS with an internal reject, preventing ntlm_auth from querying Active Directory and preventing STATUS_LOGON_FAILURE events on the Domain Controller.

3. Adjust RouterOS SSH AAA Settings

Ensure SSH authentication methods on MikroTik are explicitly restricted to force a single authentication flow:

Code snippet

/ip ssh set authentication-forwarding=no

If you are using public key authentication combined with RADIUS fallback, ensure pubkey-via-radius=no is configured under /system routerboard or /ip ssh depending on your RouterOS version to prevent key-probing RADIUS requests.

How would you like to proceed?

With this the issue persist:
ssh -o PreferredAuthentications=password user@domain@routeros-ip
There isn't any change.

Even with -o PubkeyAuthentication=no -o KbdInteractiveAuthentication=no options.

I can confirm that the ms-chap2 response sends an empty password. Then we can try to filter these request in the radius.

I can't find pubkey-via-radius=no settings in the options.

I can see in version 7.24 stable (not in long term):
***) ssh - do not attempt automatic empty password login when RADIUS is used;

We tried in the last version, 7.24.4 and we can confirm it is fixed.**

Thanks to everybody for the help.
P.D. Next time we will try with the las version :slight_smile:

Yes, IA wrote the post :wink: with my tests.
I am updating a lab router to do the same test and I will come back with the results.

As I was writing: