Strobesstrobes
Platform
Solutions
Resources
Customers
Company
Pricing
Book a Demo
Strobesstrobes

Strobes connects every exposure signal to autonomous action, so security teams fix what matters, prove what works, and stop chasing noise.

Book a DemoTalk to an expert
ISO 27001SOC 2CREST
  • Platform
  • Platform Overview
  • Agentic Exposure Management
  • AI Agents
  • Integrations
  • API & Developers
  • Workflows & Automation
  • Analytics & Reporting
  • Solutions
  • Exposure Assessment (EAP)
  • Attack Surface Management
  • Application Security Posture
  • Risk-Based Vulnerability Management
  • Adversarial Exposure Validation (AEV)
  • AI Pentesting
  • Pentesting as a Service
  • CTEM Framework
  • By Industry
  • Financial Institutions
  • Technology
  • Retail
  • Healthcare
  • Manufacturing
  • By Roles
  • CISOs
  • Security Directors
  • Cloud Security Leaders
  • App Sec Leaders
  • Resources
  • Quick Agentic Pentest
  • Blog
  • Customer Stories
  • eBooks
  • Datasheets
  • Videos & Demos
  • Exposure Management Academy
  • CTEM Maturity Assessment
  • Pentest Health Check
  • Security Tool ROI Calculator
  • Company
  • About Strobes
  • Meet the Team
  • Trust & Security
  • Contact Us
  • Careers
  • Become a Partner
  • Technology Partner
  • Partner Deal Registration
  • Press Release

Weekly insight for security leaders

CTEM research, agentic AI trends, and what's actually moving the needle.

© 2026 Strobes Security Inc. All rights reserved.

Privacy PolicyTerms of ServiceCookie PolicyAccessibilitySitemap
Back to Blog
Thick Client Penetration Testing Guide
Offensive SecurityApplication Security

Thick Client Penetration Testing Guide

Akhil ReniNovember 21, 20259 min read

Table of Contents

  • Why is a thick client's own logic never a security boundary?
  • Two-tier clients hand you the database; three-tier clients hand you the API
  • How do you triage local storage and binary secrets fast?
  • How do you beat client-side auth with Frida?
  • Named pipes and DLL search order are the local privilege-escalation pair
  • What does a thick client findings report actually contain?
  • Frequently asked questions
  • Sources and references

Authors

A
Akhil Reni

Share

Table of Contents

  • Why is a thick client's own logic never a security boundary?
  • Two-tier clients hand you the database; three-tier clients hand you the API
  • How do you triage local storage and binary secrets fast?
  • How do you beat client-side auth with Frida?
  • Named pipes and DLL search order are the local privilege-escalation pair
  • What does a thick client findings report actually contain?
  • Frequently asked questions
  • Sources and references

Authors

A
Akhil Reni

Share

TL;DR
  • ✓Any security control a thick client enforces in its own binary is a suggestion, not a boundary, because the binary runs on the attacker's machine.
  • ✓The fastest wins are usually unglamorous: a hardcoded connection string in a decompiled .NET assembly or a base64 blob mislabeled as encryption.
  • ✓Two-tier clients hand you database credentials directly; three-tier clients push the fight to the API, where authorization bugs live.
  • ✓Runtime instrumentation with Frida beats fighting obfuscation statically, letting you flip an auth check to true without ever reading the obfuscated source.
  • ✓Named pipes created with a null DACL are a recurring local privilege-escalation path that scanners almost never catch.

A thick client is the rare app where the attacker owns the runtime. The binary, its config, its session keys, and often a database connection string all sit on a machine the tester fully controls, which means every client-side check can be read, patched, or hooked at will. That single fact is why thick client penetration testing finds issues a web scanner never could, and why developers who treat the desktop as trusted ship credentials in plaintext for years.

This guide is built around what actually surfaces on engagements: decompiling a .NET assembly to read a connection string, hooking an auth routine with Frida when obfuscation blocks static analysis, and abusing a null-DACL named pipe for local privilege escalation. Each technique comes with the real tool output you would screenshot, the line that matters, and the config change that closes it.

Table of contents
  1. Why is a thick client's own logic never a security boundary?
  2. Two-tier clients hand you the database; three-tier clients hand you the API
  3. How do you triage local storage and binary secrets fast?
  4. How do you beat client-side auth with Frida?
  5. Named pipes and DLL search order are the local privilege-escalation pair
  6. What does a thick client findings report actually contain?

Why is a thick client's own logic never a security boundary?

Because the binary executes on a machine the attacker controls, anything it decides locally can be observed and overridden. A license check, a role gate, a "this user is an admin" boolean, a client-side input validator: all of it is advisory once you can attach a debugger or a hooking framework. The only durable boundary is the server, which is why three-tier designs that enforce authorization server-side hold up and two-tier designs that trust the client do not.

This reframes the whole engagement. You are not just looking for bugs, you are inventorying every decision the client makes that the server should be making instead. The same principle drives modern offensive work generally, including the continuous approach in our comparison of DAST, pentesting, and agentic pentesting. Three surfaces carry the findings:

  • Local: config files, the registry, named pipes, memory, and DLL search order.
  • Network: the protocol to the backend, frequently a custom TCP framing rather than plain HTTP.
  • Binary: client-side auth, license gating, embedded secrets, and weak crypto you can read straight out of the assembly.

Two-tier clients hand you the database; three-tier clients hand you the API

The architecture decides where the money is. A two-tier (client-server) app opens a direct connection to the database, so the client must carry the connection string. Decompile it or dump it from memory and you get database credentials and can query the backend directly, bypassing every application-layer control. A three-tier app puts an app server in the middle, keeps the DB credentials server-side, and forces you to attack the API boundary, where broken object-level authorization and parameter tampering live, much like the issues you would hunt in API penetration testing.

Decompiling a managed binary is where two-tier clients fall over. dnSpy and ILSpy turn a .NET assembly back into readable C#, and the first thing worth grepping for is a connection string:

// dnSpy > right-click assembly > "Edit Class (C#)" / search for ConnectionString
// DataAccess.cs (decompiled from VendorApp.dll)
public sealed class Db {
    private const string Conn =
        "Server=sql01.corp.internal;Database=Billing;"
      + "User Id=svc_billing;Password=W1nter2023!;";   // <- hardcoded, plaintext
    public SqlConnection Open() => new SqlConnection(Conn);
}

That one constant is game over: you connect to sql01 with svc_billing from your own machine, outside the app entirely. The mistake teams make is assuming a compiled binary hides this. It does not. Decompilation recovers near-original source, and IL is trivial to read.

Sample findings excerpt
FindingSeverity (CVSS)EvidenceRemediation
Hardcoded DB connection string9.1 CriticaldnSpy decompile of DataAccess.Db.ConnMove DB access behind an API; rotate svc_billing
Null-DACL named pipe to SYSTEM service8.8 Highaccesschk shows RW Everyone on \pipe\VendorAppSvcSet explicit DACL; authenticate pipe clients
Client-side auth check bypassable7.4 HighFrida flips CheckCredentials 0x0 to 0x1Enforce authentication server-side only
DLL loaded from writable install dir7.8 HighProcMon NAME NOT FOUND; icacls (M) for UsersAbsolute-path loads; lock down install ACLs
API key stored as base64 (not encrypted)5.9 Mediumstrings output decodes to plaintext keyDPAPI with per-user entropy; server-issued tokens

How do you triage local storage and binary secrets fast?

Start with a strings sweep before you decompile anything, because it is cheap and frequently decisive. Run Process Monitor to watch what the app reads and writes at launch and login, then pull printable strings from the binary and config to surface endpoints, passwords, and keys:

$ strings -n 8 VendorApp.exe | grep -iE "password|secret|api[_-]?key|server=|BEGIN .*KEY"
Server=sql01.corp.internal;User Id=svc_billing;Password=W1nter2023!    <- connection string
X-Api-Key: 7c4a8d09ca3762af61e59520943dc26494f8941b                      <- backend API key
-----BEGIN RSA PRIVATE KEY-----                                        <- embedded signing key
https://api.vendorapp.internal/v2/                                     <- internal endpoint

The telltale lines are the connection string, the static API key, and the private key marker. What testers miss most is the secret that only looks encrypted: a "protected" value that is plain base64, or a DPAPI blob sealed with a static key you can unseal as the same user. Decode before you call it safe. Check %APPDATA%, %LOCALAPPDATA%, ProgramData, the HKCU and HKLM hives, and any embedded SQLite cache with cached tokens or PII. This is the desktop cousin of the storage work in our mobile app penetration testing checklist.

How do you beat client-side auth with Frida?

When the assembly is obfuscated, stop reading and start hooking. Frida attaches to the live process and lets you replace a function's return value, so you can force a client-side authentication or license check to pass without understanding a single line of the mangled source. The pattern: find the method (in dnSpy if readable, or by export name for native code), attach, and rewrite the return on the way out.

$ frida -p 8472 -l auth_bypass.js
// auth_bypass.js  -- force CheckCredentials() to return true
Interceptor.attach(Module.getExportByName(null, 'CheckCredentials'), {
  onLeave: function (retval) {
    console.log('[*] CheckCredentials returned ' + retval);   // [*] CheckCredentials returned 0x0
    retval.replace(0x1);                                       // <- now it returns true
    console.log('[+] forced to ' + retval);                   // [+] forced to 0x1
  }
});
// console shows 0x0 (deny) flipped to 0x1 (allow); the login dialog accepts any password

The output line that matters is the flip from 0x0 to 0x1: the app now treats authentication as successful regardless of the password. For HTTPS that ignores the system proxy, the same instrument-at-runtime mindset applies, hook the send/recv or TLS layer and read plaintext before it is wrapped, or redirect the backend hostname with a hosts entry and run an invisible Burp listener. This runtime-first approach is exactly what scales in the agentic model we cover in the agentic pentesting comparison.

War story
On a manufacturing ERP engagement, the client's auto-update service exposed a named pipe with a null DACL that accepted an unauthenticated 'run installer' command. We pointed it at our own binary and went from standard domain user to SYSTEM on every workstation with the client installed.

Named pipes and DLL search order are the local privilege-escalation pair

Two local issues recur on nearly every Windows thick client, and scanners miss both. The first is DLL hijacking: if the app loads a DLL by name without a full path and installs into a writable directory, you plant a malicious DLL earlier in the search order and run code with the app's privileges. The second, more often overlooked, is a named pipe created with a null DACL, which lets any local user connect to a privileged service and send commands. Check the pipe's permissions with Sysinternals accesschk:

$ accesschk.exe -accepteulas \pipe\VendorAppSvc
\\.\Pipe\VendorAppSvc
  RW Everyone                            <- any local user can read AND write
  RW NT AUTHORITY\SYSTEM
The service running this pipe executes as SYSTEM; commands sent to it inherit that token.

The damning line is RW Everyone on a pipe backed by a SYSTEM service: a standard user sends a crafted message and the service acts on it as SYSTEM. Confirm install-directory ACLs with icacls for the DLL angle, and look for an unquoted service path while you are there. On a recent assessment of a manufacturing ERP client, we found the update service exposed exactly this pipe, accepted an unauthenticated "run installer" command, and pointed it at our own binary, turning a standard domain user into SYSTEM on every workstation that had the client installed.

What does a thick client findings report actually contain?

It contains reproducible findings tied to evidence and a concrete fix, not a tool dump. Each issue names the artifact (the decompiled method, the registry key, the pipe), the privilege or data it exposes, a CVSS rating, and the exact setting that closes it. The fixes are config-level and specific: move the connection string to the server and have the client call an API instead, replace home-grown obfuscation with phishing-resistant server-side authorization, set an explicit DACL on every named pipe, load DLLs by absolute path with SetDllDirectory hardening, and seal local secrets with DPAPI under a per-user entropy value rather than a static key.

Map findings to references so clients can prioritize: CWE-312 for cleartext storage, CWE-427 for DLL hijacking, CWE-798 for hardcoded credentials, and OWASP ASVS V2/V6 for authentication and storage controls. For organizations that want this validated continuously instead of once a year, fold it into the broader program described in our look at automated versus manual penetration testing.

Thick client test checklist
Binary
  • ✓Decompile managed code (dnSpy, ILSpy)
  • ✓Grep strings for secrets and endpoints
  • ✓Hook auth and license checks with Frida
  • ✓Review crypto and anti-tamper claims
Local host
  • ✓Config, registry, and SQLite for cleartext secrets
  • ✓Named-pipe DACLs with accesschk
  • ✓DLL search order and install ACLs (icacls)
  • ✓Unquoted service paths and weak service perms
Network
  • ✓Classify protocol (HTTP vs custom TCP)
  • ✓Intercept proxy-aware traffic with Burp
  • ✓Hook socket/TLS layer for proxy-unaware apps
  • ✓Tamper and replay at the API boundary

Frequently asked questions

What is a thick client application?
A thick client (or fat client) is a desktop application that runs significant business logic locally rather than in a browser. Examples include trading terminals, ERP front-ends, and admin consoles built in .NET, Java, or native C/C++. Because code and data live on the endpoint, the attacker controls the runtime, which exposes a much larger local attack surface than a web app.
Which tools are used for thick client penetration testing?
dnSpy and ILSpy decompile .NET assemblies, JD-GUI and CFR handle Java, and IDA Pro or Ghidra cover native binaries. Frida hooks live processes to bypass client-side checks, Burp Suite and Echo Mirage intercept traffic, and Sysinternals tools like Process Monitor, accesschk, and icacls inspect the host. strings and ProcMon are usually the first two you run.
How do you bypass a client-side license or authentication check?
Two ways. Statically, decompile the assembly in dnSpy, patch the check to always return true, and recompile the method in place. Dynamically, attach Frida to the running process and replace the function's return value at runtime, which works even when obfuscation makes the source unreadable. Both prove the same point: client-side checks are not a security boundary.
Why are named pipes a privilege-escalation risk in thick clients?
Thick clients often run a helper service that exposes a named pipe for inter-process communication, and developers frequently create that pipe with a null or overly permissive DACL. If any local user can write to a pipe backed by a SYSTEM service, they can send commands that execute with SYSTEM privileges. Check pipe permissions with Sysinternals accesschk.
How is thick client testing different from web application testing?
Web testing focuses on the server-side request and response. Thick client testing adds the local host (files, registry, memory, named pipes, DLLs) and the binary itself (decompilation, hardcoded secrets, anti-tamper), plus protocols that are often custom TCP rather than HTTP. You need reverse-engineering and Windows host-analysis skills on top of web skills.
How long does a thick client penetration test take?
A focused engagement on one client typically runs five to ten business days, depending on whether it is two-tier or three-tier, how much custom protocol work is involved, and whether the binary is obfuscated. Obfuscation and custom TCP framing add time because you move from reading decompiled source to runtime instrumentation with Frida and protocol reverse engineering.

Sources and references

  • OWASP ASVS
  • CWE-798: Use of Hard-coded Credentials
  • CWE-427: Uncontrolled Search Path Element
  • Frida Dynamic Instrumentation Toolkit
A
Akhil Reni
Co-founder and CTO, Strobes
Akhil Reni is co-founder and CTO of Strobes, building AI-driven penetration testing and exposure management for security teams.
Tags
Thick Client SecurityPenetration TestingOffensive Security

Stop chasing vulnerabilities Start reducing exposure

See how Strobes AI agents validate and fix your most critical exposures automatically.

Book a Demo
Continue Reading

Related Posts

Vulnerability validation: why most of your scanner backlog is noise - Strobes
Exposure ValidationApplication Security

Vulnerability Validation: Why Most of Your Scanner Backlog Is Noise

Vulnerability validation proves which scanner findings are real, reachable, and exploitable. Why manual triage fails and how agentic validation scales.

Jun 9, 202619 min
How to pentest single-page applications - React, Angular and Vue SPA security testing guide
Penetration TestingApplication Security

How to Pentest Single-Page Applications (React, Angular, Vue)

Learn how to pentest React, Angular, and Vue SPAs. Covers DOM XSS, client-side routing bypass, JS bundle secrets, and why traditional DAST scanners fail.

Jun 4, 202623 min
Bug bounty vs pentesting vs AI pentesting comparison featured image
Penetration TestingApplication Security

Bug Bounty vs. Pentesting vs. AI Pentesting: Which Model Fits Your AppSec Program?

Bug bounty vs pentesting vs AI pentesting: compare costs, coverage, compliance, and when to use each model. Build a layered AppSec testing strategy.

Jun 4, 202621 min