By Het Mehta | Published: 2025-06-03 | Last Updated: 3/27/2026
Disclaimer
Performing penetration testing without explicit, written permission from the application owner is illegal and unethical. This content is provided for educational purposes only.
This checklist provides a structured approach to thick client application penetration testing. Thick client applications - also known as fat clients or desktop apps that run directly on the user's machine and interact with local resources, the operating system, and often remote backend services. Unlike web apps, there is no WAF between the attacker and the application logic: debuggers can be attached directly to running processes, memory can be dumped and searched, and binaries can be reverse engineered entirely offline. This makes thick client VAPT both uniquely rewarding and uniquely complex.
This checklist aligns with the OWASP Thick Client Application Security Verification Standard (TASVS) and the OWASP Desktop App Security Top 10. It covers key phases from reconnaissance to detailed exploitation and reporting.
Before starting a thick client security assessment, setting up a clean, isolated testing environment is critical. Use a dedicated VM to avoid contaminating your host machine and to allow clean snapshots between test runs.
The first step in any thick client penetration test is thoroughly understanding the application, like its architecture, technology stack, communication model, and local footprint. Knowing whether you're dealing with a 2-tier (client talks directly to DB) or 3-tier (client talks to app server which talks to DB) architecture fundamentally shapes the attack surface and testing priorities.
# Quick file type identification on Linux/macOS
file /path/to/thickclient.exe
# Check if it's a .NET assembly on Windows (PowerShell)
[System.Reflection.AssemblyName]::GetAssemblyName("C:\path\to\thickclient.exe")
# Use Detect It Easy (DIE) or CFF Explorer for GUI-based identification
# On Windows, use Process Explorer to inspect loaded DLLs
# On Linux, list open files for a running process
lsof -p <PID> | grep "\.so"
lsof, file.# Windows: list active connections with PID
netstat -ano | findstr ESTABLISHED
# Linux: list listening ports with process names (use ss, not netstat)
ss -tulpn
# Windows: real-time view using TCPView (Sysinternals)
# Procmon filter to catch only app file/registry writes
# Process Name is <app_name>.exe AND Operation is WriteFile OR RegSetValue
ss/netstat, Regshot, OLE/COM Object Viewer, RPCView, PipeViewer.Static analysis of thick client applications involves examining binaries, decompiled code, and configuration files without running the application. This phase is where you'll find hardcoded credentials, weak cryptography, insecure storage patterns, and missing binary protections โ all before writing a single exploit.
# Unpack a UPX-compressed executable
upx -d /path/to/packed_executable.exe
# Detect It Easy (DIE) will identify packer, compiler, and framework
# Extract Electron app.asar
npx asar extract app.asar ./extracted_source
# Then search the JS source for hardcoded secrets, nodeIntegration=true, etc.
grep -r "nodeIntegration\|contextIsolation\|allowRunningInsecureContent" ./extracted_source
# Extract and grep strings from a binary
strings /path/to/executable | grep -iE "password|api.key|secret|token|bearer|jdbc"
# On Windows using findstr
findstr /s /i "password api_key secret token" C:\path\to\app\*.*
strings.A key thick client VAPT check that is often missed: verify that compiled binaries have modern exploit mitigations enabled. Missing protections raise the exploitability of any memory corruption bug found later.
# PESecurity (NetSPI) - check ASLR, DEP, SafeSEH, CFG, Authenticode on all binaries
Import-Module .\Get-PESecurity.psm1
Get-PESecurity -directory "C:\Program Files\YourApp\" -recursive | Format-Table
Get-PESecurity -directory "C:\Program Files\YourApp\" -recursive | Export-CSV results.csv
# Sigcheck - verify code signing
sigcheck -a -u "C:\Program Files\YourApp\app.exe"
# BinSkim (Microsoft, actively maintained BinScope successor)
binskim analyze "C:\Program Files\YourApp\app.exe"
<!-- Example: Insecure .NET App.config -->
<configuration>
<appSettings>
<add key="ConnectionString" value="Server=db01;Database=app;User=sa;Password=P@ssw0rd"/>
<add key="DebugMode" value="true"/>
</appSettings>
</configuration>
grep / findstr, OWASP Dependency-Check.// Insecure: writing credentials to a plaintext file
FILE *fp = fopen("session.dat", "w");
fprintf(fp, "user=%s&pass=%s", username, password);
fclose(fp);
/* Insecure: always-true hostname verifier in Java */
HostnameVerifier allHostsValid = (hostname, session) -> true; // NEVER do this
// Buffer overflow: no size check
char buffer[64];
strcpy(buffer, user_input);
// SQLite injection: unsanitized user input
sprintf(sql, "SELECT * FROM users WHERE name='%s'", user_input);
grep / rg / findstr.Dynamic analysis means attacking the running application. This is where thick client pentesting gets truly unique: you can attach a debugger, patch memory at runtime, hook API calls, inject DLLs, and manipulate the GUI to reveal hidden functionality. The local execution model means there's no server-side safety net to catch your attacks.
GUI manipulation is a critical and often overlooked thick client attack surface. Disabled buttons, hidden fields, and masked inputs can all reveal security controls that exist only at the UI layer.
# WinSpy++: right-click a password field โ Properties โ Style tab
# Change "Password" style bit to reveal plaintext
# Or use Spy++ (VS) / Accessibility Insights to read control content directly
# Procmon filter for app-specific file and registry writes
# Process Name: <app_name>.exe
# Operation: WriteFile OR RegSetValue
# Result: SUCCESS
# Windows: check effective permissions
icacls "C:\Program Files\YourApp"
accesschk -wvu "Everyone" "C:\Program Files\YourApp"
# Linux: check permissions
ls -la /opt/YourApp
icacls, ls.# Check registry key ACLs
accesschk -kqsv "HKLM\Software\YourApp"
# Read a specific value
reg query "HKLM\Software\YourApp" /v DatabasePassword
regedit), AccessChk.# Wireshark filter for plain HTTP basic auth
http.authbasic
# Wireshark filter for a custom protocol port
tcp.port == 12345
# Force app traffic through Burp via ProxyCap: set SOCKS5 proxy to 127.0.0.1:8080
testssl.sh, sslyze.# Dump process memory with Process Hacker 2 / System Informer
# Right-click process โ Properties โ Memory โ Save All
# Search a memory dump for strings
strings process_dump.dmp | grep -iE "password|token|secret"
# Use Volatility for forensic-style memory analysis
vol.py -f memory.raw --profile=Win10x64 procdump -p <PID> -D ./output/
// Frida: hook a .NET method to log arguments and force return value
// Example: bypass a license check returning false
const LicenseManager = Java.use("com.vendor.app.LicenseManager");
LicenseManager.isLicensed.implementation = function() {
console.log("[*] isLicensed() called - forcing return true");
return true;
};
// Hook Windows API: intercept data before encryption
const CryptEncrypt = Module.findExportByName("advapi32.dll", "CryptEncrypt");
Interceptor.attach(CryptEncrypt, {
onEnter(args) {
const len = args[5].readUInt();
console.log("[CryptEncrypt] plaintext:", args[4].readByteArray(len));
}
});
# Python: connect to a Named Pipe and write a payload (Windows)
import win32file, win32pipe
handle = win32file.CreateFile(
r"\\.\pipe\TargetAppPipe",
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
0, None, win32file.OPEN_EXISTING, 0, None
)
win32pipe.SetNamedPipeHandleState(handle, win32pipe.PIPE_READMODE_MESSAGE, None, None)
win32file.WriteFile(handle, b'{"action":"adminCommand","user":"attacker"}')
result = win32file.ReadFile(handle, 4096)
print(result[1])
win32file.CloseHandle(handle)
# Procmon filter for DLL hijacking opportunities
# Operation: CreateFile
# Path ends with: .dll
# Result: NAME NOT FOUND
# Then check if you have write access to that directory
# Find unquoted service paths
wmic service get name,pathname,startmode | findstr /i "auto" | findstr /iv "c:\windows\\" | findstr /iv """"
# Check service binary permissions
icacls "C:\Program Files\YourApp\service.exe"
sc qc "YourService"
icacls, sc, wmic, PowerSploit / PowerUp.# Fuzz a command-line argument with a long string
./thickclient.exe --config "$(python3 -c "print('A'*5000)")"
# Use Boofuzz for structured protocol fuzzing
# See: https://github.com/jtpereyda/boofuzz
Even if the local thick client logic is secure, the backend API it communicates with may be vulnerable to the full range of web and API attacks. This phase treats the thick client as an API client and applies standard web application testing methodology to all intercepted traffic.
# testssl.sh: comprehensive TLS check
./testssl.sh https://backend.thickclientapp.com
# Wireshark: filter for plaintext HTTP basic auth
http.authbasic
testssl.sh, sslyze.# IDOR/BOLA: change the resource ID in the intercepted request
GET /api/v1/users/123/report HTTP/1.1
Host: api.thickclientapp.com
Authorization: Bearer <token_for_user_123>
# XXE via intercepted XML request
POST /api/v1/import HTTP/1.1
Content-Type: application/xml
<?xml version="1.0"?>
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>&xxe;</data>
curl, sqlmap, SecLists.A great pentest is only as valuable as its report. For thick client engagements, make sure findings clearly explain the local execution context โ reviewers unfamiliar with thick client testing may not immediately grasp why a disabled UI button or a world-writable DLL directory is a critical finding.
Thick client application penetration testing is one of the most comprehensive and technically deep forms of application security assessment. The local execution model exposes an attack surface that web app testing simply cannot reach โ from binary protections and DLL hijacking to memory forensics and IPC abuse. Use this checklist as a living document, adapt it to the specific architecture and technology of each engagement, and stay current with the OWASP TASVS standard as it continues to evolve.