LazyHackers.in — Checklist
🖥️ Thick Client Pentest Checklist
Desktop apps (.NET / Java / native / Electron): scenario · tool · steps · the finding · the fix
☰ How to use this guide
A thick client runs logic on the user's machine, so the threat model is different from web: the attacker owns the binary, the memory and the local files. Two architectures matter — 2-tier (client talks straight to the database) and 3-tier (client → app server → DB). 2-tier is the dangerous one: decompile the client, recover the DB connection string, and you may have direct database access. This guide turns every item into how-to-test for .NET, Java, native and Electron apps; the backend it talks to is still an API/DB, so pair with the API checklist.
# Identify the tech stack first (drives the toolset)
file App.exe ; strings -n 8 App.exe | grep -iE 'mscorlib|java|electron|Qt|connectionstring'
# .NET -> dnSpy / ILSpy / dotPeek (+ de4dot if obfuscated)
# Java -> JD-GUI / JADX / procyon ; native -> Ghidra / IDA / x64dbg
# Electron -> npx asar extract app.asar out/ (then it is just JS)0 Architecture & recon
Determine the architecture and tech stack, and map where the client talks to (DB directly? app server? cloud?).
# Watch what the app opens and connects to at launch
procmon.exe # filter: Process Name is App.exe (file/registry/network)
# Active connections + listening ports
netstat -anob | findstr App.exe
TCPView.exeArchitecture & recon — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Tech stack identified (.NET/Java/native/Electron) | file + strings | Stack identified |
| Architecture (2-tier vs 3-tier) | procmon/netstat | Architecture mapped |
| Endpoints/DB/services it connects to | TCPView/Wireshark | Connectivity mapped |
| Files/registry touched at runtime | ProcMon | Local footprint mapped |
| Installer/update mechanism reviewed | inspect installer/updater | Update mechanism reviewed |
1 Static binary analysis
Decompile and read. Hardcoded secrets, connection strings, API keys and crypto material fall straight out — especially in managed (.NET/Java) and Electron apps.
# .NET: decompile + search the whole assembly
dnSpy App.exe # (de4dot App.exe first if obfuscated)
# Java: JADX over the jar
jadx -d out App.jar
# Electron: extract and grep the JS
npx asar extract resources/app.asar app_src && grep -rEi 'password|apikey|secret|http' app_src
# Quick wins from raw strings
strings -n 8 App.exe | grep -iE 'connectionstring|server=|pwd=|password=|api[_-]?key|BEGIN .*KEY'Static binary analysis — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Hardcoded connection string | dnSpy/strings | Hardcoded connection string |
| Hardcoded secrets/API keys/creds | decompile + grep | Hardcoded secret |
| Hardcoded crypto keys/IV/salt | decompile | Hardcoded crypto material |
| Sensitive logic in client (should be server) | review decompiled | Sensitive logic on client |
| No/weak obfuscation (managed code) | decompile clarity | No code obfuscation |
| Debug strings / internal endpoints | strings | Debug info in binary |
| Binary not signed / signature not checked | signtool verify | Unsigned/unverified binary |
| Vulnerable bundled library (CVE) | inventory libs | Vulnerable dependency |
2 Local data storage
Thick clients scatter data across the registry, config files, app-data and DPAPI. Exercise the app, then inspect what it wrote.
# Diff the registry + filesystem around an action (e.g. login)
regshot # 1st shot -> do the action -> 2nd shot -> compare
# Where data lands
%APPDATA% %LOCALAPPDATA% ProgramData ; *.config / *.ini / *.xml / *.sqlite
# DPAPI-protected blobs (decrypt in the user's context)
# (mimikatz dpapi::* or SharpDPAPI in a lab) — confirms reversible local "protection"Local storage — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Sensitive data in config files (plaintext) | inspect *.config/.ini | Plaintext config storage |
| Sensitive data in registry | regshot/regedit | Sensitive data in registry |
| Sensitive data in local DB/files | inspect appdata | Insecure local storage |
| Credentials cached, not cleared on logout | logout + re-inspect | Credentials persist |
| Weak/reversible local protection (DPAPI/XOR) | analyse protection | Reversible local protection |
| Sensitive data in logs/temp | inspect logs/temp | Sensitive data in logs |
| Insecure file permissions on app data | icacls | Weak file permissions |
3 Traffic interception
Half of thick clients aren't proxy-aware and many use raw TCP/TLS rather than HTTP. The test is can you see and modify the traffic — via a proxy, a forced proxy, or a TCP interceptor.
- HTTP + proxy-aware: point the app at Burp (system proxy / config) and read the traffic.
- HTTP but not proxy-aware: force it through Burp with Proxifier or an invisible/transparent proxy + hosts redirect.
- Non-HTTP (raw TCP/TLS): use a TCP interceptor (Echo Mirage, mitm_relay) or Wireshark to capture; modify in the relay.
- Check TLS: no validation / accepts self-signed / no pinning means trivial MITM.
# Force a non-proxy-aware app through Burp
# Proxifier rule: App.exe -> 127.0.0.1:8080 (Burp invisible proxy + hostname mapping)
# Raw TCP/TLS interception
mitm_relay.py -l 0.0.0.0 -p 8080 -r tcp:9999:realserver:9999 # relay + Burp
# or Echo Mirage to hook send/recv; Wireshark to confirm cleartextTraffic interception — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Cleartext traffic | Wireshark | Cleartext transmission |
| Not proxy-aware (forced proxy needed) | Proxifier + Burp | Traffic interceptable |
| Raw TCP/TLS protocol | Echo Mirage/mitm_relay | Non-HTTP traffic interception |
| No TLS validation / accepts self-signed | present bad cert | Broken TLS validation |
| No certificate pinning | MITM succeeds | No certificate pinning |
| Weak TLS version/cipher | inspect handshake | Weak TLS |
| Sensitive data in requests/URLs | inspect traffic | Sensitive data in transit |
4 Client-side auth/authz & GUI manipulation
Thick clients love to enforce auth/roles in the UI. Anything the client decides can be flipped — enable disabled controls, edit the response, or patch the check.
# Re-enable disabled buttons / unhide admin controls
# Window finder tools (e.g. ManageEngine/UISpy/Window Detective) -> toggle Enabled/Visible
# Patch a client-side check in .NET (dnSpy: edit IL/C#, e.g. isAdmin() -> return true)
# dnSpy -> Edit Method -> save module
# Or tamper the server response in Burp: {"role":"user"} -> {"role":"admin"}Client-side auth/authz & GUI — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| AuthZ enforced client-side only | patch check / tamper response | Client-side authorisation |
| Disabled/hidden controls re-enableable | Window finder | Client-side control bypass |
| Client-side check patchable | dnSpy edit | Patchable security check |
| Response tamper grants access | flip in Burp | Response-tampering bypass |
| No server-side re-validation | call backend directly | Missing server-side validation |
| Hardcoded/backdoor credentials | decompile | Backdoor credential |
5 Injection to the backend
In 2-tier apps the client builds SQL directly — SQLi hits the DB straight away. In 3-tier, the same injection classes apply to the app-server protocol. Intercept and inject.
# 2-tier: capture the client's DB traffic, then inject in the query path
# (e.g. search box -> ' OR 1=1-- ; or sqlmap against the captured request)
sqlmap -r captured-request.txt -p param --batch
# Direct DB access with the recovered connection string (2-tier):
# mssqlclient.py / sqlcmd with the creds pulled in S1 -> full DB if over-privilegedInjection to backend — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| SQL injection (direct DB / via server) | intercept + sqlmap | SQL injection |
| Direct DB access via recovered creds | connect with creds | Direct database access |
| Over-privileged DB account | review grants | Excessive DB privileges |
| Command injection | inject in params | OS command injection |
| Other injection (LDAP/XPath/etc.) | intercept + inject | Injection |
| No server-side input validation | send raw request | Missing input validation |
6 DLL hijacking & binary planting
If the app loads DLLs from a writable or search-order location, a planted DLL runs in its context — local privilege escalation or persistence.
# Find DLLs loaded from missing/writable paths
procmon.exe # filter: Result is NAME NOT FOUND, Path ends with .dll
# Check writable app/install dirs
icacls "C:\Program Files\App" # look for (M)/(F) for Users/Authenticated UsersDLL hijacking & binary planting — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| DLL loaded from missing/writable path | ProcMon NAME NOT FOUND | DLL hijacking |
| Writable install/app directory | icacls | Insecure directory permissions |
| Unsafe DLL search order | review loading | Unsafe library loading |
| Loaded modules unsigned/unverified | check signatures | Unverified module loading |
| Service binary path writable/unquoted | review service | Service path hijack |
7 Memory & runtime analysis
Sensitive data lingers in process memory; runtime hooking can change outcomes. Dump and search, then hook.
# Search process memory for secrets
# Process Hacker -> right-click process -> Memory -> search for "password"/token
# Hook a method at runtime to flip an outcome
frida -p <pid> -l hook.js # e.g. override IsLicensed()/IsAdmin() to return trueMemory & runtime — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Secrets readable in memory | Process Hacker search | Sensitive data in memory |
| Runtime hooking changes outcome | Frida hook | Client-side decision bypass |
| Local data tamper alters logic | edit in memory/file | Client data tampering |
| No anti-debug/anti-tamper (context) | attach debugger | No runtime protection |
8 IPC & privilege issues
Local IPC (named pipes, COM, sockets, WCF) and privileged helper services are an attack surface for local privilege escalation.
IPC & privileges — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Insecure named pipe / COM / socket IPC | enumerate + connect | Insecure IPC |
| Privileged helper service abusable | review service | Privileged-service abuse |
| IPC input not validated | inject via IPC | IPC injection |
| App runs with excessive privilege | check token | Excessive privilege |
| Auto-update lacks integrity check | MITM update | Insecure update mechanism |
E Electron-specific
Electron apps are Chromium + Node — extract the asar and you have the JS. The killer combos are nodeIntegration on with remote/untrusted content, and XSS becoming RCE through the Node bridge.
npx asar extract resources/app.asar app_src
grep -rEi 'nodeIntegration|contextIsolation|webSecurity|allowRunningInsecureContent|shell\.openExternal' app_src
# Open devtools if enabled; check for loadURL of remote/untrusted contentElectron — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| nodeIntegration enabled | grep config | nodeIntegration enabled |
| contextIsolation disabled | grep config | Context isolation off |
| webSecurity disabled | grep config | webSecurity disabled |
| Loads untrusted remote content | review loadURL | Untrusted content loaded |
| XSS → RCE via Node bridge | inject + test bridge | XSS to RCE |
| Unrestricted shell.openExternal | review usage | Command/URL execution |
| asar not integrity-checked | tamper asar | No asar integrity |
| DevTools enabled in production | open devtools | DevTools in production |
✓ Coverage map & how to run it
Run all universal sections; add the Electron block for Electron apps. The biggest wins are 2-tier connection-string recovery (§1), client-side auth bypass (§4), and non-proxy traffic interception (§3).
| Section | Run on | Focus |
|---|---|---|
| Universal 0–8 | Every thick client | Architecture, static, storage, traffic, auth/GUI, injection, DLL, memory, IPC |
| Electron | Electron apps | nodeIntegration, context isolation, XSS→RCE, asar |
Core principle: the client is fully attacker-controlled — binary, memory and local files. Recover what it hides, intercept what it sends (proxy-aware or not), and prove that every security decision is re-enforced on the server. The backend it talks to is still an API/DB — pair with the API checklist. Tick a box only when you've actually run the test.