top of page

The Secrets of Wi-Fi Credentials

When you connect to a Wi-Fi 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.

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.

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 Wi-fi also provides a centralised 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 later fetched 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 each stored network.

Windows Native Wifi

The more recent Windows “Native Wifi” provides a better way to access the Wi-Fi credentials as it is the front end of any API call to automatically configure component configures, to connect or disconnect from / to a Wi-Fi 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 initialise our program as follow:

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.

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 plan 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 hold now 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 per 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:

Hide Copy 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;

// Get a handle to the desktop window

const HWND hDesktop = GetDesktopWindow();

// Get the size of screen to the variable desktop

GetWindowRect(hDesktop, &desktop);

// The top left corner will have coordinates (0,0)

// and the bottom right corner will have coordinates

// (horizontal, vertical)

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;

//This handle is needed to get the current background attribute HANDLE

hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

CONSOLE_SCREEN_BUFFER_INFO csbi;

//csbi is used for wAttributes word

if (GetConsoleScreenBufferInfo(hStdOut, &csbi))

{

//To mask out all but the background attribute, and to add the color

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

Featured Posts

Recent Posts

Archive

Search By Tags

Follow Us

  • Facebook Basic Square
  • Twitter Basic Square
  • Google+ Basic Square
bottom of page