Martin's Blog

FinFisher: reconstructing a surveillance platform from its support server

It has been some time since the FinFisher leak, and I thought it might be interesting to take another look and see whether the material could still tell us something new. I got hold of the data and started reviewing it: first the directory tree, then the source code, database dump, manuals, customer packages and assorted sales material.

One of the obvious questions was whether the old FinFisher platform could be made to run again.

The answer is yes, but not in the way the question suggests. The code in the archive is a customer support system. It has product pages, licences, software updates, support tickets and a separate administrative BackOffice. It is not the FinSpy operator console, and it is not the FinSpy Master command-and-control server.

That initially sounds disappointing. It is not. A support server sits at an interesting junction between sales, engineering and operations. Its database describes which accounts could see which products. Its download tree shows how customer packages were staged. Its demo pages preserve several infection mechanisms. Combined with Citizen Lab’s scanning work, it is enough to reconstruct a useful part of the platform without running a single payload.

One caution applies throughout: a folder called FinFly-ISP proves that the product existed in the archive. It does not prove that every customer bought it, or that every advertised capability worked as described.

What was actually leaked

The substantial source code is under www/GGI. It is a classic PHP 4/5-era application: short tags, mysql_*, no framework, and one PHP file per page. A central include creates the database connection and nearly every model as a global before wrapping the page in a shared template.

There are really two applications under that directory:

The BackOffice is easy to mistake for the interesting console. It is not. It is the place where Gamma staff managed customer accounts and support content.

The rest of the web root is more revealing than the PHP:

Path Role
www/FinFisher/<hex-id>/ Password-protected customer delivery drops
www/FinFisher/Engineers7117/ Internal engineering staging area containing encrypted products, manuals and training material
www/FinFisher/Sales/ Product portfolios, roadmaps, sales kits and demonstration videos
www/ffw/ FinFly Web demonstration pages and delivery artefacts
qateam/ Mobile QA builds for Android, BlackBerry, Symbian and Windows Mobile
Database.sql The populated support database

The www/FinFisher tree is about 38 GB. Twenty-one customer-style directories contain files named Package.zip.gpg, Package1.zip.gpg or Package2.zip.gpg. I checked every one of those files with GnuPG’s packet parser. They contain a public-key-encrypted session-key packet followed by an encrypted data packet. They are encrypted, not merely signed, and the archive does not contain the corresponding OpenPGP private keys.

A password-recovery detour

There is a separate encrypted artefact in the engineering tree: FinSpy-PC+Mobile-2012-07-12-Final.zip. It is a 33.7 GB Zip64 archive containing one 34.2 GB Acronis .tib disk image. It uses legacy ZipCrypto rather than OpenPGP or AES, so this was a ZIP-password problem—not a missing-private-key problem. Fisher’s 2014 account names the same file as an unresolved obstacle.

Using Hashcat mode 17200 on an NVIDIA GPU, I tested roughly 1.2 billion raw SecLists, RockYou and Rocktastic candidates, followed by just over one trillion rule-generated mutations. None matched. I stopped a much larger Rocktastic-plus-rules run when it expanded to roughly 55–59 trillion candidates per pass.

That does not prove the password is strong; only that it was absent from the tested candidate families. A contextual or known-plaintext approach may be more useful than further blind guessing. The disk image was never extracted, mounted or executed.

This is a distribution server with an application attached to it, not a source release of the surveillance backend.

How the archive leaked

In August 2014, someone using the name Phineas Fisher published Hack Back: A DIY Guide for Those Without the Patience to Wait for Whistleblowers. It is a first-person account claiming responsibility for the Gamma intrusion. It is not a forensic report, so I treat it as the attacker’s version of events and compare it with what survives in the source tree.

The story begins with ordinary reconnaissance. Domain-registration pivots led to the FinFisher support site. A distinctive path, Scripts/scripts.js.php, then exposed other sites built by the same small web-design company. Fisher claims those weaker sites supplied enough reused source code to understand the custom application before returning to the FinFisher portal.

The claimed compromise chain is short:

Stage Fisher’s account What this archive supports
Discovery Registration data and a shared script path identified the support portal and related applications The unusual Scripts/scripts.js.php path exists in both portal trees
Initial access SQL injection in GGI/Home/print.php exposed the database and allowed server-side files to be read The endpoint passes attacker-controlled id data into SQL; its failed validation does not stop execution
Execution A customer credential from the database was used to create a support ticket carrying a PHP attachment The upload handler preserves the submitted extension and has no server-side extension allowlist
Escalation and pivoting An attempted route to root failed; internal discovery found a separate qateam web server The leak includes the QA tree and mobile builds, but not evidence sufficient to replay the network pivot
Collection Source, database records and hosted files were taken, but the FinSpy server software was not obtained That boundary matches the material in the leak: support, distribution, sales and QA—not FinSpy Master

The SQL injection is a nice example of validation that looks useful but is not. print.php checks whether id is numeric, but on failure it only emits JavaScript asking the browser window to close. PHP continues running and calls getMaterialInfo() anyway. The class constructs the query by inserting the value directly:

$MaterialID = $_GET['id'];
// The non-numeric branch prints window.close(), but does not exit.
$result = $material->getMaterialInfo($MaterialID);

$query = "Select * From material Where MaterialID = $MaterialID";

The ticket upload is similarly trusting. It takes the suffix from the supplied filename, prefixes it with a generated tracking ID, and passes the result to move_uploaded_file() without checking an allowlist on the server. That corroborates the unsafe primitive described by Fisher. It does not prove every runtime detail of the shell claim: the archived attachments directory contains an .htaccess file intended to deny direct remote access. The live deployment could have differed, the rule could have been ineffective, or another route could have been involved.

A 2020 paper by Peter Maynard and Kieran McLaughlin, Big Fish, Little Fish, Critical Infrastructure, places this in a broader pattern. Across Fisher’s published operations, the authors map recurring behaviour to ATT&CK: exploitation of Internet-facing applications, valid accounts, web shells, discovery and lateral movement, collection, proxying and exfiltration. That is useful context, not independent proof. The paper is explicit that much of its model comes from Fisher’s own post-mortems, supplemented by reporting and public documentation.

The most important line in the 2014 account is therefore not an exploit detail. It is the admission that the attempt to obtain the FinSpy server software failed. That explains both the value and the limits of this archive. The breach reached the loading dock and a QA shelf, but apparently not the machine behind the surveillance operation.

How many customers?

There is no honest single number.

The database snapshot contains 88 customer-account rows. Of those, 85 are not marked deleted. Fifty-four accounts have at least one activated, non-deleted licence. Across those accounts I counted 202 unique account/product pairs, covering ten product IDs. A licensed account had between one and ten products.

Measure Count
Customer account rows 88
Accounts not marked deleted 85
Accounts with an active, non-deleted licence 54
Active account/product pairs 202
Product IDs represented by active licences 10
Encrypted Package*.zip.gpg drops 21

The database also has 749 licence rows and 330 support requests. Multiple licences can belong to one account and product—for different installations, machines or versions—so counting licence rows as customers would be nonsense.

The application confirms that product visibility was licence-driven. Product pages call hasPurchasedThisProduct(), which checks for an activated licence for the current customer and product. The similarly named customer_product table is mainly used for release-notification preferences; it is not the authoritative entitlement table.

None of these numbers equal “FinFisher had 88 customers.” An account can be a demo, an internal user, a reseller, a second account for the same organisation, or an abandoned record. The encrypted drop directories do not map cleanly to the database without using historical identity data, which I deliberately did not expose.

Citizen Lab approached the question from the other direction. Its 2015 scan identified 135 matching servers and assessed that they represented 33 likely government users in 32 countries. That is an infrastructure census, not a contract database. The two sets of numbers constrain the answer, but neither is a worldwide customer total.

Two planes, not one monolithic platform

The archive and the public reporting make more sense when split into two planes.

FinFisher support, distribution and operational surveillance planes
The leak exposes the support and distribution plane. The operational path is reconstructed from the archive and Citizen Lab's public research.

The support server prepared and distributed software. The operational plane collected data from infected devices.

According to Citizen Lab’s 2015 report, a customer received a FinSpy Master intended to run on its premises. Targets normally communicated with one or more FinSpy Relays hosted elsewhere. The relay hid the address of the Master and forwarded traffic in both directions. The operator console sat behind the Master side of this arrangement.

This means an Internet scan did not normally discover a login page labelled “FinSpy operator console.” It found the network-facing edge of the collection system. Calling every matching IP an operator console confuses three different components and overstates what the scan proves.

Product catalogue versus exploits

The archive names a broad product family:

It is tempting to turn that list into a list of exploits. That would be wrong. An exploit abuses a vulnerability. An infection vector gets code onto a target. FinFisher sold both concepts under adjacent product names, while much of the publicly observed delivery depended on deception or privileged network access rather than a client vulnerability.

What the archive proves

The FinFly Web demo has version-one and version-two pages for:

These delivery mechanisms are present in the archive. An add-on prompt, signed applet or fake installer may succeed without exploiting a memory-corruption bug; the user can be persuaded to approve or run it.

The Sales/FinSploit.zip manifest is also useful. It contains three AVI demonstrations with these titles:

  1. Windows 7 SP1 — Acrobat Reader PDF exploit;
  2. Windows 7 SP1 — browser exploit;
  3. Windows 7 SP1 — Microsoft Office 2010 DOC/XLS exploits.

The ZIP contains videos, not the exploit implementations. The filenames prove that Gamma demonstrated those classes of exploit in 2011. They do not identify the vulnerabilities, CVEs, reliability, provenance or the customers who could use them.

What public samples add

Citizen Lab’s Bahrain analysis documented executable attachments disguised as images or documents, including right-to-left filename tricks. Once running, the Windows agent used process hollowing and an MBR bootkit. Those are execution, persistence and concealment techniques, not initial-access exploits.

The later 2015 report described a weaponised Word document containing a large embedded binary and downloading a FinFisher payload. The report did not turn that observation into a complete customer-facing exploit catalogue.

ESET’s 2017 investigation found another important route: in-path replacement of legitimate software downloads. Victims requesting applications such as messaging clients, media players or archive tools received an HTTP 307 Temporary Redirect to a trojanised installer. ESET assessed that ISP-level placement was the most likely explanation in two countries. No browser exploit was required for that step; control of the network path was the capability.

Citizen Lab’s early mobile samples and Access Now’s later Android work both describe user-installed packages. The reports do not support calling those samples zero-click mobile exploits.

The defensible conclusion is therefore narrower than the sales catalogue: FinFisher customers had access to several delivery products, and Gamma demonstrated PDF, browser and Office exploit classes. The archive does not let us produce a customer-by-customer list of CVEs. The customer packages that might answer more are encrypted.

How Citizen Lab found the servers

Citizen Lab’s work evolved because the servers evolved.

The early scans used peculiar HTTP responses. The group’s public spyware-scan repository preserves three historical regular expressions:

Fingerprint Distinguishing response
2.0 Apache, Vary: Accept-Encoding, length 204, ISO-8859-1 and a trailing UTC marker
3.0 HTTP 200 with UTF-8 content type and an empty body
4.0 Apache, HTTP 200, length 140 and ISO-8859-1

Rapid7 also reported the memorable Hallo Steffi HTTP behaviour. These were not magic vendor banners. They were response anomalies useful when combined with sample analysis and repeated scanning. FinFisher changed its behaviour, and the older probes stopped working around late 2012.

For the 2015 work, Citizen Lab derived a new fingerprint from FinFisher samples and used zmap across IPv4. The report says the fingerprint found 135 servers, but the article does not publish a turnkey byte sequence for reproducing that scan.

The clever part came after discovery. When queried in a browser, many servers returned a decoy page—usually Google or Yahoo. The relay apparently asked the Master to fetch that page instead of fetching it directly. This leaked the Master’s point of view:

That let the researchers separate an overseas relay from an on-premises Master. It was an architectural mistake expressed through web content.

By the time of Access Now’s 2018 samples, the easy path had largely closed. Relays no longer returned the same useful decoys, domains were more disposable, and servers unable to decrypt a sample-bound request stayed silent. A current HTTP keyword search should not be presented as a reliable detector for every FinSpy generation.

Searching Censys without fooling yourself

Censys works best here as a passive candidate generator and pivot engine. The method is simple: search broadly, pivot on exact features, then require independent corroboration. The examples use Censys’s current query language and Web Property fields.

1. Seed with historical features

Run separate searches for the response artefacts documented in earlier research:

web.endpoints.http.body: "Hallo Steffi"
web.endpoints.http.body: "userLocation"
web.endpoints.http.headers: "content-length 140"
  and web.endpoints.http.headers: "text/html charset iso-8859-1"

These are discovery queries, not attribution. The strings and Apache response shape are old and non-unique, but they can reduce a historical dataset to a manageable candidate set.

2. Search archive-derived artefacts

The leak supplies three more searches for the support and FinFly Web planes:

web.endpoints.http.body: "FinFly Web Demo Page"
web.endpoints.http.body_hash_sha256="635e2ee5269a7f32bd0f9c8a554ba88c82e5bbd61e467721eb5488b558f13bb3"
web.endpoints.http.favicons.hash_sha256="3b60e89883932958d7e3264d4418ec05188812a7ac0ce0b87cf9380fbe96e0d1"

Use the text search to find modified copies and the hashes to find exact ones. The favicon identifies a reused support-portal asset, not a FinSpy C2 or operator console.

3. Pivot from a known positive

Starting from a sample-configured endpoint, packet capture or historical Censys record, pivot on exact response and certificate values:

web.endpoints.http.body_hash_sha256="<known-positive-body-sha256>"
web.endpoints.http.favicons.hash_sha256="<known-positive-favicon-sha256>"
web.cert.fingerprint_sha256="<known-positive-certificate-sha256>"

Then compare first- and last-seen dates, hosting, redirect behaviour and shared certificates. Censys explains what it retains and hashes in its Web Property documentation.

4. Corroborate before attributing

A response string or header shape is low-confidence. An exact historical hash with matching dates and infrastructure is better. The strongest case combines a sample-configured endpoint, a protocol fingerprint and matching web artefacts.

Finally, count what was actually found. A relay, Master, domain and customer are different units, and IP addresses can be reassigned. Keep the investigation passive unless you own the target or have explicit authorisation; stored Censys observations are sufficient for this workflow.

What can and cannot be reconstructed

The support-server archive gives a surprisingly coherent view of the business and distribution architecture:

Citizen Lab’s work supplies the missing operational topology: implant to Relay, Relay to customer-hosted Master, then operator console. It also shows why infrastructure fingerprinting is temporal. The HTTP oddity that works in one version becomes a silent server in the next.

What we cannot reconstruct is equally important:

All in all, the archive is not the attack backend. It is the loading dock. But if you combine what passed through that loading dock with careful public infrastructure research, the outline of the larger system becomes visible. Just do not mistake the outline for the machine itself.

References

#Malware-Analysis #Threat-Research #Threat-Intelligence #Censys #Finfisher