Documentation Index
Fetch the complete documentation index at: https://docs.tac.build/llms.txt
Use this file to discover all available pages before exploring further.
Bug Bounty Report Guide
The report template below covers vulnerabilities in TAC-related FunC / Tact contracts deployed on TON.
Tools: Blueprint, toncli, tonutils-go, @ton/ton (JS SDK).
Report Template
SECURITY VULNERABILITY REPORT
<Protocol> — <Severity> <Class> in <ContractName>
Date: YYYY-MM-DD
Status: <THEORETICAL | TESTNET CONFIRMED | MAINNET CONFIRMED>
===============================
EXECUTIVE SUMMARY
===============================
<op handler broken, supply/TON at risk, repeatability>
===============================
VULNERABILITY DETAILS
===============================
Contract: <file.fc / .tact>, <op handler>, line <X>
Deployed: EQ<address>
Class: <category>
Root cause:
<one sentence — missing check / wrong branch condition>
Vulnerable snippet:
<code>
Fixed version:
<code>
TON-specific notes:
<bounce handling, gas forwarding, replay guard — if relevant>
Prerequisites:
<conditions>
===============================
EXPLOITATION STEPS
===============================
Message body:
op: <hex>
payload: <fields>
1. <step>
2. <step>
...
Blueprint test / script:
<abbreviated code>
===============================
POC RESULTS
===============================
Contract: EQ<address>
Exploit TX: <tonviewer link>
Balance table:
<address> before → after delta
Scaling:
<calls → minted / drained → gas>
===============================
IMPACT ASSESSMENT
===============================
Severity: <CRITICAL | HIGH | MEDIUM | LOW>
1. <point>
2. <point>
===============================
RECOMMENDED FIX
===============================
Immediate: <change>
Long-term: <architectural / tooling>
Audit: <adjacent ops / handlers>
===============================
PROOF-OF-CONCEPT CODE
===============================
<full Blueprint test or @ton/ton script>
===============================
DISCLOSURE TIMELINE
===============================
YYYY-MM-DD <event>
YYYY-MM-DD <event>
SECURITY VULNERABILITY REPORT
<Protocol> — <Severity> <Class> in <ContractName>
Date: YYYY-MM-DD
Status: <THEORETICAL | TESTNET CONFIRMED | MAINNET CONFIRMED>
Useful classes:
Unprotected Internal Message, Bounce Exploit, Storage Drain,
Replay Attack (no op-guard), Incorrect Fees / Forward Gas,
Cell Overflow, Tact Inheritance Access Control.
2. Executive Summary
Same four questions:
- What is broken? — which op-code handler / recv_internal branch.
- What does the attacker gain? — TON drained / token minted / ownership.
- How much? — contract balance or jetton supply at risk.
- Is it repeatable? — per-message / once / needs timing.
Example:
The jetton minter's recv_internal handler does not verify the sender against
the admin address before processing op::mint. Any wallet can send a mint
message and create arbitrary tokens, inflating the supply without limit.
Contract balance at risk: 0 TON (no drain)
Token supply at risk: unlimited inflation
Attack cost: ~0.05 TON (gas)
3. Vulnerability Details
3.1 Affected Contract
Contract: contracts/JettonMinter.fc (or .tact)
Handler: recv_internal — op::mint branch (line ~47)
Deployed: EQ<address> (mainnet / testnet)
Provide a Tonviewer / Tonscan link if the contract is verified.
3.2 Root Cause
The op::mint branch reads sender_address from the message slice but
never calls throw_unless(73, equal_slices(sender_address, storage::admin))
before executing the mint logic.
3.3 Vulnerable Code Snippet
;; JettonMinter.fc ~line 47 — VULNERABLE
if (op == op::mint) {
;; ← missing: throw_unless(73, equal_slices(sender, admin));
slice to_address = in_msg_body~load_msg_addr();
int amount = in_msg_body~load_coins();
mint_tokens(to_address, amount);
...
}
3.4 Fixed Version
if (op == op::mint) {
throw_unless(73, equal_slices(sender, admin)); ;; ← added
slice to_address = in_msg_body~load_msg_addr();
int amount = in_msg_body~load_coins();
mint_tokens(to_address, amount);
...
}
3.5 TON-specific Considerations
- Bounce messages: does the contract handle
op::excesses / bounced
messages safely? A missing bounce handler can leave funds locked.
- Forward gas: does the contract forward enough gas for sub-messages?
Under-forwarding silently fails without reverting the parent tx.
- Storage fees: contracts with no incoming messages will be frozen and
then deleted. Does the attacker benefit from this?
- Replay: TON has no global nonce — contracts must implement their own
seqno or use the
valid_until trick.
4. Exploitation Steps
For each step, include the message body layout (op + payload):
1. Build a mint message:
op: 0x642b7d07 (op::mint)
to_address: attacker_wallet
amount: 1_000_000_000_000 (1 000 000 jettons, 6 decimals)
2. Send from any wallet with 0.1 TON attached (covers gas + forward).
3. Check attacker's jetton wallet balance — should increase by 1 000 000.
4. Repeat for unlimited supply inflation.
Blueprint test skeleton:
it("mints without admin auth", async () => {
const attacker = await blockchain.treasury("attacker");
await minter.sendMint(attacker.getSender(), {
toAddress: attacker.address,
amount: toNano("1000000"),
value: toNano("0.1"),
});
const balance = await getJettonBalance(attacker.address);
expect(balance).toEqual(toNano("1000000")); // should FAIL if patched
});
5. PoC Results
Required:
- Contract address (
EQ...).
- Transaction hash (lt + hash, or Tonviewer link).
- Message trace (Tonviewer shows the full message tree).
- Jetton balance / TON balance before and after.
Example:
CONTRACT BALANCE BEFORE BALANCE AFTER DELTA
-------- -------------- ------------- -----
JettonMinter 100 000 supply 1 100 000 +1 000 000 minted
Attacker jetton wlt 0 1 000 000 +1 000 000
Exploit TX: https://tonviewer.com/transaction/<hash>
Gas spent: 0.048 TON
5.1 Scaling Economics
Calls Amount per call Total minted Gas cost
----- --------------- ------------ --------
1 1 000 000 1 000 000 0.05 TON
100 1 000 000 100 000 000 5.0 TON
6. Impact Assessment
Severity: CRITICAL
1. Unlimited token inflation — total supply can be minted to any address.
2. No admin role required — any wallet can exploit.
3. Per-message — repeatable indefinitely.
4. DEX liquidity pools backed by this jetton become worthless.
TON-specific severity boosters:
- contract holds significant TON balance (storage drain possible),
- contract is a bridge or DEX vault,
- no upgrade / pause mechanism exists.
7. Recommended Fix
Add sender verification as the first statement in the op::mint branch:
throw_unless(73, equal_slices(sender, storage::admin));
Long-term
Use Tact's @internal access modifier or a dedicated access-control library.
Add Blueprint unit tests that assert every privileged op reverts for non-admin.
Adjacent Audit
Review all other privileged ops in recv_internal:
op::change_admin, op::burn_notification, op::set_content.
Verify bounce handler is implemented for all outgoing messages carrying value.
8. Proof-of-Concept Code
Provide a Blueprint test or a minimal @ton/ton script:
// test/JettonMinterExploit.spec.ts
import { Blockchain, SandboxContract } from "@ton/sandbox";
import { JettonMinter } from "../wrappers/JettonMinter";
import { toNano } from "@ton/ton";
describe("Exploit: unrestricted mint", () => {
let blockchain: Blockchain;
let minter: SandboxContract<JettonMinter>;
beforeEach(async () => {
blockchain = await Blockchain.create();
// deploy with 1 TON initial balance
minter = ...; // standard deploy
});
it("mints 1M tokens without admin", async () => {
const attacker = await blockchain.treasury("attacker");
const result = await minter.sendMint(attacker.getSender(), {
toAddress: attacker.address,
amount: toNano("1000000"),
value: toNano("0.1"),
});
expect(result.transactions).toHaveTransaction({ success: true });
// if this passes — the bug is confirmed
});
});