Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

Attacking Thick Client Applications

Thick client applications (also called rich or fat clients) are locally installed applications that perform significant processing on the client side rather than relying solely on remote servers. Unlike web applications, they don’t require internet access and can contain significant attack surface including hardcoded credentials, unencrypted communications, and insecure local storage.


Thick Client Architecture

Two-Tier Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Thick Client   β”‚
β”‚  (Application)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚ Direct connection
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Database     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Risk: Attacker can directly communicate with database; credentials embedded in client.

Three-Tier Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Thick Client   β”‚
β”‚  (Application)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚ HTTP/HTTPS
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Application   β”‚
β”‚     Server      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚
         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Database     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Risk: Better security; database not directly accessible, but application server may be exploitable.


Common Vulnerabilities in Thick Clients

VulnerabilityImpactExample
Hardcoded credentialsComplete compromiseDatabase password in source code
Improper error handlingInformation disclosureStack traces revealing system paths
DLL hijackingCode executionMalicious DLL loaded instead of legitimate
Buffer overflowCode execution, DoSStack or heap overflow
SQL injectionData breach, code executionDirect SQL queries to database
Insecure storageCredential theftPasswords stored in plaintext files
Weak session managementSession hijackingPredictable session tokens
Unencrypted communicationMITM attacks, credential theftHTTP or unencrypted TCP

Penetration Testing Phases

Phase 1: Information Gathering

Identify application architecture:

  • Is it two-tier or three-tier?
  • What’s the backend server (if any)?
  • What database is used?

Identify technologies:

# Use CFF Explorer to analyze PE headers
# Detect It Easy for quick analysis
# Strings utility to find embedded information
strings application.exe | grep -i "version\|database\|server"

Identify entry points:

  • User input fields
  • File operations
  • Network communications
  • Registry access

Tools:

  • CFF Explorer β€” PE file analysis
  • Detect It Easy β€” Quick binary analysis
  • Process Monitor β€” Monitor runtime behavior
  • Strings β€” Extract readable text from binaries

Phase 2: Client-Side Attacks

Static Analysis

Reverse-engineer the application to find hardcoded credentials:

# For .NET applications
dnspy application.exe    # Decompile and inspect source code

# For Java applications
jd-gui application.jar   # Decompile JAR files

# For native binaries
ida application.exe      # IDA Pro for advanced analysis
ghidra application.exe   # Ghidra (free alternative)

Look for:

  • Hardcoded database credentials
  • API keys and tokens
  • Server URLs and hostnames
  • Encryption keys
  • Username/password patterns

Dynamic Analysis

Monitor application execution to capture sensitive data:

# Monitor registry access
procmon.exe              # Process Monitor (SysInternals)

# Debugging and inspection
x64dbg                   # 64-bit debugger
ollydbg                  # 32-bit debugger
radare2                  # Portable disassembler/debugger

# Memory analysis
frida                    # Dynamic instrumentation toolkit
volatility               # Memory forensics

Technique: Extract Credentials from Memory

  1. Attach debugger to running application
  2. Set breakpoints at authentication functions
  3. Inspect memory for plaintext passwords/credentials
  4. Extract and decode Base64/encrypted values

Phase 3: Network-Side Attacks

Analyze network traffic between client and server:

# Capture traffic
wireshark                # GUI packet analyzer
tcpdump                  # Command-line packet capture
netsh trace              # Windows native tracing

# Analyze traffic
burp suite               # Intercept and analyze HTTP/HTTPS
tcpview                  # View active TCP/UDP connections
fiddler                  # HTTP(S) traffic interception

Vulnerability examples:

  • Credentials transmitted in cleartext
  • No certificate validation (MITM possible)
  • Unencrypted database connections
  • Predictable request patterns

Phase 4: Server-Side Attacks

Target the application server or database:

# Common server-side attacks
sqlmap                   # SQL injection testing
burp suite               # Web application testing framework
metasploit               # Exploitation framework

Attack vectors:

  • SQL injection
  • Authentication bypass
  • Privilege escalation
  • Business logic flaws
  • API vulnerabilities

Extracting Hardcoded Credentials - Case Study

Scenario: Restart-OracleService.exe

Initial discovery: Application found in SMB NETLOGON share.

Step 1: Identify Hidden Behavior

# Run the application
C:\Apps>.\Restart-OracleService.exe
C:\Apps>

# Use Process Monitor to capture file operations
# Monitor C:\Users\[user]\AppData\Local\Temp
# Look for temporary files created and deleted

Finding: Application creates temporary batch files and deletes them immediately.

Step 2: Prevent File Deletion

Modify TEMP folder permissions to prevent deletion:

C:\Users\[user]\AppData\Local\Temp
β†’ Right-click β†’ Properties β†’ Security β†’ Advanced
β†’ Edit [user] permissions
β†’ Uncheck "Delete subfolders and files" and "Delete"
β†’ Apply

Step 3: Capture Generated Files

Run application again and check TEMP folder:

C:\>dir C:\Users\[user]\AppData\Local\Temp\2
04/03/2023  02:09 PM         1,730,212 6F39.bat
04/03/2023  02:09 PM                 0 6F39.tmp

Step 4: Analyze Batch File

@shift /0
@echo off

if %username% == matt goto correcto
if %username% == frankytech goto correcto
if %username% == ev4si0n goto correcto
goto error

:correcto
# Outputs Base64-encoded binary to file
echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAA... > c:\programdata\oracle.txt
# More Base64 lines appended...

# PowerShell script to decode Base64
echo $salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1

powershell.exe -exec bypass -file c:\programdata\monta.ps1
del c:\programdata\monta.ps1
del c:\programdata\oracle.txt
c:\programdata\restart-service.exe
del c:\programdata\restart-service.exe

Key insight: Application drops and executes a binary, then cleans up traces.

Step 5: Preserve Dropped Files

Modify batch file to prevent deletion:

@shift /0
@echo off

echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAA... > c:\programdata\oracle.txt
echo AAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g >> c:\programdata\oracle.txt
# ... more Base64 lines ...

echo $salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1

# Comment out deletion commands
REM del c:\programdata\monta.ps1
REM del c:\programdata\oracle.txt
c:\programdata\restart-service.exe
REM del c:\programdata\restart-service.exe

Step 6: Execute Modified Batch

Files now remain in C:\ProgramData\:

C:\>ls C:\ProgramData\

Mode                LastWriteTime         Length Name
-a----        3/24/2023   1:01 PM            273 monta.ps1
-a----        3/24/2023   1:01 PM         601066 oracle.txt
-a----        3/24/2023   1:17 PM         432273 restart-service.exe

Step 7: Debug the Extracted Binary

Use x64dbg to analyze the executable:

1. Open x64dbg
2. File β†’ Open β†’ select restart-service.exe
3. Options β†’ Preferences β†’ Uncheck all except "Exit Breakpoint"
4. Right-click in CPU view β†’ Follow in Memory Map
5. Look for suspicious memory regions (RW-- protection)
6. Double-click interesting regions (look for MZ magic bytes)
7. Right-click β†’ Dump Memory to File

Step 8: Extract and Analyze Memory Dump

# Run strings on exported dump
strings restart-service_00000000001E0000.bin | grep -i "oracle\|password\|user"

# Output shows .NET Framework references
.NETFramework,Version=v4.0,Profile=Client
.NET Framework 4 Client Profile

Step 9: Deobfuscate .NET Binary

Use de4dot to remove obfuscation:

de4dot restart-service_00000000001E0000.bin
# Outputs: restart-service_00000000001E0000-cleaned.bin

Step 10: Decompile with dnSpy

1. Open dnSpy
2. File β†’ Open β†’ select restart-service_00000000001E0000-cleaned.bin
3. Browse decompiled C# source code
4. Look for hardcoded credentials and business logic

Result: Discover custom runas.exe implementation with hardcoded Oracle service credentials.


Attacking Applications Connecting to Services

Applications that connect to backend services (databases, APIs, message queues) often embed connection strings containing credentials. If these strings aren’t sufficiently protected, they can be extracted through binary examination β€” either from a native ELF executable using a debugger, or from a .NET assembly using a decompiler. Recovered credentials can be reused directly against the backend service, or tried against other services on the network (password reuse/spraying).

ELF Executable Examination (GDB/PEDA)

Scenario: octopus_checker binary found on a remote Linux host. Running it locally shows it attempts a connection to a database instance:

rnemeth@htb[/htb]$ ./octopus_checker

Program had started..
Attempting Connection
Connecting ...

The driver reported the following diagnostics whilst running SQLDriverConnect

01000:1:0:[unixODBC][Driver Manager]Can't open lib 'ODBC Driver 17 for SQL Server' : file not found
connected

The binary likely builds a SQL connection string containing credentials at runtime. PEDA (Python Exploit Development Assistance for GDB) extends GDB with better register/memory visibility for stepping through the binary.

Step 1 β€” Load the binary and set the disassembly flavor:

rnemeth@htb[/htb]$ gdb ./octopus_checker
gdb-peda$ set disassembly-flavor intel
gdb-peda$ disas main

Disassembling main reveals a series of call instructions referencing string addresses β€” these are fragments of the SQL connection string, but out of order and reversed due to endianness (the byte order in which the architecture reads multi-byte values).

Step 2 β€” Find the call to SQLDriverConnect:

0x00005555555555ff <+425>:   mov    esi,0x0
0x0000555555555604 <+430>:   mov    rdi,rax
0x0000555555555607 <+433>:   call   0x5555555551b0 <SQLDriverConnect@plt>
0x000055555555560c <+438>:   add    rsp,0x10
0x0000555555555610 <+442>:   mov    WORD PTR [rbp-0x4b4],ax

Step 3 β€” Breakpoint on the call and run:

gdb-peda$ b *0x5555555551b0
Breakpoint 1 at 0x5555555551b0

gdb-peda$ run

When the breakpoint hits, the fully-assembled connection string is visible in a register (here, RDX) since it’s passed as an argument to SQLDriverConnect:

RDX: 0x7fffffffda70 ("DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost, 1401;UID=username;PWD=password;")

Key insight: Rather than manually reconstructing a reversed/fragmented string from disassembly, breakpoint on the function that consumes the fully-built string (the driver connect call) and read it straight out of the register/stack at that point.

DLL File Examination (dnSpy)

Scenario: MultimasterAPI.dll found on a remote Windows host. Metadata confirms it’s a .NET assembly:

C:\> Get-FileMetaData .\MultimasterAPI.dll

<SNIP>
M .NETFramework,Version=v4.6.1 TFrameworkDisplayName.NET Framework 4.6.1    api/getColleagues
http://localhost:8081*POST
<SNIP>

Since it’s a managed .NET assembly, it can be decompiled directly (no need for a debugger/memory dump as with native binaries).

Step 1 β€” Open the DLL in dnSpy and browse the namespace/class tree.

Step 2 β€” Inspect controller classes for hardcoded backend logic. MultimasterAPI.Controllers -> ColleagueController reveals a SQL connection string with a plaintext password embedded directly in the Get/GetColleagues methods.

Key insight: .NET assemblies decompile close to original source (dnSpy reconstructs readable C#/VB), so connection strings and other secrets are often trivially visible without any dynamic analysis β€” check controllers/data-access classes first.

Takeaways

  • Native binaries (ELF/PE) that build connection strings at runtime can still leak the fully-assembled string via a breakpoint at the driver/connect API call β€” no need to manually reverse fragmented, endian-reversed string constants.
  • Managed binaries (.NET, Java) are far easier: decompile with dnSpy/jd-gui and read the connection string directly from source.
  • Extracted database credentials should be tested for direct reuse against the backend service, and also tried against other services/accounts on the network (password reuse).

Tools Summary

Static Analysis

ToolPurposeSupported
IDA ProDisassembly and decompilationEXE, DLL, binaries
GhidraFree disassembly/decompilationEXE, DLL, binaries
dnSpy.NET decompilationEXE, DLL (managed)
JADXJava decompilationAPK, JAR, DEX
Radare2Portable disassemblerEXE, DLL, binaries
StringsExtract readable textAny binary
CFF ExplorerPE file analysisEXE, DLL
GDB + PEDADisassembly, breakpoints, register/memory inspectionELF binaries

Dynamic Analysis

ToolPurpose
Process MonitorMonitor system calls
x64dbg64-bit debugging
ollydbg32-bit debugging
WinDbgWindows debugging
FridaDynamic instrumentation
VolatilityMemory forensics

Network Analysis

ToolPurpose
WiresharkPacket capture and analysis
Burp SuiteHTTP(S) interception
FiddlerHTTP proxy
tcpdumpCLI packet capture
Telerik FiddlerHTTP debugging

Hardcoded Credentials Common Locations

In source code/binaries:

  • Database connection strings
  • API keys and tokens
  • Service account credentials
  • Encryption keys
  • OAuth tokens

In configuration files:

  • config.ini, config.xml, app.config
  • web.config (ASP.NET)
  • .properties files (Java)
  • Registry keys (Windows)
  • INI files in program directory

In memory:

  • Decrypted passwords during execution
  • Session tokens
  • Encryption keys
  • API responses with sensitive data

In temporary files:

  • Batch files created during execution
  • Log files with debug output
  • Cache files with plaintext data
  • Installation files

Assessment Checklist

  • Identify application architecture (two-tier/three-tier)
  • Perform static analysis (decompile source code)
  • Search for hardcoded credentials in binaries
  • Check configuration files for sensitive data
  • Monitor runtime behavior with Process Monitor
  • Capture and analyze network traffic
  • Intercept HTTP(S) communications
  • Test for SQL injection in local database calls
  • Analyze memory dumps for credentials/keys
  • Test for DLL hijacking vulnerabilities
  • Check for improper error handling
  • Test authentication mechanisms
  • Document all findings with proof-of-concept

Key Takeaways

  • Thick clients are complex: Multiple attack surfaces (client, network, server)
  • Hardcoded credentials common: Source code and binaries frequently contain secrets
  • Local access dangerous: Full control of the application logic
  • Network traffic unencrypted: Monitor for plaintext credentials and data
  • Debugging access: Ability to inspect runtime memory and logic
  • File extraction: Temporary files may reveal dropped/executed code
  • Decompilation possible: .NET and Java applications easily reversible
  • Default behaviors exploitable: Many applications designed without security in mind