Passwords in WPF: How to find yours and how to prevent it being found






4.21/5 (20 votes)
How I can find your password and how to prevent it.
Introduction
Windows Presentation Foundation introduces a new control for entering passwords, but unfortunately, like the TextBox
of Win32, it is not too complicated to reveal the magic word hidden under the asterisks.
The fastest method is to analyze the VisualTree (the tree view of controls) with Snoop and go directly to read the password control.
In seconds, the password can be found, but we can make life more difficult for prying eyes with a few lines of code.
First, how can we hide a value of a property from Snoop?
Snoop displays both the dependency property and the CLR property, but we will create a dependency property specifically to trick the program (at least in the current version).
Create a simple Window
in a new WPF project called MainWindow
and to insert a normal DependencyProperty
called MyString1
with an easily recognizable default value:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty MyString1Property =
DependencyProperty.Register(
"MyString1",
typeof(string),
typeof(MainWindow),
new FrameworkPropertyMetadata("default value of MyString1")
);
public string MyString1
{
get { return (string)GetValue(MyString1Property); }
set { SetValue(MyString1Property, value); }
}
}
Snooping the MainWindow
(jargon that means opening Snoop, selecting our running application, clicking "Snoop this application", and looking in the tree view element MainWindow
), we can see and change the default value MyString1
. Add this code to the constructor of the class:
MyString1 = "nuovo valore";
The new value is shown by Snoop on Window
load, and everything works properly.
We now create a new DependencyProperty
called MyString2
, inserting the code for a normal CLR property in the CLR "wrapper" code:
public static readonly DependencyProperty MyString2Property =
DependencyProperty.Register(
"MyString2",
typeof(string),
typeof(MainWindow),
new FrameworkPropertyMetadata("default value of MyString2")
);
string _myString2 = "value of internal field of MyString2";
public string MyString2
{
get { return _myString2; }
set { _myString2 = value; }
}
How will Snoop behave now with MyString2
? Simple, it will continue to read the DependencyProperty
managed by the WPF property system while "the code" will use the CLR wrapper. In other words, Snoop reads MyString2
with the method GetValue, while the code reads MyString2
through _myString2
.
Perfect, we found how to get away from Snoop!
To better exemplify the enclosed source code AntiSnoopSample, a small program that I wrote to show the potential of a property dependency "badly written", I include a CLR property MyString3
to show that Snoop correctly reads the property dependency.
With what we have learned, let’s get back to our problem, analyzing the control PasswordBox
; we notice immediately that it is marked with sealed
(can not be inherited). This excludes a direct derivation of our control that stores the password in a field not easily readable by Snoop .
At this point, we can choose two roads: derive "our PasswordBox" from a TextBox
control, or try to work around this limitation by managing the PasswordChanged
event.
The event PasswordChanged
is triggered when the property value is changed. Then, we can copy the character inserted in a private field to our window, and replace the password of PasswordBox
with asterisks (in the attached code, the password is replaced with asterisks). Now, we try to recover our password with Snoop: we only get asterisks. In our application, we should use the private field (in the code, this.pwd
) to use the clear password.
This choice has several limitations. Because the control PasswordBox
is "closed", it is not possible to detect the position of the cursor text. It is not possible to replicate the password in private if it was a pasted text or if you press the Delete key or Backspace.
We will have to simply delete the entire password under any of these conditions. But in some scenarios, this could go well.
If we choose the path of derivation instead, we have greater flexibility, but also greater "responsibility": the code will have to locate the position of changes in the password and reproduce it in a field not visible in Snoop.
I implemented the control SecurePasswordBox
derived from a TextBox
for a illustration. The whole logic is executed in the override of the OnTextChanged
event and a clear password is recoverable through the usual Text
property of the control. Snoop does not read the Text property of their own SecurePasswordBox
because, as explained, its CLR wrapper implements a normal private field instead of calling SetValue
/ GetValue
.
Snoop calling GetValue
effectively reads "base.Text
" or the text contained in the inherited control but set to a series of asterisks, and our privacy is safe from the malicious use of Snoop. Mission accomplished.
NB: These solutions are not be definitive, a modified version of Snoop could very well read passwords even in these cases, or the attacker could analyze the memory of the program and find the password in each case. But, these solutions will defend the security of the application.
The advice is not to allow the arbitrary execution of any program in Full Trust in sensitive environments to avoid unpleasant surprises.