SECURE_NODE // ATHENS_GR
← BACK TO RESEARCH Technique · Thick Client · SQL Injection

SQLi in a Thick Client Authentication Endpoint

10 JUN 2026| ≈ 3 MIN READ| SQLiThickClientDotNet
SANITIZED ▸ Methodology only. All hostnames, database names, usernames, and organizational details are lab placeholders. No client name, real credential, real SQL query, or production system identifier appears here. Assessment was authorized and scoped in writing.

Thick client assessments surface vulnerabilities that web-only scoping misses. The application here was a Windows .NET desktop tool for card payments management — installed on teller workstations, communicating with a SQL Server backend over a REST API. The authentication form submitted a JSON body to the API. The API constructed a SQL query from the submitted username without parameterisation. A single-quote in the username field produced a 500 response with the full .NET exception detail in the response body, including the library name, query fragment, and SQL Server error message.

FINAL SEVERITY
Critical
SQLi in auth, full DB access path
SCOPE
Grey-box
thick client + API review
STACK
.NET / SQL Server
System.Data.SqlClient

Single quote, full exception

Burp's Proxy intercepts HTTPS from the thick client after installing the CA certificate in the Windows cert store. The login request was found and a single-quote appended to the username value. The response was a 500 with verbose error detail from the SQL Server driver:

POST /api/auth/login HTTP/1.1
Host: payments.acme-lab.test
Content-Type: application/json

{"username":"lab_user'","password":"<lab-password>"}

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "error": "System.Data.SqlClient.SqlException",
  "message": "Incorrect syntax near '\\''.",
  "source": "AuthLib",
  "detail": "Unclosed quotation mark after the character string '<lab-password>'.",
  "stackTrace": "at System.Data.SqlClient.SqlConnection.OnError..."
}

# unparameterised query confirmed — username injected directly into SQL string
# exception reveals: library name, query context, and password value in error message

The error reveals the library name (AuthLib) and, in the detail string, the plaintext password submitted in the request — the SQL query was structured such that the password appears in the error output when the username injection breaks the syntax. This means any exception logged server-side also contains cleartext credentials.

The companion finding: cleartext credential storage in logs

The verbose error logging to the application server's log files means credentials submitted to the broken authentication endpoint are stored in plaintext in logs. The cleartext credential storage and the verbose error detail are companion findings from the same assessment.

The test timeline

DAY 1 — PROXY SETUP

Burp CA installed in Windows cert store. Thick client HTTPS traffic proxied. Authentication request identified — JSON body with username and password fields.

DAY 1 — SQLi CONFIRMED

Single quote produces 500 with SqlException. System.Data.SqlClient.SqlException: Incorrect syntax near "'". Full stack trace, library name, and query context returned.

DAY 1 — CREDENTIAL IN ERROR

Plaintext password appears in SQL error detail. Query construction places password value in error context — any logged exception contains the submitted credential in cleartext.

DAY 1 — INJECTION DEPTH

Boolean-based injection confirmed. lab_user' AND 1=1-- vs lab_user' AND 1=2-- produce different responses. Authentication bypass and schema enumeration paths established.

DAY 2 — REPORT

Full injection chain documented. Authentication bypass PoC, error-logged credential path, and verbose error exposure all captured with evidence.

Fixing the injection

  • Replace all string-concatenated SQL with parameterised queries or stored procedures. Every value from any external input — username, password, account ID — must be passed as a parameter, never interpolated into a query string. In .NET, this means SqlCommand with Parameters.Add(). Search for all string concatenation adjacent to SQL strings in the codebase.
  • Move credentials from plain configuration to a secure credential store. Database connection strings and API keys stored in app.config or as plaintext environment variables should be replaced with a secrets manager (Azure Key Vault, Vault by HashiCorp, or Windows DPAPI-protected configuration).
  • Disable verbose error responses in production. Set customErrors mode="On" in web.config and configure a generic error handler that returns a reference ID without stack trace, query context, or internal class names. Log the full exception server-side with the reference ID for debugging.
  • Include thick clients in assessment scope explicitly. The .NET application had never been assessed — only the web portal sharing the same backend had been tested. Thick clients communicate with the same API endpoints and often bypass controls designed for the web flow.

The takeaway

A single-quote in the username field produced a 500, a stack trace, the password value in plaintext, and a confirmed injection point into the authentication query. The fix is one pattern change — parameterisation — applied consistently across the codebase.