Skip to content
HomeAboutServicesGuidesContact

Guide

Web3 penetration testing

The contract is the most reviewed part of a crypto product and the smallest part of its attack surface. What a penetration test covers, and why the paths it finds rarely touch the chain.

In short

  • An audit reads code that runs on chain. A penetration test attacks everything that surrounds it, which is where most of the operational surface lives and where least of the review budget goes.
  • Users cannot verify the front end. They can read the contract, compare the deployed bytecode and check the governance, and still sign a transaction that a swapped interface built for somebody else.
  • Key custody is an infrastructure question before it is a cryptographic one. The signing scheme can be sound while the machines that hold the signers are not.
  • Anyone who can change what the pipeline ships holds an upgrade path, whether or not the contract has one.
  • The output that matters is an attack path, not a list of weaknesses. Three findings that chain are a different fact from three that do not.

What the audit did not cover

A smart contract audit has a scope, and a report worth reading states it: these files, at this commit, on this chain. Everything outside that boundary is not a gap in the work. It is a different piece of work that was not commissioned.

That boundary is narrower than most teams assume. The contract is the part that holds the funds, so it receives the review. It is also the part an attacker finds hardest to change, because it is public, immutable by default, and watched. The surrounding system is none of those things.

In plain terms: the audit answers whether the vault is built correctly. A penetration test asks who has a key to the room the vault is in, and what happens if they are not the person you think.

The surface nobody draws on the diagram

Architecture diagrams show contracts, a front end and an API. The surface an intruder works against is larger and less tidy.

It includes the hosting account that serves the interface and the DNS records that point at it. The API and whatever it trusts about the client calling it. The cloud roles, including the ones granted for a migration two years ago and never revoked. The repository, the people with merge rights, and the automation that holds deploy credentials. The administrative tooling built quickly because only staff would ever see it. The laptops and phones belonging to the people who sign transactions.

In plain terms: your protocol is on chain, but your organisation is not, and an attacker is content to attack either.

The front end your users cannot verify

A careful user can read your contract, confirm the deployed bytecode matches what was audited, and check who holds the admin keys. None of that tells them anything about the page they are looking at.

They are trusting that the domain still resolves to your hosting, that the hosting still serves your build, and that every dependency in that build is the one you intended. A registrar account with a recoverable password, a stale token with write access to the hosting bucket, or a compromised package somewhere in the dependency tree each produce the same outcome: a page that looks correct and constructs a transaction that is not.

This path takes user funds without touching the protocol. The contract behaves exactly as audited, the chain records a valid signature, and nothing on chain was defeated, because the deception happened before the user signed.

In plain terms: the interface is part of your product in every way that matters to a user, and it is the part they have no way of checking.

Keys are infrastructure before they are cryptography

A multisig with a five-of-nine threshold is a statement about cryptography. Whether five of those nine can be reached is a statement about operations, and the second question is the one an intruder asks.

The practical questions are where each signer's key material lives, what else runs on that machine, whether the hardware wallet is held by a person or sits permanently connected in an office, how a signer is replaced when somebody leaves, and whether any single person controls enough of the signing set to act alone in practice.

A recovery path deserves the same attention. Seed phrases in a shared password manager, a cloud backup made once during setup, or a custodian whose own account recovery runs through an email address, each reduce the scheme to the weakest of those paths rather than to its stated threshold.

In plain terms: a threshold describes how many signatures are required. It does not describe how hard those signatures are to obtain, and only the second number is a security property.

The pipeline is an upgrade path

A protocol that advertises immutable contracts can still change what its users interact with, because something decides which build reaches production and which address the interface points at.

That decision usually sits with a pipeline: a repository, a set of merge permissions, and automation holding credentials for the hosting account and sometimes for a deploy key. Whoever can influence what that pipeline ships has an upgrade path over the product, and it is rarely governed like one. Contract upgrades get a timelock and an announcement. A front-end deploy gets a merge.

The question worth asking is whether what reached production is what was reviewed. That is answerable, by matching the deployed artefacts against the audited commit, and it is a different exercise from the audit itself.

In plain terms: a timelock on the contract and a one-click deploy on everything else is a strong lock on one door of a building with several.

Findings that chain, and findings that do not

A scanner produces a list. A penetration test produces a route, and the difference is what a buyer is paying for.

Three isolated issues of middling severity may be worth scheduling. The same three, where the first yields a credential that reaches the second and the second grants access used by the third, describe a path from the public internet to your signing infrastructure. The individual ratings are unchanged; the conclusion is not.

This is why the deliverable is a narrative. It states where the test started, what each step yielded, how far it reached, and where it was stopped, either by a control that worked or by the agreed limit of the engagement. A reader can then judge the exposure rather than count issues by colour.

In plain terms: severity ratings describe weaknesses one at a time. An attacker does not use them one at a time.

Authorisation is the part that protects you

A penetration test is an attack carried out with permission, and the permission is a document rather than an understanding. It names the systems in scope, the accounts and addresses used, the window, the people to contact, and what is excluded.

The exclusions matter as much as the inclusions. Destructive testing, denial of service, and anything touching real customer data are normally out of scope unless a client asks for them specifically and a window is agreed. Testing is usually run against an environment that mirrors production, with production reserved for findings that cannot exist anywhere else.

Scope also protects third parties. Your cloud provider, your custodian and your payment processor are separate parties with their own rules, and testing their systems because yours depends on them is not yours to authorise.

In plain terms: the written scope is what separates a security engagement from an incident, and it is written before anyone starts.

What to ask of your own system

Six questions, answerable without a test.

If somebody held your DNS registrar account for one hour, what could they do, and how quickly would you know?

Which credentials can deploy the front end, where do they live, and when was that list last checked against the people who still work here?

For each signer on the multisig: what machine holds the key, what else runs on it, and who could reach it?

Which cloud roles still exist from a migration or an incident, and what can each of them reach today?

If an administrative account were taken over, what is the worst sequence of actions available, and is any step of it alerted?

What proves that the interface your users loaded this morning was built from the commit you believe it was?

Questions

Our contracts are audited. Why would we need this as well?

Because the audit covered the contracts. The front end, the APIs, the cloud account, the deploy pipeline and the machines holding signing keys are outside any contract audit's scope, and an attacker chooses the surface, not you. Teams that buy only the audit have reviewed the part that is hardest to change and left the rest unexamined.

Does a bug bounty cover the off-chain surface?

Partly, and unevenly. Researchers go where the reward is clearest, which tends to be the contracts and the public web application. Cloud configuration, pipeline permissions and signer infrastructure are frequently out of a bounty's scope, and a bounty tells you only what somebody found and chose to report. It cannot tell you that a surface was examined.

Can this run without touching production?

Mostly. A staging environment that genuinely mirrors production covers the majority of the work. Some findings exist only in production, such as a specific cloud role or a DNS configuration, and those are agreed individually in writing with a window and a named contact rather than assumed at the start.

How often is it worth repeating?

The useful trigger is change rather than a date. A new signer, a cloud migration, a change to who can deploy, or a significant new feature each alter the surface that was tested. Teams shipping continuously tend to settle on an annual test with a retest after any material change to access or infrastructure.

The work behind this

Related reading