Prevent failed logon attempt window after failing to supply proper credentials to a remote desktop server using Network Level Authentication
Asked Answered
M

1

7

I am using the 'Microsoft Terminal Services Control Type Library' to establish a connection to a remote desktop server. I am looking for a way to prevent or suppress the 'Windows Security' prompt that is displayed when failing to provide a proper username/password combination when connecting to a remote desktop server that uses Network Level Authentication (NLA). The window looks something like this:

enter image description here

I have read about and tried every combination of settings that I can find online at this time and none of them have been successful. Here are a couple of the questions I found on stackoverlow that talk about this exact issue and supposedly get it resolved but the answers are not working for me:

AxMsRdpClient9 Dismiss login dialog

AxMsRdpClient6NotSafeForScripting AllowPromptingForCredentials

It may sound ridiculous but my ultimate goal is just to attempt connecting to an rdp server and purposely enter an invalid username/password and then disconnect when it fails. I do not care about actually connecting or displaying anything. If it matters, I am doing this in an attempt to trigger a failed logon attempt in the event logs on the remote server which another app will make use of later.

The code below already triggers a failed logon attempt in the event logs but I just cannot find a way to stop this failed logon box from popping up on the client machine and I would rather not resort to hacks that attempt to close the window after it is open. When the remote desktop server is configured to allow connections from computers running any version of remote desktop (less secure option) I do not have this same problem as the popup prompt is obviously part of the extra security that NLA offers.

I have already tried so many different combinations of settings for this control that my head is spinning. Here is one example that is modeled after one of the other stackoverflow questions above:

Public Class Form1
    Dim WithEvents oRemote As AxMSTSCLib.AxMsRdpClient6NotSafeForScripting

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        oRemote = New AxMSTSCLib.AxMsRdpClient6NotSafeForScripting
        CType(oRemote, System.ComponentModel.ISupportInitialize).BeginInit()
        oRemote.Dock = System.Windows.Forms.DockStyle.Fill
        oRemote.Enabled = True
        oRemote.Name = "OfficeWin7"
        Me.Controls.Add(oRemote)
        CType(oRemote, System.ComponentModel.ISupportInitialize).EndInit()
        oRemote.CreateControl()
        oRemote.Size = New System.Drawing.Size(800, 600)

        oRemote.Server = "IPADDRESS"
        oRemote.UserName = "TestAccount"
        oRemote.AdvancedSettings7.ClearTextPassword = "WrongPassword"

        Dim ocx As MSTSCLib.IMsRdpClientNonScriptable4 = oRemote.GetOcx()

        ocx.EnableCredSspSupport = True
        ocx.AllowCredentialSaving = False
        ocx.PromptForCredentials = False
        ocx.PromptForCredsOnClient = False

        oRemote.Connect()
    End Sub

    Private Sub oRemote_OnAuthenticationWarningDismissed(sender As Object, e As EventArgs) Handles oRemote.OnAuthenticationWarningDismissed
        MessageBox.Show("The credentials popup is now closing")
    End Sub

    Private Sub oRemote_OnAuthenticationWarningDisplayed(sender As Object, e As EventArgs) Handles oRemote.OnAuthenticationWarningDisplayed
        MessageBox.Show("The credentials popup is about to be shown")
    End Sub
End Class

Supposedly it is the ocx.PromptForCredentials = False line that should prevent this popup but it doesn't seem to make a difference if that value is set to True or False. I would almost assume by the property name that ocx.PromptForCredsOnClient might actually work, but again, it doesn't make a difference what I set that value to. I always get the same popup.

At this point I have no idea what I am doing wrong but my gut tells me that to get this working I need to instantiate the base AxMsRdpClient6NotSafeForScripting object as something else like AxMsRdpClient9NotSafeForScripting or even AxMsTscAxNotSafeForScripting which is the default type the control uses when I drop it onto a form. I have already tried a bunch of these combinations of settings however and am hoping someone can shed some light on the situation.

I should also mention I am open to alternative methods of connecting to a remote desktop server using .Net that don't involve using the Microsoft Terminal Services Control Type Library if there are any. I didn't have much luck finding them if they do exist but please let me know if I missed anything in my search.

Edit: To ensure that your own server is set up the same or similar to mine there are only two requirements that need to be met:

  1. Remote Desktop must be running on a version of windows that is Vista or newer
  2. Remote desktop must be set to use NLA. In Win7 the exact option text is: 'Allow connections only from computers running Remote Desktop with Network Level Authentication (more secure)'

At that point I don't care what options you change in the code as long as the server logs a failed login when you attempt to connect and the credentials box (or any other popups) never appear on the client side.

It appears that the easiest way to add the proper references that this code needs is to add the 'Microsoft Terminal Services control' from the COM tab to your toolbox and then drop a 'Microsoft RDP Client Control' onto a form. More information here: http://s.codeproject.com/Articles/43705/Remote-Desktop-using-C-NET

Here is the same code in C# in case that makes this question more popular:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private AxMSTSCLib.AxMsRdpClient6NotSafeForScripting withEventsField_oRemote;
        public AxMSTSCLib.AxMsRdpClient6NotSafeForScripting oRemote {
            get { return withEventsField_oRemote; }
            set {
                if (withEventsField_oRemote != null) {
                    withEventsField_oRemote.OnAuthenticationWarningDismissed -= oRemote_OnAuthenticationWarningDismissed;
                    withEventsField_oRemote.OnAuthenticationWarningDisplayed -= oRemote_OnAuthenticationWarningDisplayed;
                }
                withEventsField_oRemote = value;
                if (withEventsField_oRemote != null) {
                    withEventsField_oRemote.OnAuthenticationWarningDismissed += oRemote_OnAuthenticationWarningDismissed;
                    withEventsField_oRemote.OnAuthenticationWarningDisplayed += oRemote_OnAuthenticationWarningDisplayed;
                }
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            oRemote = new AxMSTSCLib.AxMsRdpClient6NotSafeForScripting();
            ((System.ComponentModel.ISupportInitialize)oRemote).BeginInit();
            oRemote.Dock = System.Windows.Forms.DockStyle.Fill;
            oRemote.Enabled = true;
            oRemote.Name = "OfficeWin7";
            this.Controls.Add(oRemote);
            ((System.ComponentModel.ISupportInitialize)oRemote).EndInit();
            oRemote.CreateControl();
            oRemote.Size = new System.Drawing.Size(800, 600);

            oRemote.Server = "IPADDRESS";
            oRemote.UserName = "TestAccount";
            oRemote.AdvancedSettings7.ClearTextPassword = "WrongPassword";

            MSTSCLib.IMsRdpClientNonScriptable4 ocx = (MSTSCLib.IMsRdpClientNonScriptable4)oRemote.GetOcx();

            ocx.EnableCredSspSupport = true;
            ocx.AllowCredentialSaving = false;
            ocx.PromptForCredentials = false;
            ocx.PromptForCredsOnClient = false;

            oRemote.Connect();
        }

        private void oRemote_OnAuthenticationWarningDismissed(object sender, EventArgs e)
        {
            MessageBox.Show("The credentials popup is now closing");
        }

        private void oRemote_OnAuthenticationWarningDisplayed(object sender, EventArgs e)
        {
            MessageBox.Show("The credentials popup is about to be shown");
        }
        public Form1()
        {
            Load += Form1_Load;
        }
    }
}
Marcus answered 7/6, 2016 at 2:28 Comment(5)
Shouldn't you try this question on another StackExchange site, that is more about seucrity ?Ablative
Did a quick search and I don't see too many other examples of this type of code up on another stack site. At least here there are a number of examples but most don't touch on my particular problem. I'm open to suggestions about where else to post this though because my project is on hold until I can solve this.Marcus
Maybe I'm wrong, but it might be worth a shot on Server Fault. Now maybe there you will be off-topic but you never know as I believe your question lies between SO and Server Fault...Ablative
Hi, I'm not sure whether you are still looking for an answer to this, but this question was linked to a similar one I answered today - the code is in Delphi, but the way to use the interfaces should be fairly self-explanatory.Dhumma
@Dhumma I love you. I never did get this working the way I wanted before. I was able to load up my old code and thanks to you pointing out that I should use IMsRdpClientNonScriptable5 instead of IMsRdpClientNonScriptable4, which has the .AllowPromptingForCredentials property, I can now generate a failed login on a remote machine without any kind of popup or user interaction. Exactly what I was trying to do months ago. THANK YOU!!Marcus
O
1

First off, you really need to make sure your test environment uses the latest update for RDP, especially if it's Windows 7 - just run Windows Update.

Next, focus on this line:

    ocx.EnableCredSspSupport = True

It is enabling a security support provider. I'm not an expert in the matter, but probably there is an internal routine saying:

Load locally stored credentials; if there aren't such - ask the user then crypt, hash, bla-bla, and store them locally, in order to use them next time

At this point the dialog, asking for credentials is popped up. I don't think you can influence this behavior, but, fortunately you can influence whether the provider takes place in the picture or not. So set this to False and see what happens.

Opiate answered 14/6, 2016 at 15:38 Comment(6)
Windows and other microsoft products are all fully updated. EnableCredSspSupport = true is the line that enables NLA support. Without this you cannot connect to 'newer' remote desktop instances running NLA - it just refuses to connect. If there is another way to enable NLA support on the client without using this line then I am all ears.Marcus
@JoeyJoeJoeJrShabadoo: Indeed. But you mentioned you can influence the server settings, right? Can you give it a try, just to isolate the problem... Disable NLA on the server side.Opiate
Please read my entire question. I already mentioned that the code works great when I disable NLA on the server. My problem is that I want to support both types of remote desktop connections and I am trying to get the NLA side working now. It already triggers the failed logon like I want but the problem is this code is intended to be run unattended and the credentials popup is messing me up.Marcus
@JoeyJoeJoeJrShabadoo Did you ever find a solution to this?Tickle
Yes @LorneCash, I've been super busy lately and haven't had a chance to add a proper solution but in the comments above @Dhumma helped me figure out my mistake. Instead of using IMsRdpClientNonScriptable4 you must use IMsRdpClientNonScriptable5, which has the .AllowPromptingForCredentials property that will solve this problemMarcus
@JoeyJoeJoeJrShabadoo Thanks so much for the reply, i should have read more.Tickle

© 2022 - 2024 — McMap. All rights reserved.