Prevent failed logon attempt window after failing

2019-02-22 05:03发布

问题:

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:

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;
        }
    }
}

回答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.