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 customer portal, with products, updates, manuals, licences and support;
BackOffice, a duplicated administrative application with users, roles, permissions and CRUD pages for the same data.
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.
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:
- FinSpy PC and FinSpy Mobile: the surveillance agents;
- FinFly Web, ISP, LAN and USB: delivery products for different positions in the network or different access models;
- FinUSB Suite and FinFireWire: collection from locally accessible computers and media;
- FinIntrusion Kit and FinIPCatcher: tactical intrusion and network tooling;
- training, relay, engineering-acceptance and troubleshooting material.
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:
- Firefox XPI plugin or add-on delivery;
- a signed Java applet;
- iframe injection;
- a static page presenting fake installers for Windows, macOS and Linux.
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:
- Windows 7 SP1 — Acrobat Reader PDF exploit;
- Windows 7 SP1 — browser exploit;
- 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:
- a Google search for “what is my IP address” could return the Master’s IP;
- Yahoo’s
userLocationobject could reveal the Master’s city or country; - other decoys leaked similar data through cookies or localisation.
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:
- customer accounts were granted product access through activated licences;
- the portal exposed product information, documents, releases and support;
- customer packages were placed in separately HTTP-authenticated directories and additionally encrypted with customer-specific OpenPGP keys;
- FinFly Web preserved several web and social-engineering delivery modes;
- sales material demonstrated PDF, browser and Office exploit categories;
- the separate BackOffice managed the support system, not surveillance cases.
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:
- the exact worldwide customer count;
- the contents of the encrypted customer packages;
- the contents of the password-protected FinSpy disk image;
- a customer-by-customer list of exploits or CVEs;
- the FinSpy Master source code or a runnable operator console;
- a current universal FinFisher network signature.
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
- Citizen Lab, From Bahrain With Love: FinFisher’s Spy Kit Exposed?
- Citizen Lab, The Smartphone Who Loved Me: FinFisher Goes Mobile?
- Citizen Lab, You Only Click Twice: FinFisher’s Global Proliferation
- Citizen Lab, For Their Eyes Only
- Citizen Lab, Mapping FinFisher’s Continuing Proliferation
- Citizen Lab,
spyware-scanhistorical fingerprints - ESET, New FinFisher surveillance campaigns: Internet providers involved?
- ESET, A guide to deobfuscating and devirtualizing FinFisher
- Access Now, FinFisher changes tactics to hook critics
- Censys, Censys Query Language
- Censys, Platform Web Property Dataset
- Phineas Fisher, Hack Back: A DIY Guide for Those Without the Patience to Wait for Whistleblowers
- Peter Maynard and Kieran McLaughlin, Big Fish, Little Fish, Critical Infrastructure: An Analysis of Phineas Fisher and the ‘Hacktivist’ Threat to Critical Infrastructure
#Malware-Analysis #Threat-Research #Threat-Intelligence #Censys #Finfisher