The article focuses on Wi-Fi stored credentials and how they can easily be fetched and decrypted.
Introduction
When you connect to a Wifi network and choose to save the credentials, they can be later fetched not only by Windows but by anyone who knows where and how to look for them. We developed a small utility named GetWifiData for the purpose of displaying any stored Wifi data. We will maintain this tool and publish updates in a dedicated web site. See also How Skype account details can be obtained.
Purpose of the Article
In the past (Windows XP), credentials were stored in the Registry, however since Windows 7, the credentials are stored in separate XML files. The Native Wifi also provides a centralized way to access the credentials, while Windows Cryptography provides the necessities to decrypt the encryption keys back. Several articles were published about Wifi credentials, but some of them are outdated which is why I wrote this article and created the utility to display all stored Wifi credentials.
How Wifi Credentials Are Stored
When you connect to a Wifi network and choose to save the credentials, they can be fetched later not only by Windows but by anyone who knows where and how to look for them.
The location is different among different versions of Windows.
On Windows XP, that place would be under the following Registry location:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WZCSVC\Parameters\Interfaces\
On Windows Vista, 7, 8, 8.1 and 10 that place would be in a file (not in the Registry):
C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\{Random-GUID}.xml
Under each interface, you can find separate files per stored network.
Windows Native Wifi
The more recent Windows “Native Wifi” provides a better way to access the Wifi credentials as it is the front end of any API call to automatically configure component configures, to connect or disconnect from / to a Wifi network. Further, Windows Native Wifi can store profiles on the networks it interacts with in the form of XML documents.
The source code of this article uses a simple method to fetch each element from these XML files and to generate a report of all stored Wifi credentials.
We initilize our program as follows:
WLAN_INTERFACE_INFO_LIST* pWirelessAdapterList = NULL;
dwResult = WlanEnumInterfaces(hWlan, NULL, &pWirelessAdapterList);
if (dwResult != ERROR_SUCCESS)
{
WlanCloseHandle(hWlan, NULL);
return result;
}
int nResCount = 1;
WLAN_INTERFACE_INFO* pWirelessAdapterInfo = NULL;
Interpretation of the XML Credential Files
Let's take a sample credential file as an example. The file name is:
C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\
{9D0E4D68-B83A-4745-8021-DE4381E509BF}\{5094B710-D0BF-473A-BC7D-A51D4885F681}.xml
As you can see in the photo above, the file holds the credentials of a Wifi network named Villa_Carriagehouse
from a great B&B hotel in Indinapolis. We stayed there several months ago (great hotel, by the way) so the credentials are stored in my PC and can be fetched, as you can see.
Going Back to Our Topic...
Windows uses the WLAN_profile Schema to define each WLAN's profile using the following XML elements:
- SSID - Both plain text and HEX versions can be found and contain the SSID of a wireless LAN.
- name - The '
name
' element is the SSID
in the form of plain text. - authentication - The '
authentication
' element specifies the authentication method to be used. - encryption - The '
encryption
' element specifies the type of data encryption to be used. - keyMaterial - The '
keyMaterial
' element contains a network key or passphrase. If the protected element has a value of TRUE
, then this key material is encrypted; otherwise, the key material is unencrypted. Encrypted key material is expressed in hexadecimal form.
How Wi-Fi Credentials Can Be Decrypted
When the Wi-Fi credentials are encrypted, they can be decrypted using Windows Cryptography. Microsoft cryptographic technologies include CryptoAPI, Cryptographic Service Providers (CSP), CryptoAPI Tools, CAPICOM, WinTrust, issuing and managing certificates, and developing customizable public key infrastructures.
First, we need to locate an element named keyMaterial
and isolate it into a string variable (strKey
).
Given strKey
is a CString
variable holding an encrypted Wi-Fi credential key (pass code), the following code block uses CryptUnprotectedData
to decrypt it back.
BYTE byteKey[1024] = { 0 };
DWORD dwLength = 1024;
DATA_BLOB dataOut, dataVerify;
BOOL bRes = CryptStringToBinary
(strKey, strKey.GetLength(), CRYPT_STRING_HEX, byteKey, &dwLength, 0, 0);
if (bRes)
{
dataOut.cbData = dwLength;
dataOut.pbData = (BYTE*)byteKey;
if (CryptUnprotectData(&dataOut, NULL, NULL, NULL, NULL, 0, &dataVerify))
{
TCHAR str[MAX_PATH] = { 0 };
wsprintf(str, L"%hs", dataVerify.pbData);
strKey = str;
}
}
As a result, strKey
will now hold the decrypted password for the given Wi-Fi network.
Wrapping Up
Now, all we need to do is to go over each Wi-Fi / Network interface and for each interface, go over each XML profile and fetch the Wi-Fi credentials of that profile, all into one report on screen and in a file.
To do so, here are some helper functions we use at Secured Globe, Inc.
Logging using WriteStatus()
We use logging for several purposes and in most cases, we want to see anything important that happens, on a Console window (even with UI based applications) as well as in a text file (the 'log') which can be used later.
void WriteStatus(LPCTSTR lpText, ...)
{
FILE *fp;
CTime Today = CTime::GetCurrentTime();
CString sMsg;
CString sLine;
va_list ptr;
va_start(ptr, lpText);
sMsg.FormatV(lpText, ptr);
sLine.Format(L"%s",(LPCTSTR)sMsg);
_wfopen_s(&fp, utilsLogFilename, L"a");
if (fp)
{
fwprintf(fp, L"%s", sLine);
fclose(fp);
}
wprintf(L"%s", sMsg);
}
Basically, instead of using printf
(or wprintf
), you just call WriteStatus
with the same arguments.
The output of the GetWifiData
program is generated using this function.
Ensuring Administration Privileges
Such program requires Administrator privileges. That can be achieved first by forcing the user to elevate.
Go to the project's properties. Then go to Linker -> Manifest File -> UAC Execution Level, and set the value to requireAdministrator (/level='requireAdministrator').
In addition, there is a way to detect the execution level during runtime. We use the following function:
BOOL IsElevated()
{
DWORD dwSize = 0;
HANDLE hToken = NULL;
BOOL bReturn = FALSE;
TOKEN_ELEVATION tokenInformation;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return FALSE;
if (GetTokenInformation(hToken, TokenElevation,
&tokenInformation, sizeof(TOKEN_ELEVATION), &dwSize))
{
bReturn = (BOOL)tokenInformation.TokenIsElevated;
}
CloseHandle(hToken);
return bReturn;
}
Then you can warn the user in case the program isn't running "As Administrator" which isn't really needed given the project's settings described herein, but for the purpose of describing the IsElevated()
function, I placed the following line in our source code:
if (!IsElevated()) WriteStatus(L"[!] Running without administrative rights\n");
Displaying Output Using a Fancy Console Window
For the purpose of showing the program's output during runtime, without having to wait for the log file, we use the following functions and functionalities:
To get the Desktop measures for the purpose of resizing the console to fit the maximum size possible (larger size = more data to be displayed), we use the following function:
void GetDesktopResolution(int& horizontal, int& vertical)
{
RECT desktop;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktop);
horizontal = desktop.right;
vertical = desktop.bottom;
}
Then we control the color of the text using the following function:
#define LOG_COLOR_WHITE 7
#define LOG_COLOR_GREEN 10
#define LOG_COLOR_YELLOW 14
#define LOG_COLOR_MAGENTA 13
#define LOG_COLOR_CIAN 11
void SetColor(int ForgC)
{
WORD wColor;
static int LastColor = -1;
if (LastColor == ForgC) return;
LastColor = ForgC;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hStdOut, &csbi))
{
wColor = (csbi.wAttributes & 0xF0) + (ForgC & 0x0F);
SetConsoleTextAttribute(hStdOut, wColor);
}
return;
}
Of course, this is only an example and you can define other colors and set more combinations of foreground and background colors to fit your needs.
Then to combine all together, here is an example:
SetColor(LOG_COLOR_CIAN);
if (!IsElevated()) WriteStatus(L"[!] Running without administrative rights\n");
WriteStatus(L"WiFi Stored Credentials Report\nproduced by GetWifiData,
by Secured Globe, Inc.\n\n");
SetColor(LOG_COLOR_MAGENTA);
WriteStatus(L"http://www.securedglobe.com\n\n\n");
SetColor(LOG_COLOR_CIAN);
The Source Code
This article is accompanied with a Visual Studio 2013 Ultimate, C++ project. The source code is independent and generates a single executable that can run on its own with no external DLLs needed and no pre-installation required.
History
- 9th January, 2017: Initial version