Docs
Checking for ownership hijack vulnerability

Checking for ownership hijack vulnerability

Learn how to check if anyone can become your contract's owner.

In this tutorial, you'll learn how to identify and prevent the ownership hijack attack — a vulnerability that lets malicious actors seize control of a contract or access privileged administrative functions.

You'll start by creating a deliberately vulnerable TON smart contract, then use the TSA Blueprint plugin to detect the security flaw. This practical approach shows how vulnerabilities can appear in code and how automated tools can catch them before deployment.

By the end, you'll be able to:

  • Add TSA security analysis to your Blueprint projects
  • Understand vulnerability reports and their implications
  • Verify the discovered vulnerability on a live blockchain network

1. Create a Project with a Vulnerable Contract

To see the TON ownership hijack checker in action, we'll first create a smart contract with a deliberate vulnerability — one that would allow an attacker to take over contract ownership.

Follow these steps to set up the project and implement the flawed contract.

Create a Blueprint Project

Before you begin, make sure you meet all the requirements for working with Blueprint. You can find the details in the Blueprint documentation.

Once everything is set up, create a new project by running:

npm create ton@latest

You'll be prompted to enter:

  • Project name — enter OwnerHijackDemo
  • First contract name — enter VulnerableContract
  • Contract template — choose An empty contract (Tolk)

We'll use this empty contract as a starting point to build our intentionally vulnerable example.

Enter the generated directory:

cd OwnerHijackDemo

Implement the Vulnerable Contract

The generated empty contract is located at contracts/vulnerable_contract.tolk.

Open this file and replace its contents with the following:

const OP_CHANGE_OWNER = 0
 
struct Storage { owner: address }
 
fun Storage.load() { return Storage.fromCell(contract.getData()); }
 
fun Storage.save(self) { contract.setData(self.toCell()); }
 
get fun getOwner(): address { return Storage.load().owner; }
 
fun setOwner(newOwner: address) { Storage { owner: newOwner }.save(); }
 
fun onInternalMessage(in: InMessage) {
    var msgBody  = in.body;
    var opcode: int = msgBody.loadUint(32);
    if (opcode == OP_CHANGE_OWNER) {
        setOwner(msgBody.loadAddress());
    }
}
 

Security Issue: This contract contains a critical vulnerability — it lacks authorization checks. Any user can send a message to change the owner however they like, including assigning the owner to oneself!

2. Set Up the TSA Plugin

To detect and analyze security vulnerabilities like the one in our contract, we'll use the TSA plugin.

Refer to TSA Blueprint Plugin Installation Guide for instructions.

3. Run the Owner Hijack Checker

Now let's analyze our vulnerable contract. Run the TSA owner-hijack checker with the following command:

yarn blueprint tsa owner-hijack-check -m getOwner -c VulnerableContract

By default, built-in checkers are interactive: TSA asks for confirmation twice — first before preparation and timeout calculation, and then again before the analysis starts. If you want to skip both prompts, add --no-interactive.

When you execute this command, TSA performs symbolic analysis of your contract. This means it systematically explores different execution paths to identify any that could lead to vulnerabilities.

Here's what the command options mean:

  • owner-hijack-check refers specifically to the TON ownership hijack checker — a specialized analyzer that searches only for execution paths where the owner of the contract might change.
  • -m getOwner specifies a get-method that retrieves the owner.
  • -c VulnerableContract specifies the name of the contract to analyze.

You can customize the analysis with additional options. To see all available parameters, run:

yarn blueprint tsa owner-hijack-check --help

The analysis process consists of:

  • Path exploration — Examining different ways the contract can be invoked
  • Constraint solving — Identifying the inputs needed to reach potentially vulnerable execution paths
  • Vulnerability detection — Flagging paths that permit unauthorized ownership changes

When vulnerabilities are found, the tool reports them along with detailed steps to reproduce each vulnerable execution path.

After a few moments, you'll see a vulnerability report similar to this:

⚠️ Vulnerability found!
Summary path: tsa/reports/run-<id>/summary.txt
Typed input: tsa/reports/run-<id>/typed-input.yaml
SARIF with full information: tsa/reports/run-<id>/report.sarif

The generated files are stored in tsa/reports/run-<id>:

tsa
└── reports
    └── run-<id>
        ├── contract-data.boc
        ├── message-body.boc
        ├── report.sarif
        ├── summary.txt
        ├── tsa-reproduce-config.json
        └── typed-input.yaml

Next Steps

There are two options for proceeding:

  • Manual investigation — Examine the generated files to understand the vulnerability in detail
  • Automated reproduction — Use TSA's built-in functionality to reproduce the issue on an actual blockchain

We'll explore both approaches starting with manual investigation. The automated reproduction will be described in Section 4 of this tutorial.

Manual Investigation

Let's break down what each component of the report means:

  • Summary (summary.txt): A concise overview of the vulnerability — this is what was displayed in your terminal.
  • Typed input (typed-input.yaml): The recommended file for investigation. It contains the typed representation of the reproducing message body and contract data in one place.
  • Raw BoC inputs (message-body.boc, contract-data.boc): The exact message body and contract data that reproduce the vulnerability.
  • SARIF report (report.sarif): A standardized format for static analysis results. This file contains comprehensive details about analyzed execution paths. For most use cases, you won't need to examine this file directly.

The checker has successfully identified our missing authorization check — anyone can change the owner of the contract.

4. Reproduce the Vulnerability on the Blockchain

Prerequisites

Before proceeding, ensure you have:

  1. A TestNet walletCreate one with Tonkeeper
  2. TestNet TONsGet TONs from the Testgiver bot

For this tutorial, you'll need at least 1.5 TestNet TONs to cover deployment and transaction fees.

Starting the Reproduction

The vulnerability report includes a command to reproduce the issue on-chain:

To reproduce the vulnerability on the blockchain, run:
> yarn blueprint tsa reproduce --config tsa/reports/run-<id>/tsa-reproduce-config.json

This configuration file, tsa-reproduce-config.json, is generated by tsa owner-hijack-check and contains all necessary data for the reproduction process.

Run the Reproduction Command

yarn blueprint tsa reproduce --config tsa/reports/run-<id>/tsa-reproduce-config.json

Configure Network and Wallet

You'll be prompted to:

  • Choose a network — Select testnet
  • Connect your wallet — Follow the on-screen instructions to link your TestNet wallet

Deploy the Vulnerable Contract

Next, you'll deploy the contract to TestNet. When asked:

  • Reuse of an already deployed contract: Answer no

Answer no issues a fresh deployment of a contract under test that will receive a message that triggers the found vulnerability. This is outside of the scope for this tutorial, but you can also send a reproduction message to an already deployed contract. To do this, answer "yes" to this question and proceed according to prompts. The data of the input contract will be checked, so don't be afraid to type the wrong address — the plugin will report the wrong contract state.

  • Contract funding amount: Enter 0.5 TONs. This is the money you are about to withdraw
  • Confirm deployment: Approve the transaction in your wallet app

Fund the Reproduction Attempt

You'll then specify how much you're willing to spend on reproducing the vulnerability:

  • Reproduction budget: Enter 0.5 TONs

Execute and Confirm

TSA will now:

  1. Re-analyze the contract with your wallet as the sender
  2. Generate a reproduction transaction if the vulnerability persists
  3. Prompt you to confirm this transaction in your wallet

Once confirmed, you'll receive a transaction ID.

Verify the Results

You can use the following explorers to view the transaction (TonViewer is recommended for better debugging tools):

TonViewer:

https://testnet.tonviewer.com/transaction/<transaction_id>

TonScan:

https://testnet.tonscan.org/tx/<transaction_id>

You can verify that the owner of the contract was changed.

Further Investigation

For deeper analysis, you can use:

  • TxTracer and ReTracer — Tools for transaction tracing
  • Typed data files — Generated earlier for detailed cell analysis

Since we already understand this vulnerability (missing authorization), extensive investigation isn't necessary — but these tools are useful for analyzing more complex issues.

5. Fix the Vulnerability and Re-run Analysis

Understanding the Fix

The vulnerability stems from missing authorization checks. To fix it, we need to add authorization logic: check the sender of the message for having the right to change the owner (or, in other words, check that the sender is the owner).

The Fixed Contract

Update contracts/vulnerable_contract.tolk with the following corrected code:

const OP_CHANGE_OWNER = 0
 
struct Storage { owner: address }
 
fun Storage.load() { return Storage.fromCell(contract.getData()); }
 
fun Storage.save(self) { contract.setData(self.toCell()); }
 
get fun getOwner(): address { return Storage.load().owner; }
 
fun setOwner(newOwner: address) { Storage { owner: newOwner }.save(); }
 
fun onInternalMessage(in: InMessage) {
    var msgBody  = in.body;
    var opcode: int = msgBody.loadUint(32);
    if (opcode == OP_CHANGE_OWNER) {
        assert(in.senderAddress == getOwner()) throw 501; // added check
        setOwner(msgBody.loadAddress());
    }
}
 
 

Verify the Fix

Re-run the TSA ownership hijack checker to confirm the vulnerability is resolved:

yarn blueprint tsa owner-hijack-check -m getOwner -c VulnerableContract

Expected Result: The tool should report that no vulnerabilities were found, confirming our security fix is effective.

Conclusion

In this tutorial, you've learned how to use the TSA ownership hijack checker to identify and fix a critical smart contract vulnerability. You've successfully:

  • Set up TSA security analysis in a Blueprint project
  • Detected unauthorized withdrawal vulnerabilities
  • Reproduced issues on TestNet

The TSA plugin offers additional security checkers beyond ownership hijack analysis — explore them with yarn blueprint tsa --help or read our other tutorials to further strengthen your contract security.

Happy secure coding!