A scanner finds nothing in a thick client and that is not a result
A web scanner starts from an address and works outwards through whatever each response hands it. A desktop application hands it an installer. There is no address to start from, no markup to parse, no request it can read and nowhere addressable to put a payload — so the run completes, the report is empty, and the empty report gets filed. This is what is on the other side of that blank page.
Yash K · · What we test · about 12 min
The empty report
The scan came back clean, and it was never going to come back otherwise
This is not a tuning problem, a licence problem or a question of pointing the tool somewhere better. It is structural, and it is worth being precise about why.
A web scanner does the same two things. It fetches something, and it reads what came back for places to go next and places to put a payload. Anchors, forms, script, redirects, parameters, headers, path segments — each one is a named, addressable location, and the whole discipline of automated web testing rests on the fact that a response tells you where the application continues. A thick client tells you nothing, because a thick client is a compiled program that talks to a server, and the conversation it has is not made of documents. There is no first address. There is no markup describing what the application can do, because the operations are code paths rather than links. There is no response body in a format anything but the client itself can decode. Point a scanner at a desktop application and it has not failed — it has correctly reported that it found no surface, which is true, and which is not the same statement as the one the report gets read as. The surface exists. It comes into being when the program runs, it is made of whatever the two ends agreed to speak, and somebody has to construct a way of seeing it before a single test can be attempted.
The six prerequisites
What a crawler needs before it starts, and what a desktop application offers instead
Read down the first column. Each row is something a web application supplies for free and a thick client does not supply at all.
| What the tool needs | Where a web application supplies it | What a thick client supplies |
|---|---|---|
| Somewhere to begin | The address you typed into the tool. Everything else in the run is reached from it. | An installer. Nothing in the package is obliged to announce which servers it will reach, and a program that has not been run yet has not reached any of them. |
| Somewhere to go next | Anchors, forms and script, parsed out of each response and added to a queue that drives the whole crawl. | A compiled interface. The operations it can invoke exist as code, and code does not enumerate itself to anybody standing outside the process. |
| A request it can read and rewrite | A method, a path, headers and a body, in a format a proxy parses without being told how. | Whatever the two ends settled on: a length-prefixed binary frame, a serialised object graph, a long-lived socket carrying something that is not HTTP in any respect. |
| A session it can hold | A login form it can fill, and a cookie or header it can replay on every request afterwards. | An authentication exchange inside the protocol, with the credential assembled by code whose behaviour is written down nowhere outside the binary. |
| A response it can judge | Text to match against for an error, a stack trace, a timing difference, a payload coming back reflected. | Bytes the client alone knows how to decode. To the tool it is a length and a blob. |
| Somewhere to put the payload | A parameter, a header or a path segment — every one of them named and addressable. | A field inside a structure whose layout is defined by agreement between client and server and recorded in neither of them. |
Somewhere to begin
- Where a web application supplies it
- The address you typed into the tool. Everything else in the run is reached from it.
- What a thick client supplies
- An installer. Nothing in the package is obliged to announce which servers it will reach, and a program that has not been run yet has not reached any of them.
Somewhere to go next
- Where a web application supplies it
- Anchors, forms and script, parsed out of each response and added to a queue that drives the whole crawl.
- What a thick client supplies
- A compiled interface. The operations it can invoke exist as code, and code does not enumerate itself to anybody standing outside the process.
A request it can read and rewrite
- Where a web application supplies it
- A method, a path, headers and a body, in a format a proxy parses without being told how.
- What a thick client supplies
- Whatever the two ends settled on: a length-prefixed binary frame, a serialised object graph, a long-lived socket carrying something that is not HTTP in any respect.
A session it can hold
- Where a web application supplies it
- A login form it can fill, and a cookie or header it can replay on every request afterwards.
- What a thick client supplies
- An authentication exchange inside the protocol, with the credential assembled by code whose behaviour is written down nowhere outside the binary.
A response it can judge
- Where a web application supplies it
- Text to match against for an error, a stack trace, a timing difference, a payload coming back reflected.
- What a thick client supplies
- Bytes the client alone knows how to decode. To the tool it is a length and a blob.
Somewhere to put the payload
- Where a web application supplies it
- A parameter, a header or a path segment — every one of them named and addressable.
- What a thick client supplies
- A field inside a structure whose layout is defined by agreement between client and server and recorded in neither of them.
Three constructions
There is no surface until somebody makes one
Each of these produces something a scanner would need in order to start. None of the three can be produced by scanning.
Getting between the client and the server
- What is there to begin with
- A connection, and the knowledge that bytes are moving along it. Nothing that can tell you what any of those bytes mean, or which of them correspond to an action a user took.
- What has to be done
- The transport has to be terminated somewhere under your control, and the frames crossing it have to be parsed into operations, one at a time, before a single one can be altered and sent back.
- What it yields
- The first artefact in the whole exercise that resembles a list of requests — and it was manufactured rather than discovered, which is the entire difference from a web assessment.
Reading what the application was told to trust
- What is there to begin with
- A file your users double-click, carrying everything the program needs to reach a server it will never be told about at runtime.
- What has to be done
- The package is opened and read for the decisions that were compiled in rather than configured: where it connects, what it presents in order to get in, and which of the checks it performs are the only copy of that check anywhere.
- What it yields
- Destinations, credential material and trust decisions — none of which a crawler could have requested, because there was no responder to request them from.
Reading what the run leaves behind
- What is there to begin with
- An installed program and a session used the way a real one gets used, on a computer whose disk and registry are part of the application whether anyone scoped them or not.
- What has to be done
- Local state has to be captured while the program is live, because what is not captured then can be overwritten or removed by the time the process exits and cannot be recovered afterwards.
- What it yields
- Artefacts with a path and a list of accounts that can read them. There is no worse version of this that a scanner performs — a scanner has no concept of a local file, so it does not attempt it at all.
The protocol
Pointing the tool at the port does not rescue it
It is the obvious next move, and it is worth following through to where it stops, because it stops early.
Suppose you skip the client entirely and aim the scanner at the port the client talks to. The tool connects and begins doing the one thing it knows: sending requests shaped like HTTP requests and reading replies shaped like HTTP replies. The server on the other end is speaking something else. It either closes the connection, or answers with a framing error, or waits for a handshake that never arrives — and the run ends with the same empty report by a different route. Even where the transport happens to be HTTP underneath, the payload riding on it is a serialised structure that the scanner has no schema for, so it cannot locate a field, cannot substitute a value, and cannot recognise that the reply it got back was an authorisation failure rather than a success. Its entire attack corpus is written in a language of parameters and it is looking at a language of messages. Add certificate pinning compiled into the binary and even the observation problem gets harder: the client will refuse to speak through anything it was not built to trust, which has to be dealt with in the program before the protocol becomes visible at all. None of this is exotic engineering. It is simply work that happens before scanning, which is why a product whose whole design assumes the surface already exists reports that there is nothing there.
Weakness classes
Five weaknesses that live where a crawler has never been
The first two carry a measured rank and it is an archive-wide rank: across the whole engagement archive, every coverage class pooled together. It is not a thick-client frequency, and no figure on this page is attached to the desktop class.
| State | What it means | What follows |
|---|---|---|
| CWE-798 · Use of hard-coded credentials | A username, password or token compiled into the program because the program had to authenticate to something and there was nowhere else to put it. It ships to every machine the installer reaches, identical on all of them. | A crawler sees none of it. The string sits in a file on a disk it never had. |
| CWE-321 · Use of a hard-coded cryptographic key | The key that protects the traffic, the licence check or the local store, shipped inside the artefact it protects. Recovering it is not an attack on the cryptography; it is reading the package. | A crawler sees none of it, and would not know a key if a response handed it one. |
| CWE-319 · Cleartext transmission of sensitive information | A protocol nobody watched, because watching it needed a parser somebody had to write. Not being HTTP keeps the traffic out of the sightline of the tools that look at traffic. | Visible only once the frames on the wire have been turned into operations. |
| CWE-306 · Missing authentication for a critical function | A server operation the interface draws for nobody, reachable by anything that can construct the message. The interface is the access control, and the interface is running on hardware somebody else owns. | Unreachable by a tool that can only send messages the official client would send. |
| CWE-284 · Improper access control Terminal | The account can ask for it, and the server answers. Whether it should have is a decision that was taken in the client, at a point where it could only ever have been advice. | Requires two accounts, a message from one and an answer meant for the other. |
- Second and third most frequent across the engagement archive — every coverage class pooled, not a desktop figure
- Recorded across the same archive. No rank and no count is stated for it
- TerminalNo state follows this one
Sourcing
Where those two rankings come from, and what they do not say
The archive-wide CWE ranking, stated beside the claim that uses it As of 2026-09-15
- The ranking is taken across the whole engagement archive — every coverage class Security Brigade has worked, pooled into a single set and counted by weakness identifier.
- Hard-coded credentials (CWE-798) and hard-coded cryptographic keys (CWE-321) sit second and third in that set by frequency.
- Cleartext transmission (CWE-319), missing authentication for a critical function (CWE-306) and improper access control (CWE-284) are recorded in the same archive. No rank is stated for them, and none is implied by the order they appear in above.
- Every engagement Security Brigade has run since the firm started in 2006 was worked inside Lemon, which is where that archive lives and what trained the models the platform runs on.
Deliberately excluded
- It is not a thick-client frequency. The ranking was taken across every class at once, so no count and no proportion anywhere on this page is attached to the desktop class.
- It is not a prediction about your application. A weakness being frequent across an archive says nothing whatever about whether it is in the binary you distribute.
- Physical, hardware and wireless testing, which are out of scope for the platform in every class.
The paper trail
Four ways an empty result gets written down
Each of these is a sentence somebody wrote in good faith. Each one reads, a year later, as a statement about the application.
‘No vulnerabilities found’
The tool ran, reached nothing, and produced a report with the target named across the top of it. Nobody reading that report afterwards can distinguish an application that was tested and found sound from one that was never reachable in the first place.
‘Not applicable’
The honest version. The tool declined the target because it could not parse it, and somebody recorded that accurately. The exception is correct on the day it is written, and it becomes permanent, because nothing was scheduled to take its place.
‘Covered by the server test’
The interface behind the client was assessed, driven by a request corpus captured while the official client was used. That corpus is, by construction, the set of messages the developers meant to exist — which is the one set that proves least about what the server will accept.
‘Out of scope’
The desktop client was never in the application inventory, because an application inventory is a list of addresses and this application has no address. The same blindness, one layer further up, deciding what gets tested at all.
Where this stops
This article argued one thing. These pages carry the rest
What a desktop assessment actually does, what it needs from you, and where it hands over to an adjacent class — none of which is on this page.
Thick client penetration testing
The desktop runtimes covered, what happens phase by phase, which actions wait for your written approval, the standards each finding is mapped against and the scope agreed before a run starts.
AdjacentWhen the server has callers besides this client
Where the interface behind the client has consumers of its own, it is worked as its own class with its own routes and a credential for each role.
AdjacentWhen the question is why it was built that way
Reading the repository answers a different half of the same question. The package tells you what was shipped; the source tells you what it was meant to do.
Before scopingWhat one scan covers
What a single scan includes, what counts as one target, and what is agreed in writing before anything is exercised.