Windows Development Patterns and Practices
A key to targeting Windows applications for bug bounty is to understand the patterns and practices employed by businesses and development teams in Microsoft environments. While many of these patterns and practices are similar to those found in other environments, the historical lifecycle of the Microsoft ecosystem plays an important role in how Windows application development has become what it is today. Many of the features we find in the modern Windows application development stem from Microsoft’s emphasis upon creating development tools that minimize the overhead and duplication of effort borne by application developers using their tools.
Some of the most important guiding principles to Windows application development include:
- Code reusability and extensibility
- Division of labor for large projects
- Modular application maintenance
As these bullet points highlight, a fundamental ethos in Windows application development teams is, “write once, use many.” Which is to say, one shouldn’t code a given capability more than once. Instead, application capabilities should be built into discrete components and integrated into applications in a modular fashion. And the way this is implemented in a Windows environment is via dynamic-link libraries (DLLs).
A Brief History of DLLs
Historically, desktop applications were written in a high-level programming language, such as C or C++, and then built into machine code at compile time. In the early era of Windows development, developers could choose between Visual C++, which compiled to machine code; or Visual Basic, which compiled to an intermediate language and wasn’t converted to machine code until runtime. As operating systems and development tools have progressed, we have seen an industry-wide shift toward runtime interpreted languages, such as Python, Java, Visual Basic, and Visual C#.
In the modern Windows environment, applications are developed using a variety of languages, nearly all of which are compiled to a common intermediate language and interpreted to machine code at runtime via the .NET runtime environment. A key driver of this development model is the ability to modularly distribute application functionality using object-oriented libraries via the .NET framework, wherein application components are packaged into so-called “assemblies,” which essentially correspond to the DLLs of yesteryear. As a result, Windows users and developers can dynamically load the capabilities of assemblies using everything from Visual Basic to PowerShell. In fact, one can even build an entire Windows forms-based graphical application using .NET through PowerShell.
DLL Use Cases
- Modularity for homegrown apps
- Third-party libraries
- Traditional ASP.NET apps
- SharePoint apps/extensions
- Third-party middleware integrations
DLL Bug Patterns
In light of the patterns and practices that modular application components introduce, and the business needs such technologies aim to satisfy, we can expect that there are some common bug patterns found in DLLs. Many of these share some parallels with web-based targets, such as web APIs, because both technologies aim to provide modular and standardized inter-process communication facilities. Some common bug patterns relevant to DLLs include:
- Deprecated code not removed from release builds.
- New application functionality prematurely included in release builds.
- Inadequate sanitization of DLL entrypoint/method parameters.
- Naïve routing of web application requests.
- Hardcoded passwords/keys/tokens.
- Insecure object deserialization/construction.
Tools – Microsoft ILSpy
One of the key benefits of just-in-time compiled programming languages, from the bug bounty hunter’s perspective, is the ability to easily revert the target application to human-readable source code. In the case of modern (.NET) Windows applications, we have several tools to do so, one of which is actually provided by Microsoft themselves. The tool is called ILSpy and it provides basic functionality to convert an application’s .NET intermediate language to human-readable source code.
ILSpy is also useful to identify method attributes, as depicted in the below screenshot, which can play an important role in the attack surface for .NET assemblies (DLLs). Specifically, when a .NET DLL is used by a Windows web application to handle incoming web requests, any method with the “[Remote]” or “[WebMethod]” attributes are exposed via HTTP requests. One common example of such methods is in the native SharePoint libraries, which provide remotely-accessible Microsoft Access and Excel web services via dynamically-loaded DLLs.
The Microsoft ILSpy tool can be downloaded for free from the Microsoft Store app in Windows 10 and above.
Tools – JetBrains DotPeak
Another useful tool for reviewing human-readable source code for .NET applications is the JetBrains product called “DotPeek.” This is a third-party tool but it’s free for non-commercial use. An advantage of this tool over ILSpy is that it allows you to target an entire directory and it attempts to decompile all of the assemblies in the target directory. The tool then allows you to search for text across all identified assemblies, which is especially useful to search for bug bounty-relevant data, such as hardcoded passwords, keys, and tokens.
DotPeek also allows some basic interrogation of pre-.NET DLLs, which can be very useful when a target application makes use of both .NET (a.k.a. “managed”) and pre-.NET (a.k.a. “unmanaged”) application modules. The boundary between managed and unmanaged application components is an ideal place to look for vulnerabilities, as it is often necessary to perform manual translation and (de)serialization of data passed between these components.
Tools – Netstat
Last but not least, the Windows native netstat tool is also very helpful when assessing Windows applications. It is increasingly common to find that Windows desktop applications expose lightweight HTTP services for things like peer-to-peer communication/integration with mobile apps. From an elevated command prompt, run the following command to list all listening ports along with the filename of the application associated with the open ports:
Netstat – bano
Once a listening service has been identified, you can test for common network and web-based vulnerabilities using simple command line tools, like telnet and nc.
It’s very helpful to understand the business environment(s) in which Windows applications are developed when targeting them for bug bounty. Whether developed by an in-house application development team, an outsourced contractor, or as an off-the-shelf product, many of the Windows development patterns and practices remain the same. Specifically, code reusability and extensibility are of paramount importance in a Windows development environment. From the hacker’s perspective, this insight can help us to better understand the modular structure of Windows applications and guides our understanding of the exposed attack surface. For instance, modules (DLLs) that provide standardized functionality for multiple applications or application components should be assessed for shared credentials and undocumented legacy functionality. Along the same vein, such modules may also include undocumented pre-production functionality, which may lack robust security controls. In contrast, modules (DLLs) that provide specialized functionality for specific application components should be assessed for hardcoded credentials and fragile business logic.
In each of these cases, the bug bounty hunter’s toolkit and approach are similar. We can use tools like Microsoft ILSpy and JetBrains DotPeek to easily decompile .NET applications to human-readable source code, which facilitates easy code review and keyword searches. For unmanaged (i.e. non-.NET) Windows DLLs, the IDA Pro continues to be the go-to tool for disassembly, reverse engineering, and keyword search capabilities. Lastly, when working with Windows DLLs, it can be helpful to think of the DLL’s “exported methods” as roughly equivalent to API endpoints in a web application. In fact, some web applications maintain a one-to-one mapping between API endpoints and exported DLL methods. In any case, the bug bounty hunter’s goal is to identify vulnerable functionality that is exported by the application’s DLLs and then follow all code paths to find ways in which the vulnerable methods can be reached. This last step also provides a great area for further research, as branching code paths often leads to an overwhelming number of possible execution flows that must be traced. While much research has investigated ways of paring down the many possible code paths, much more is needed and provides many opportunities for finding juicy bugs hiding in plain sight.
I hope this high-level overview of DLL hacking has been helpful and inspires you to mine your favorite Windows apps for gold nuggets. Until next time … happy hacking!
About the Author
Nerdwell is a systems and security engineer with a passion for bug bounty and vulnerability research. He currently works in critical infrastructure protection and has experience supporting technology in a variety of industries, ranging from manufacturing to healthcare. With over 20 years’ experiences, Nerdwell understands firsthand the challenges of building and supporting complex technology solutions securely. In addition to finding bugs and performing security research, Nerdwell enjoys networking and sharing knowledge with fellow hackers.
Get Started with Bugcrowd
Every minute that goes by, your unknown vulnerabilities leave you more exposed to cyber attacks.