Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
Welcome to the final installment of Introduction to Hacking Thick Clients! What a journey. We’ve learned a lot, but there’s just one last thing to mention – the memory!
Viewing Data
If a system is compromised by an attacker, values stored in memory may be exposed. Of course, if an attacker has compromised a system to the point of analyzing memory, there are a lot of other issues. But it is my opinion that an application should take responsibility for its security as much as possible and not rely on the security of the system it resides on. I go further into some solutions and their practicalities in my freshman blog post, but most of them are focused on limiting exposure, not eliminating exposure. If possible, assign sensitive data to mutable data types, such as arrays. Immutable data types like strings cannot be cleared from memory on command, but an array can be cleared from memory as soon as its values are no longer needed.
So let’s see how Beta Bank handles its sensitive data. I’ll let it slide if I’ve logged in and the password is still in memory. But in this example, I’ve already authenticated to the application, logged out, and meandered through my studio apartment for a few minutes. That’s enough leeway – time to examine the application’s memory. First, find the application in Task Manager, right click, and click “Create dump file.”
Now, a likely massive file has been generated. That’s everything from that process’s memory. And thankfully, friendly neighborhood Mark Russinovich has created a tool called Strings that will help to analyze this data by retrieving only the string values. I’ve taken the liberty of running the following line in PowerShell:
The resultant file should be a much more manageable size, and any text editor worth its salt can search for values of choice. The password I used when logging in was NetSPI1!. And it shows up a lot.
Another choice is to just open the entire dump file in a hex editor like HxD. The password appears just as easily and visibly, but so does a lot of other data.
Modifying Data
As I was coming up with examples for this portion of the blog, I realized that I first began modifying data in memory as a delinquent child. If you were at all like me growing up and wanted to impress people with lies, you may remember Cheat Engine. Just like my Beta Bank password, game data such as item counts, experience points, and high scores may be in memory plain as day. And these values could be located through a simple search, and the data at that address could be changed to a new value. That level 25 character was a few clicks away from being level 2,147,483,647.
These days, it is much more difficult to earn the admiration of my peers through lies. But gosh darn it, I’m going to try. I’ve heard from previous installments in this blog series that there’s a user named The_Chairman who has a lot of money. I’m going to withdraw a lot of it. The plan is to change my username in memory to The_Chairman. Since the space for the username will already be allocated, I’ve created an account with the same username length – The_Flairman.
This account is going to be swamped with overdraft fees. In Wireshark, a SQL statement to withdraw money is being sent with the username The_Flairman. For the sake of this example, let’s just use this as verification and pretend the traffic is encrypted. And that a bunch of other security vulnerabilities are patched while still allowing me to do something like this.
HxD is just as usable for editing memory as it is viewing, so we’ll stick with what we know. With Beta Bank running, click Open RAM to view the live memory for the process.
Searching for The_Flairman shows a number of locations where the username is referenced in memory.
I went ahead and replaced every instance of The_Flairman with The_Chairman.
Below is one of these instances.
Saving these edits will overwrite the current live memory.
Back in the application, it’s clear that these changes went into effect. But were they functional?
Using Wireshark, I was able to verify that the Withdraw button will now send a SQL command to withdraw a large amount of money with the username The_Chairman.
This wouldn’t be an issue at all if the SQL command wasn’t written with a complete lack of security awareness. In this application, there’s not a lot going on that would be “vulnerable” to memory values changing since authorization and storage is all on the server. Others may have simple boolean controls that can be bypassed through memory editing. Plenty of client-side control logic could be bypassed. I even found a simple example involving WinRAR, everyone’s favorite application, and how to bypass its registration control.
Further Reading
This blog post barely scratched the surface of memory analysis. The techniques outlined above are a few that are used during our routine testing of thick client applications. But they’re also entry points for diving further into malware analysis and reverse engineering. The following resources may be helpful starting points for additional learning:
Introduction to Hacking Thick Clients: The Conclusion
This series outlined quite a few thick client application testing methods and tools at a high level. While it never went especially deep on any one topic, I hope that it, along with BetaFast and Beta Bank, have opened the door for people to develop new security testing skills while having a bit of fun. This is a service line that really pushes creative thinking. Some of the exploitation steps I’ve seen in reports have made me reach out to coworkers and ask how they even thought to try them. Every once in a while, they unmute me and reply. But I digress . . .
Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
When testing any application, it’s important to have a good understanding of what’s going on beneath the surface. Consider the largest desktop application you’ve ever seen – that’s like half of the thick client applications I test. They’re monuments to teamwork and the human spirit. I once had six hours of demos for a test, and the scope didn’t even cover the entire application. Even if the code isn’t obfuscated, there’s no way I can read through and understand everything that’s going on. Thankfully, people have made tools to help.
API Monitor
One tool in our arsenal is API Monitor. This application monitors all of the API calls in a process and displays their parameters. In this example, the Summary is a list of API calls made during the authentication to BetaFast. Note the API Filter on the left. When I had all of them checked and authenticated to BetaFast, the Summary almost immediately had over 500,000 API calls logged. And that takes up some serious resources.
Monitoring BetaFast with API Monitor didn’t highlight any particular vulnerabilities from my experience, and almost all of the main functionality is contained to a single assembly, so API monitor won’t find the most sensitive data being sent between libraries. But it’s a valuable tool for displaying information on which system resources are being used, which files are being referenced or created, network calls, security calls, etc. Moving beyond information gathering and into attacks, it is possible to intercept and modify data before encryption, or to gather cleartext passwords and connection strings as they’re passed between libraries.
Issuing Calls
Welcome back to the high level. It’s much nicer up here. There’s a tool called .net SmokeTest that can be used to load and run individual method calls. No need to load the program and search for where a method call takes place, attach the application to a debugger, or write a single line of code. If a test requires writing a method, .net SmokeTest is an easy way to execute it rather than finding the right spot for it to be called in the application. But it’s especially useful for testing various data inputs in prewritten methods.
Take this encrypt method . . . please! Using a decompiler as outlined in the previous blog post, I found a hardcoded encrypted password in BetaFast.exe. Specifically, in the LoginViewModel class. Upon login, the password from the login form is encrypted and compared to this stored value.
The method is written below.
Even further below is a Decrypt method, but it’s never called in the application. Since the IV and Key are hardcoded in the application, it just needs a ciphertext input.
It makes sense that the next step would be calling Decrypt on the hardcoded encrypted password. First, start up .net SmokeTest and create an instance of the LoginViewModel class.
Open the LoginViewModel class and enter the stored password as the ciphertext parameter in the Decrypt method. The plaintext password is then displayed below.
There’s a NetSPI blog post from years back that highlights a Powershell alternative. The following code can be run in to call specific methods.
# Load Assembly
$Assembly = [System.Reflection.Assembly]::LoadFile("C:UsersnetspiDesktopExamples3-TierBetaFast.exe")
# Create binding
$BindingFlags= [Reflection.BindingFlags] "NonPublic,Instance"
# Target the class
$Instance = new-object "BetaFast.ViewModel.LoginViewModel"
# Call the methods
$Instance.GetType().GetMethod("Encrypt",$BindingFlags).Invoke($Instance,"Spring1980!")
$Instance.GetType().GetMethod("Decrypt",$BindingFlags).Invoke($Instance,"PE/jSP7tOGDLoZLXRvPtlA==")
Spring1980! is encrypted, and PE/jSP7tOGDLoZLXRvPtlA== is decrypted.
Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
Libraries and executables can be compiled with some additional security measures to protect against code exploitation:
Address Space Layout Randomization (ASLR) – An application’s locations in memory are randomized at load time, preventing attacks such as return-to-libc that lead to code execution by overwriting specific addresses.
SafeSEH – A list of safe exception handlers is stored within a binary, preventing an attacker from forcing the application to execute code during a call to a malicious exception.
Data Execution Prevention (DEP) – Areas of memory can be marked as non-executable, preventing an attacker from storing code for a buffer overflow attack in these regions.
Authenticode/Strong Naming – Assemblies can be protected by signing. If left unsigned, an attacker is able to modify and replace them with malicious content.
Controlflowguard – An extension of ASLR and DEP that limits the addresses where code can execute from.
Thankfully, NetSPI’s very own Eric Gruber has released a tool called PESecurity to check if a binary has been compiled with the above code execution preventions. Many of the thick client applications we test have installation directories filled to the brim with assemblies, and PESecurity is especially useful for checking a large number of files.
In the below example, PESecurity is used to check BetaBank.exe. It is compiled with ASLR and DEP. SafeSEH is only applicable for 32 bit assemblies. But the executable is unsigned.
Decompiling
Decompiling is one of my favorite parts of testing thick clients. As someone who has made far too many programming mistakes, it’s cathartic to find those of other programmers. By their very nature, .Net assemblies can be read as source code using tools such as the following:
This is because .Net assemblies are managed code. When a .Net application is compiled, it’s compiled to Intermediate Language code. Only at runtime is Intermediate Language code finally compiled to machine code by a runtime environment. .Net assemblies are so easy to “reverse” into source code because the Intermediate Language contains so much information such as types and names.
Unmanaged code, such as C or C++, is compiled down to a binary. It doesn’t run through a runtime like C# does with Common Language Runtime – it’s loaded directly into memory.
Information Disclosures
Managed Code
The following example will use the aforementioned BetaBank.exe, found in our BetaFast GitHub. It will also be using dnSpy as the decompiler of choice.
One of the first things I look for when testing a thick client application is hardcoded sensitive information such as credentials, encryption keys, and connection strings. Decompiling isn’t even required to find sensitive information – configuration files are great places to look. When a .Net assembly is run, it may search a configuration file for global values such as a connection string, web endpoints, or passwords that are referenced in the executable. Procmon and the steps outlined in the previous entry are very useful for identifying these files. There’s a connection string stored right in the Beta Bank’s config file. This can be used to authenticate directly to the database using a tool such as SQL Server Management Studio.
If you’re wondering about the 10.2.2.55 address, that’s the VM I’m running Docker in.
But for information disclosures in the source code, first decompile any of the application’s binaries. BetaBank.exe is loaded into dnSpy.
dnSpy has a solid search feature. I type in terms like “key,” “IV,” “connection,” “password,” “encrypt,” and “decrypt” to look for how the application handles encryption, authentication, and database connections. The application also has a filter in its search function. Below, I limited the search to only the Selected Files, but a search can also be limited to specific libraries and object types. Looks like there’s a hardcoded key in BetaBank.ViewModel.RegisterViewModel and BetaBank.ViewModel.LoginViewModel.
And searching for “password” shows a hardcoded encrypted password. Apparently the developer(s) implemented an authorization check on the client side. The username “The_Chairman” is directly compared to the value in the Username textbox, and the encrypted password is directly compared to the encrypted value of the value in the Password textbox.
The BetaEncryption class can be decompiled, showing some very custom encryption logic.
Unmanaged Code
As I mentioned at the beginning of this section, it’s not possible to have such a clean look at the source code when testing unmanaged code. Instead, we once again look to the CTO of Microsoft Azure for help. Strings is a tool in the Sysinternals suite that will output a list of strings saved in an executable. When analyzing memory dumps or unmanaged code binaries, I will retrieve the list of all the strings (most of them will probably look like nonsense), and then search for key terms like the ones above in a text editor of choice.
Modifying Assemblies
Using dnSpy, a class can actually be modified, and the binary can be recompiled. Below, I’ve reversed the Encrypt function, and I use a MessageBox to show the decrypted adminPassword when a user successfully authenticates to the application.
Saving the module as a new executable . . .
Finally, when I authenticate as a user, the MessageBox prints The_Chairman’s password.
A MessageBox is very useful to add to the application when trying to gather information. It’s like when I use a print statement instead of a debugger.
A Beta Bank developer may decide to patch this vulnerability by removing the hardcoded encryption key. However, Beta Bank’s custom encryption can still be reverse engineered to gather the key for decryption. Below, I’ve added a function that performs an XOR on the plaintext password and the encrypted password, thus exposing the encryption key. In the Decrypt function, I retrieve the key by passing a known plaintext password (my own) and its encrypted value.
But that was actually too much work. If we don’t need The_Chairman’s password for later, the username and encrypted password can just be placed directly into the Login function. Boolean logic can also be modified so that anything verified server-side, such as IsAdmin(), can be modified to always return true.
Obfuscation
Source code isn’t often this legible. Sometimes, code is obfuscated. And while there’s no security through obscurity, it sure does make the job of a security consultant a little more difficult.
Below is the BetaEncryption class from earlier after I applied some custom obfuscation by mashing my keyboard to rename the class, methods, and parameters.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public static class jinjnhglkjd
{
public static string dsfggd(byte[] erttr, string dgfhfgi)
{
byte[] plaintextBytes = Encoding.ASCII.GetBytes(dgfhfgi);
byte[] ciphertextBytes;
int messageLength = plaintextBytes.Length;
while (messageLength % erttr.Length != 0)
{
Array.Resize(ref plaintextBytes, plaintextBytes.Length + 1);
plaintextBytes[plaintextBytes.Length - 1] = 0x00;
messageLength += 1;
}
ciphertextBytes = new byte[messageLength];
int startingIndex = 0;
for (int i = 0; i < (messageLength / erttr.Length); i++)
{
for (int j = 0; j < erttr.Length; j++)
{
ciphertextBytes[j + startingIndex] = Convert.ToByte(plaintextBytes[j + startingIndex] ^ erttr[j]);
}
startingIndex++;
}
return Convert.ToBase64String(ciphertextBytes);
}
}
All of the functionality is still there. Some obfuscation techniques may go even further to make code illegible. But what this does is makes it more difficult to search through and reverse engineer the source code. The Assembly Explorer of dnSpy would be full of nonsense class names, and searching for specific terms such as “Encrypt” would not yield any results.
There are various tools for deobfuscation such as de4dot. Below is an example of what a deobfuscator may interpret my keyboard mashing obfuscation as. The classes at least have some sense of legibility, and methods are named some variation of method rather than dsfggd.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public static class class_1
{
public static string method_1(byte[] byte_1, string string_1)
{
byte[] plaintextBytes = Encoding.ASCII.GetBytes(string_1);
byte[] ciphertextBytes;
int messageLength = plaintextBytes.Length;
while (messageLength % byte_1.Length != 0)
{
Array.Resize(ref plaintextBytes, plaintextBytes.Length + 1);
plaintextBytes[plaintextBytes.Length - 1] = 0x00;
messageLength += 1;
}
ciphertextBytes = new byte[messageLength];
int startingIndex = 0;
for (int i = 0; i < (messageLength / byte_1.Length); i++)
{
for (int j = 0; j < byte_1.Length; j++)
{
ciphertextBytes[j + startingIndex] = Convert.ToByte(plaintextBytes[j + startingIndex] ^ byte_1[j]);
}
startingIndex++;
}
return Convert.ToBase64String(ciphertextBytes);
}
}
Obfuscation doesn’t secure code. But it makes browsing the source code all around more of a headache than reading someone else’s large codebase typically is.
Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
Many applications have been written by developers who could not resist the sweet siren song of storing sensitive information in the file system and/or registry. This has included social security numbers, credit card numbers, database connection strings, credentials, etc. Developers may encrypt this data for an added sense of security, but there could be an insecure or custom encryption implementation. If it is a necessity to store encrypted data, the key must always be stored in a separate location, such as securely in a database.
Focusing the Test
Testing for information disclosures in the file system and registry is quite simple – you just need to look at the files and registry keys used by the application and make sure there isn’t any sensitive information there. But applications often make an insane number of calls to files and registry keys. That’s why it is crucial to focus on the areas of the application most likely to write or read sensitive data.
Installer
If it’s possible to test the installation, always use this as an opportunity to look for writes to the file system or registry. It’s the perfect time for data to be written that the application will later read. Will the application be connecting to a database? Maybe the installation process writes the database connection string to a registry key. Maybe default administrator credentials are being stored in the file system.
Application
In the actual application, identify functional areas that may create or modify sensitive information. This always includes the initial database connection and a login form. Plenty of applications save some information for a Remember Me function, so determine how that’s being stored and retrieved. In the authenticated portion of the application, look for areas that specifically handle sensitive data.
Process Monitor will try and show you more information than you thought was possible, so the first step is to set up a filter. One item that Process Monitor can filter by is the PID of the application you’re testing, so be sure to check for that in Task Manager.
In this example, I also filtered by Event Class.
Registry
When opening BetaFast, a login form with a Remember Me function is immediately displayed.
With Process Monitor running, authenticate to the application and allow it to remember your credentials. Notice that two RegSetValue operations are performed – one for a username, another for a password. Also note how many events actually took place there but were ignored thanks to the filter.
The registry paths might not be as obvious as CredentialsPassword, so it’s more important to look for specific operations rather than paths. Registry Editor can be used to view the paths from the previous step, displaying cleartext credentials.
AccessEnum allows us to determine who can read and write registries and directories. The Credentials registry can only be read by Administrators and NT AUTHORITYRESTRICTED. Since this is in HKEY_CURRENT_USER, these credentials would only be exposed to highly privileged accounts and the current user. But they would still be exposed.
File System
BetaFast also has a payment details form. Process Monitor can analyze what the Load Payment Details button is attempting to load.
The application is looking for a file in C:ProgramDataBetaFastPaymentDetails with the current user’s name.
The next step is to submit the form with Save Payment Details checked and see what’s happening in the file system.
Unsurprisingly, the file that Load Payment Details was looking for is now saved.
The file also unsurprisingly contains cleartext credit card information.
AccessEnum shows that everyone can read this file.
Of course, this shouldn’t be surprising either. ProgramData is not user specific while the AppData folder is. Storing cleartext sensitive information in AppData would still show up in one of our reports, but it at least wouldn’t be readable by everyone on the system.
DLL Hijacking
This topic could be a blog or two of its own, and we even have one from 2012. But it also has to do with the file system, so it’s worth a brief overview here as well.
Applications will search for DLLs to load.
Some applications will not specify a fully qualified path.
Windows will search a predefined order of directories for this DLL.
Using Process Monitor, an attacker could identify this DLL because the application would be trying to open a .dll file that could not be found.
An attacker could then place a malicious DLL in one of the directories in the search order, causing the application to execute malicious code.
The problem with DLL Hijacking is in step 5. The issue mostly comes down to configuration issues. Microsoft highlights a few different attack scenarios and their severities in this article. They also have an article on how to prevent and identify these attacks. For even more additional reading on attack scenarios, check out this article as well as this one.
The main point I wanted to cover is that file system vulnerabilities don’t just come from writing data – they sometimes stem from what’s being read. Also, Process Monitor is again very useful.
Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
In this post, we’ll cover network testing in thick client applications and how it’s performed on different architectures. BetaFast is written with a three-tier architecture.
Tier 1: The client displays and collects data.
Tier 2: Web requests are sent to a server where business logic is handled.
Tier 3: A database server modifies and retrieves data for the application server.
Beta Bank is written with a two-tier architecture.
Tier 1: The client displays and collects data.
Tier 2: A database server handles business logic and performs data queries and modifications for the client.
One-tier architecture also exists, but it’s not applicable to this blog post because the client, business logic, and data storage are all on the same system. There can be more than three tiers, but the same methods and tools outlined below will apply.
Two-Tier
Wireshark
One of the first things I like to do when I begin a thick client application test is see what’s actually happening on the network. And to do that, I use a tool called Wireshark to capture and filter network traffic.
In this example, I’m testing the Beta Bank vulnerable application. With a proper capture filter in place, I’ll typically begin by just sort of doing things in the thick client. I’ll submit forms, navigate through the app, modify data, etc. Then, I’ll look through Wireshark for anything sensitive.
Is the database connection unencrypted?
Is sensitive data, such as social security numbers or medical information, transmitted in cleartext?
At one point, I authenticated to the application with the username “blogger.” Searching through the packet list in Wireshark, the name “blogger” appears in a Login procedure. The fact that this is even remotely legible means that the database connection is unencrypted, and anyone with access to this network is able to read this information just as easily. Thankfully, the password appears to be encrypted, so a hacker won’t be able to access my account.
Or will they?!
Any form of a password sent from a client, whether it’s cleartext, hashed, or encrypted, should be treated as the password itself. Sure, the password is encrypted. But knowing and sending this value to the server will authenticate this user to the application. There’s no additional defense provided by only obscuring a parameter value and not securely encrypting an entire transmission. Another value observed through wireshark is the output parameter for the Login procedure – GUID. In an authenticated portion of the application, the procedures accept a GUID as a parameter.
This is what we in the business call a “session ID.” Armed with only this value, an attacker will be able to perform authenticated actions as the blogger user as long as the session remains active.
But even that is making things more complicated than they need to be.
Echo Mirage
In this example, I am the harbinger of revolution – a veteran of the computer who has had enough of the wealthy elite. While you were smelling the roses, I was sniffing the network. And I captured blogger’s username and encrypted password from that Login procedure.
Now, a tool like Echo Mirage comes in handy. Echo Mirage allows TCP traffic to be intercepted and modified. I can authenticate to the application with the username “blogger” and an arbitrary password, then intercept the packet containing the Login procedure.
The encrypted password can be changed to the value captured by Wireshark.
As soon as I send this request, I’m in. Let’s see how much money this big shot blogger has.
Hmmm. Perhaps this blogging thing is more of a hobby. Let’s see who else is banking with Beta Bank. Using the techniques from the previous post in the series, the Withdraw button can be enabled.
Wireshark shows that this function is sending a SQL query.
Echo Mirage can be used to directly modify the SQL statement. It can’t change the length of the packet, but the remaining SQL can be commented out.
The server responds with the Users table. The account connecting to the database clearly has excessive privileges, but we now have info on every user.
There’s any number of attack vectors now. There’s still much to explore and modify in the database. But The_Chairman sounds very wealthy and very elite, and their encrypted password is practically begging to be used.
Hackers – 1
Wealthy Elite – 0
Three-Tier
Burp
Burp Suite is perhaps the tool I use most on the job. It’s invaluable for any application assessment that deals with HTTP requests and responses. If you haven’t heard of it or used it, there are going to be far more interesting guides out there than this on how powerful the tool can be.
If a thick client is built on a three-tier architecture, the network portion of the test will essentially be the same as testing a web application. First order of business is proxying the traffic. In the proxy tab of Burp, set up a listener on 127.0.0.1 and a port of choice.
Next, rather than proxying a browser through Burp, proxy the whole system.
There may be a lot of traffic that doesn’t belong to the application being tested. But this is the time for determining which requests are originating from the thick client. I only restrict the scope in Burp once I know the complete set of hosts the app is reaching out to. With BetaFast, I’ve determined the application is only sending requests to https://www.betafast.net:8080.
And now Burp can be used the same as in any web application test. Below, I sent a request to the Repeater tab and found SQL injection.
Closing Remarks
A lot of important things happen over networks, so make sure not to focus only on the client when testing thick client applications! What could Beta Bank and BetaFast have done differently to protect their systems? Well, a good amount. But here are a few examples:
Beta Bank
Encrypt database connections – Encrypted connections hide sensitive data from wandering eyes on a network. They also ensure that traffic cannot be modified as long as the encryption is implemented securely.
Principle of least privilege – Since Beta Bank connects to the database as a highly-privileged user, an attacker has free rein of the database should an exploit be found. If the account connecting to the database had the least possible privilege, an attacker could not pivot beyond normal functionality.
Salted and hashed passwords – Encryption is by its nature reversible. This will be highlighted further in an upcoming entry in this series.
Authentication logic – The password being sent over the network should not be directly compared to the password stored in the database. Otherwise, the encrypted password doesn’t even need to be decrypted or a hashed password cracked. The password should be sent as-is in an encrypted transmission. The server then salts and hashes the password and compares the resultant value to the salted and hashed password stored in the database.
BetaFast
Input validation and parameterized queries – Unlike Beta Bank, it’s not an issue that traffic can be intercepted and modified. The issue is that vulnerabilities exist in the way the application server processes input. Anything sent from the client should be validated by the server, and SQL statements should have parameterized queries.
As always, be sure to check out our GitHub for copies of BetaFast and Beta Bank. And be sure to tune in for further guides on hacking thick client applications!
Introduction to Hacking Thick Clients is a series of blog posts that will outline many of the tools and methodologies used when performing thick client security assessments. In conjunction with these posts, NetSPI has released two vulnerable thick clients: BetaFast, a premier Betamax movie rental service, and Beta Bank, a premier finance application for the elite. Many examples in this series will be taken directly from these applications, which can be downloaded from the BetaFast GitHub repo. A brief overview is covered in a previous blog post.
The thick clients we come across most often at NetSPI are written in C# and a bit long in the tooth. In these applications, there are two main GUI platforms: Windows Forms and Windows Presentation Foundation (WPF).
Windows Forms
Windows Forms was state of the art in 2002. And it’s not half bad in 2020! Basically, a form is composed of controls, which are your typical Windows objects such as text boxes, labels, buttons, etc. And each of these objects has its own properties, methods, and events.
Consider the following Windows Forms login form:
An administrator’s credentials are saved. Nice! Let’s steal those for later!
Darn! Thankfully, there are some tools at our disposal that allow us to view and modify controls. The first is WinSpy. Simply target a control and note that the masked value is stored in plain text.
Another similar tool for targeting Windows Forms objects is Window Detective. Selecting a control will highlight the object in the desktop window hierarchy. There, you can view its properties, including the elusive password.
Here’s another Windows Forms example. This application verifies the Role on the client-side and has the text box disabled so it cannot be changed.
But with WinSpy and Window Detective, we can set the text property to Admin.
And why not just change the Enabled property to true?
Some applications attempt to ward off SQL injections or other attacks by limiting the length of input or the types of input characters. But relying on any client-side control is a clear security risk.
Windows Presentation Foundation
WPF was released a day before Tenacious D in The Pick of Destiny and has aged just as well. It’s more complicated to work with than Windows Forms, but allows for a much more customizable design. A WPF-focused tool such as Snoop is needed for this platform.
Authenticate to BetaFast as a typical user and note that there are four menu items: Home, Shopping Cart, Settings, Logout.
By connecting Snoop to the application process, the design tree can be expanded to reveal an AdminVisibility property set to Hidden.
Changing this value to Visible reveals additional menu items restricted to administrators.
By itself, this is not a vulnerability. Any functionality in these restricted pages should have server-side authorization checks. However, this is often not the case. Below, a User is able to create an Admin account and view all of the accounts in the database.
The payment page of this application verifies the total rental cost in the client, not the server.
That means we can use Snoop to get a nice discount on these Betamax tapes – perhaps store credit?
Closing Remarks
Modifying GUI elements in thick clients is rather straightforward, especially with a set of polished tools. The remediation steps for any of these vulnerabilities are simple as well:
Perform authorization and input validation on the server
Do not hide sensitive data behind GUI designs
I’ll add another link to our BetaFast GitHub repo here. Be sure to try some of these examples out yourself, and be sure to tune in for our next installment of Introduction to Hacking Thick Clients.
Another vulnerable application is being unleashed into the world. Developed here at NetSPI, BetaFast is a vulnerable thick client application and valuable resource for practicing security testing.
History
Years ago, a NetSPI consultant was venturing into the basement of our office building to retrieve some fresh fingerless gloves from storage. Wandering through the scarcely lit corridors, littered with the forgotten remnants of tenants past, a treasure caught their eye. Tucked behind dust-caked boxes and an aged flock of Herman Miller Aerons, nestled under the flickering light of a bare incandescent bulb, a computer terminal stood proudly.
A team of NetSPI’s best and brightest took it upon themselves to learn of its secrets. Created by a defunct and untraceable company called BetaFast, this machine’s sole purpose was to dispense Betamax tapes, a medium from 1975. How long had it remained dormant? How long had it remained operational? Why?
Releases
The consultants worked tirelessly to reverse engineer the movie rental kiosk. Its software was written in a proprietary language and, unsurprisingly, was not secure.
The kiosk’s front-end and back-end code has been rewritten in C#. Throughout the upcoming weeks, we will be releasing blog posts outlining the vulnerabilities found in the software. The code can be found on our GitHub. Additionally, our team was able to find and preserve the original BetaFast website, but with an added link to our GitHub: https://www.betafast.net/.
Vulnerabilities
Two vulnerable applications have been released. One is BetaFast, a premier Betamax rental kiosk, written with three-tier architecture. The other is Beta Bank, a premier finance application for the elite, written with two-tier architecture.
BetaFast contains but is not limited to the following vulnerabilities:
Hardcoded Encryption Data
Hardcoded Encrypted Password
SQL Injection
Authorization Bypass
Missing server-side input validation
Cleartext Password Stored – Registry
Cleartext Sensitive Data Stored – Files
Weak File Upload Controls
Weak Input Validation
No Code Obfuscation
Beta Bank was written to include many of the above findings while highlighting some additional security flaws:
I’ve used NetSPI PowerShell tools and the PowerView toolset to dump information from Active Directory during almost every internal penetration test I’ve done. These tools are a great starting point for gaining insight into an Active Directory environment. Go seems to be gaining popularity for its performance and scalability, so I tried to replicate some of the functionality in my favorite PowerShell tools. goddi (go dump domain info) dumps domain users, groups, domain controllers, and more in CSV output. And it runs on Windows and Linux!
Before going any further, I want to thank Scott Sutherland (@_nullbind) for his help and mentorship. This work is based off of internal tools he created and none of it would be possible without him! This tool is also based on work from Antti Rantasaari, Eric Gruber (@egru), Will Schroeder (@harmj0y), and the PowerView authors.
So Why Go?
Go is fast and supports cross platform compilation. During testing, goddi managed to cut execution time down to a matter of seconds when compared to its PowerShell counterparts. Go binaries can also be built for Windows, Linux, and MacOS all on the same system. The full list of OS and architecture combinations are listed in the go GitHub repo. At the time of this blog’s release, goddi has been tested on Windows (10 and 8.1) and Kali Linux.
That isn’t to say that there aren’t any drawbacks with a Go implementation. The Microsoft ADSI API is much more flexible to work with, especially when creating LDAP queries to run under the current user’s security context. goddi requires domain credentials to be explicitly provided on the command line. This can be especially annoying in scenarios where a user’s credentials may not be known. If you get access to a box with local Administrator, but don’t have domain credentials yet, you can run PSExec to get local system. With local system, you can check if you have domain user privileges and then run PowerShell in this current context without domain credentials. This functionality is on the roadmap for future development.
Features
Check out the GitHub repo for an up to date list of features. goddi dumps…
Domain users
Users in privileged user groups (DA, EA, FA)
Users with passwords not set to expire
User accounts that have been locked or disabled
Machine accounts with passwords older than 45 days
Domain Computers
Domain Controllers
Sites and Subnets
SPNs
Trusted domain relationships
Domain Groups
Domain OUs
Domain Account Policy
Domain deligation users
Domain GPOs
Domain FSMO roles
LAPS passwords
GPP passwords
Run goddi with the example command below. The CSV output is dumped in the “csv” folder in the current working directory.
In the future, I would love to see if I can get this tool to operate closer to the ADSI model. Being able to run the tool in the user’s current context would be preferable from a testing standpoint. I would also like to improve how GPP passwords are gathered. Network shares to the target DC are mapped and mounted with the net use and mount commands. While GPP cpassword searching works with these commands, I have not gotten the chance to add robust error handling for corner cases when dealing with underlying OS errors.
GitHub Repo
Check out the goddi GitHub repo for install and usage instructions. I’ll be updating the features list and roadmap there. Comments and commits are welcome! I’m not a Go expert, so I would appreciate any constructive feedback.
If you’ve ever run across insecure PXE boot deployments during a pentest, you know that they can hold a wealth of possibilities for escalation. Gaining access to PXE boot images can provide an attacker with a domain joined system, domain credentials, and lateral or vertical movement opportunities. This blog outlines a number of different methods to elevate privileges and retrieve passwords from PXE boot images. These techniques are separated into three sections: Backdoor attacks, Password Scraping attacks, and Post Login Password Dumps. Many of these attacks will rely on mounting a Windows image and the title will start with “Mount image disk”.
Create a new VM through the New Virtual Machine Wizard. Follow the guided steps and make sure to choose the “Install an operating system from a network-based installation server” option. Check the settings menu after the wizard is complete and make sure “Legacy Network Adapter” is at the top of the Startup order.
Save and start the VM. The PXE network install should start and begin the Microsoft Deployment Toolkit deployment wizard.
Run through the wizard and start the installation task sequence for the target image. This can take a while.
Mounting a Windows image
Once the setup is completely finished (including the Windows operating system setup), you should have a working Windows VM. Make sure to shutdown the VM safely and download the Kali Linux iso. Go to the Settings menu and choose the location of your DVD drive image file.
Now, change the boot order so that “CD” is at the top of the BIOS startup order.
Save the settings and start the VM. Choose to boot into the “Live (forensic mode)”.
Once Kali is booted, mount the Windows partition with the following sample commands. Make sure to change the example /dev/sda2 partition use case.
fdisk -l
mkdir /mnt/ntfs
mount -t ntfs-3g /dev/sda2 /mnt/ntfs
Backdoor Attacks
1. Add a local Administrator during setup.
This is probably the simplest way to gain elevated access to the system image. After going through the Windows PE boot process, go back into the Settings menu for the VM. Set “IDE” to be at the top in the “Startup order” of the BIOS section.
Save the settings, start the VM, and connect to the console. The VM should enter the initial Windows setup process. Pressing Shift+F10 will bring up a system console. Note that this is different than pressing F8 during the Windows PE deployment phase. Enter the following commands to add your local Administrator user.
net user netspi Password123! /add
net localgroup administrators /add netspi
Check the Administrators group membership.
Now that the user has been created and added to the Administrators group, wait for the VM to finish setup and log in.
Once logged in, you will have local Administrator privileges! We can go a step further and obtain local system with PsExec.
PsExec.exe -i -s cmd.exe
The local system cmd prompt can be used to check if the computer account has domain user privileges. This can be a good starting point for mapping out the domain with a tool like BloodHound/SharpHound.
2. Mount image disk – Add batch or executable files to all users.
The shortcuts or files located in C:Users%username%AppDataRoamingMicrosoftWindowsStart MenuProgramsStartup will run when the users log in at startup. Change directories to the Administrator’s Startup directory and create a batch file with the following commands.
@echo off
net user "startup" "password" /add
net localgroup "Administrators" /add "startup"
The batch file will run when the Administrator user logs in. If this attack is combined with attack scenario #4, the Administrator user can log in with a blank password. Check to see that the startup user is created and added to the Administrators group after login.
3. Mount image disk – Overwrite sethc.exe or other accessibility options.
Replacing sethc.exe (Sticky Keys) is a classic privilege escalation technique. sethc.exe is located at %windir%System32sethc.exe. The command below copies cmd.exe and renames it to sethc.exe.
cp cmd.exe sethc.exe
If sticky keys is enabled, a local system cmd prompt will pop up when “Shift” is clicked five times in a row.
4. Mount image disk – Use chntpw tool to overwrite Administrator password.
The chntpw tool can clear the password for a Windows user. The SAM and SYSTEM files are located in the %windir%System32config directory.
The netspi user’s password is cleared and the account can be logged into without entering a password.
Password Scraping Attacks
5. Scrape VM memory files for passwords during install or login.
My colleague James Houston deserves a huge shout out for coming up with this attack. The general idea here is to use the snapshot or suspension functionality to capture passwords in the VM’s memory. This can be done during the actual PXE boot deployment process, installation, or login steps. This example will retrieve the password for the deployment service account during the MDT deployment process.
The deployment user is used to join computers to the domain in the “Computer Details” step of the deployment task sequence.
At this point, either suspend or take a snapshot of the VM’s current state. In Hyper-V, use the Checkpoint functionality to take a snapshot. Under the Checkpoint menu in Settings, make sure that “Standard checkpoints” is selected. This will ensure application and system memory is captured. The snapshot location is also set in this menu.
Browse to the snapshot file location and look for the corresponding files for your hypervisor.
Since this example uses Hyper-V, copy off the .VMRS file to search for passwords. I used Kali Linux along with strings and grep to locate the service account and password. Searching for keywords like “User” or “Password” is a great start if the username or password was not displayed during the Windows Deployment Wizard.
6. Mount image disk – Review local Unattend/Sysprep files.
Unattend and Sysprep files can contain passwords used for deployment and setup. The following locations contain files related to Sysprep.
%windir%Panther
%windir%PantherUnattend
%windir%System32Sysprep
In this case, the unattend.xml file has been sanitized but it is always worth checking these locations for passwords and sensitive information.
7. Mount image disk – Copy the SAM file and pass the hash with the Administrator account.
The SAM and SYSTEM files are located in the %windir%System32config directory.
This file can be copied off to your local machine and Mimikatz can be used to extract the hashes. The Administrator hash can be used in pass the hash attacks with CrackMapExec or Invoke-TheHash.
crackmapexec smb targetip -u username -H LMHASH:NTHASH
Invoke-SMBExec -Target 192.168.100.20 -Domain TESTDOMAIN -Username TEST -Hash F6F38B793DB6A94BA04A52F1D3EE92F0 -Command "command or launcher to execute" -verbose
This can be an extremely effective technique to elevate privileges if the domain has shared local Administrator passwords.
8. Mount image disk – Copy the SAM file and crack the Administrator account.
Like above, once the SAM and SYSTEM files are copied to your local machine, the Administrator account can be cracked with Hashcat or John the Ripper. A sample Hashcat command is below. Visit the hashcat wiki for setup and basic usage.
Once the techniques above have given access to the PXE booted image, we can dump passwords. Mimikatz is a great tool for password dumping.
sekurlsa::logonpasswords will dump passwords from LSASS memory.
lsadump::secrets dumps the LSA secrets.
vault::cred dumps saved credentials from the Credential Manager. However, if a saved credential is set as a domain password type, this command will not retrieve the credential successfully. The Mimikatz wiki has a good explanation on how to extract these credentials.
Mitigation and Prevention
There are inherent security risks associated with the use of PXE deployments that do not require authentication or authorization of any kind, especially on user LANs. It is highly recommended that PXE installations require credentials to begin the installation process. For example, this can be configured on a distribution server simply by checking “Require a password when computers use PXE” in System Center Configuration Manager.
One of the main takeaways from the attacks above is that applications or software that contain sensitive data should not be included in any images. In addition, shared local Administrator passwords or service account passwords should not be used on images (or anywhere in the domain). Images can be compromised and this should help reduce the risk to machines on the domain. Finally, PXE deployments should only be available on isolated networks. Check out these best practices from Microsoft for more information on securing PXE boot deployments.
I thought it would be interesting to perform an automated code review on it using CheckMarx, to see how they did related to security. The source consisted mainly of C++ code (376,545 lines of code) as well as code written in assembler. The assembler code was not scanned because CheckMarx (or any other automated code scanners) does not support assembler. What came out of the tool was interesting.
CheckMarx indicated that the risk in the code is:
The distribution of risk from Informational to High:
You have to remember that this code is from the 1980s. Many people did not have a concept of secure code and the development tools did not address security at all.
The top five vulnerabilities are as follows:
From the code that I looked at, most of the issues come from the use of unsafe functions. For example:
The function strcpy has been replaced by a safe function strncpy. The function strncpy combats buffer overflow by requiring you to put a length in it. The function strncpy did not exist in the 1980s. The code also contains 123 instances of the goto statement. For example:
From the MSDN web site, Microsoft states, “It is good programming style to use the break, continue, and return statements instead of the goto statement whenever possible. However, because the break statement exits from only one level of a loop, you might have to use a goto statement to exit a deeply nested loop.” I am not sure of the C++ syntax back in the 1980s, but maybe break, continue, and return statements did not exist.
Below is the complete list of issues found in the code:
Vulnerability Type
Occurrences
Severity
Buffer Overflow unbounded
180
High
Buffer Overflow StrcpyStrcat
22
High
Format String Attack
18
High
Buffer Overflow OutOfBound
12
High
Buffer Overflow cpycat
3
High
Use of Uninitialized Pointer
135
Medium
Dangerous Functions
58
Medium
Use of Uninitialized Variable
41
Medium
Char Overflow
35
Medium
Stored Format String Attack
19
Medium
Stored Buffer Overflow cpycat
11
Medium
MemoryFree on StackVariable
4
Medium
Short Overflow
2
Medium
Integer Overflow
1
Medium
Memory Leak
1
Medium
NULL Pointer Dereference
341
Low
Potential Path Traversal
24
Low
Unchecked Array Index
18
Low
Unchecked Return Value
11
Low
Potential Off by One Error in Loops
6
Low
Use of Insufficiently Random Values
3
Low
Potential Precision Problem
2
Low
Size of Pointer Argument
1
Low
Methods Without ReturnType
500
Information
Unused Variable
310
Information
GOTO Statement
132
Information
Empty Methods
9
Information
Potential Off by One Error in Loops
6
Information
This code is a good example of what not to do.
Programming languages and tools have evolved to make your application much more secure, but only if you teach your developers the concepts of secure coding.
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
Name
Domain
Purpose
Expiry
Type
YSC
youtube.com
YouTube session cookie.
52 years
HTTP
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Name
Domain
Purpose
Expiry
Type
VISITOR_INFO1_LIVE
youtube.com
YouTube cookie.
6 months
HTTP
Analytics cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.
We do not use cookies of this type.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.
We do not use cookies of this type.
Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.
We do not use cookies of this type.
Cookies are small text files that can be used by websites to make a user's experience more efficient. The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission. This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.