In this blog, Joe – one of our Testing Team Leads here at Secarma – takes you through his methods for testing compiled applications.

Setup & Introduction

In late December last year, I was testing a client’s thick application and it was behaving in a way I’d never seen before. So, over the holidays, I decided to copy & paste my way into having a “playground” to test clients. The “playground” is based on IdentityServer4 where the goal of the OIDC client is to retrieve an endpoint of an API after authentication with the identity server.

public class SecretController : Controller

{
    [Route("/secret")]
    [Authorize]
    public string Index()
    {
      var claims = User.Claims.ToList();
      return "secret message from SecApi";
    }
}

The client in this “playground” is a basic WPF client that after authentication, retrieves the endpoint above, and displays the messages from the API.

Compiled Applications

Testing The WCF Client

For the “playground” to be useful, we need to assume that we don’t have access to the source code, that we have credentials to retrieve the API endpoint, and that we’ve been asked to test the client’s security. With that in mind, the first step in testing the client should be the enumeration phase, to identify:

  • [ ] what language the client is written in and which architecture.
  • [ ] how the client interacts with resources.
  • [ ] any “low hanging fruit”.

Identifying How the Application is Built

Understanding how the client is built will be useful when it comes to exploitation. To identify the technology used, we can use a tool like CFF Explorer. Opening up the client in CFF explorer shows the following:

Great, so now we know that the client is written in .NET and is a 32-bit binary.

How does the client interact with resources?

This stage can be split into two different areas: we need to see what network communications the client makes, and then also what local files on the local file system the client interacts with.

Network Communication

To identify the network communications the client makes, we can use a network sniffer like wireshark or tcpview and use the WCF client.

Compiled Applications

As this “playground” is local, it’s a little more difficult to digest the results, however we can see there may be something at localhost:44305 and at localhost:44337.

File System Interactions

To identify any file interactions the client makes, we can use procmon.

It’s worth making a copy of the results or leaving this running while we interact with the WPF client.

Low Hanging Fruit

At this stage, we know how the application is built, and what communications/file interactions it makes. Now, it’s time to use the application and perform some basic analysis.

Strings

Sometimes developers will hard-code values in the application, and these could include a lot of useful information. To view these hard-coded values within the application, we can use strings.

The output of strings can be quite long, and you may need to look over it a few times. In this example there was the following useful information:

[...]

MainWindow.xaml
/WpfApp;component/mainwindow.xaml
WpfApp.Properties.Resources
IdentityServer Demo Login
https://localhost:44305/
wpf
openid SecApi
http://localhost/sample-wpf-app
Unexpected Error:
UserCancel
The sign-in window was closed before authorization was completed.
Bearer
https://localhost:44337/secret
d;L
z\V
6N5
WrapNonExceptionThrows
WpfAp

[...]

From that output, we can see that the client interacts with something at https://localhost:44305/ and at https://localhost:44337/secret. The application may use “Bearer” authentication. There’s also “openid SecApi” – although we don’t know the use case of this, but due to the name, we’ll still make a note of it.

PE Security

Thesedays, applications typically have ASLR & DEP enabled by default, however it’s never a good idea to assume anything when it comes to testing. Therefore, we’ll use the PESecurity script to quickly check.

PS C:\Users\Joe Thorpe\opt\PESecurity> Import-Module .\Get-PESecurity.psm1
PS C:\Users\Joe Thorpe\opt\PESecurity> Get-PESecurity -File "C:\Users\Joe Thorpe\source\repos\IDServer\WpfApp\bin\Release\WpfApp.exe"

FileName         : C:\Users\Joe Thorpe\source\repos\IDServer\WpfApp\bin\Release\WpfApp.exe
ARCH             : I386
DotNET           : True
ASLR             : True
DEP              : True
Authenticode     : False
StrongNaming     : False
SafeSEH          : N/A
ControlFlowGuard : N/A
HighentropyVA    : True

Reversing

It’s worth trying to reverse engineer the application to get a further understanding of how the application works. There are many tools to do this, but in this instance, we know that the application is written in .NET so we can use dotPeek from JetBrains.


Shown above is the source code of the WCF client in dotPeek. With this information, we can make some assumptions around how the client works, and what it interacts with.

[...]

var options = new OidcClientOptions()
{
    Authority = "https://localhost:44305/",
      ClientId = "wpf",
      Scope = "openid SecApi",
      RedirectUri = "http://localhost/sample-wpf-app",
      Browser = new WpfEmbeddedBrowser()
      };

[...]

We can see in the above code block the authority endpoint address, as well as the intended scopes. We then see that the embedded browser is used.

[...]

try
{
    result = await _oidcClient.LoginAsync();
}

[...]

else
{
    var name = result.User.Identity.Name;

    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
    var apiResult = await client.GetStringAsync("https://localhost:44337/secret");
    Message.Text = $"{apiResult}";
}

[...]

Later in the code, it shows the attempt to access the API after authentication.

Exploitation

We should now have enough information to test the WCF client at runtime. We know that the client makes HTTP requests to authentication and access the API resources.

HTTP Proxying

To identify the requests made by the WCF client, we’re going to need a HTTP proxy; I’ll be using Fiddler for this, but any HTTP proxy should do. Sometimes when testing client applications, they are not proxy aware, so in that case we can use an invisible proxy.

Now we can intercept the traffic from the WPF client, we can see the requests, and within Fiddler we can edit the requests, and reissue them. At this point we can treat this like any other web application assessment, just using the WCF client to interact with the server. For example: we get a verbose error if the username & password is not correct.

Debugging the Client at Runtime

Another area of interest would be to inspect the application at runtime. We can do this by attaching a debugger to the application and reviewing the call stack. Since this is a .NET application, I’ll be using dnSpy.

Opening up the application in dnSpy, we see decompiled source code, we can also set breakpoints to inspect the application at certain processes.

Once a breakpoint is hit, we can see the “Locals”, in this example, we can see the URI the client is authenticating to:

A Quick Summary

Okay, so that should give you a good idea of a basic approach to testing compiled applications. We’ve gone through the initial setup, testing the WCF Client, identifying how the app is built, and had a look at how the client interacts with resources. We’ve also analysed network comms, file system interactions, taken advantage of low hanging fruit, as well as looked at Strings, PE security, exploitation, HTTP proxying, and debugging the client at runtime.

Want more of Joe’s insights? Check out his LinkedIn, Twitter, or head to Secarma Labs’ Twitter for more offensive security musings from our testing team.

Interested in developing your pentesting knowledge? This year, we’re running a series of Hacking & Defending security training courses, and if you’d like to get involved, check out our Training page.

 

Latest

The Role of AI in Cybersecurity Friend or Foe

In this article, we'll explore the role of AI in Cybersecurity the potential benefits it provides, a...

Consulting on IoT and PSTI for manufacturers

IOT Self-Statement of Compliance for PSTI?

Often when our IoT consultants find themselves deep in conversation about the Product Security and T...

fintech penetration testing

Understanding the Importance of Fintech Penetration Testing

Fintech Penetration testing aids in the identification and remediation of vulnerabilities within an ...