WEBVTT

NOTE
Subtitles for the Anatomy of a Solana program walkthrough. They match the captions
drawn on the video, and there is no spoken narration. The music is an original
score. Cue times follow the film timeline in seconds.

1
00:00:00.400 --> 00:00:04.400
This is a tip vault: about ninety lines of Rust, written with Anchor.

2
00:00:04.600 --> 00:00:08.600
The promise: anyone can tip into a vault, but only its owner can withdraw.

3
00:00:08.800 --> 00:00:13.600
Our job is to find the lines that make that promise true, then break one.

4
00:00:14.400 --> 00:00:19.000
Start with the account model: programs hold code, accounts hold everything else.

5
00:00:19.200 --> 00:00:23.600
The Tip Vault program is stateless. It owns Alice’s, Bob’s and Carol’s vaults.

6
00:00:23.800 --> 00:00:27.800
The System Program owns the wallets, the accounts that hold SOL.

7
00:00:28.000 --> 00:00:31.600
Each vault stores an authority: the person it belongs to.

8
00:00:32.400 --> 00:00:36.400
The runtime enforces exactly one hard rule.

9
00:00:36.600 --> 00:00:40.800
Only an account’s owner program can take lamports out of it.

10
00:00:41.000 --> 00:00:43.700
Everything else is up to the program’s own checks.

11
00:00:44.400 --> 00:00:48.600
Every Solana instruction lists every account it will touch before it runs.

12
00:00:48.800 --> 00:00:52.800
That up-front list is why Solana is fast: transactions touching different accounts run in parallel.

13
00:00:53.000 --> 00:00:56.600
But it also means the attacker writes the list.

14
00:00:56.800 --> 00:00:59.700
A program that trusts an account just because it showed up has already lost.

15
00:01:00.400 --> 00:01:04.000
So every program has to ask three questions about every account.

16
00:01:04.200 --> 00:01:07.600
One: did this account sign? That is the Signer type.

17
00:01:07.800 --> 00:01:11.400
Two: is it really ours, and the right type? That is Account of Vault.

18
00:01:11.600 --> 00:01:15.400
Three: do these accounts belong together? That is has_one and seeds.

19
00:01:16.400 --> 00:01:20.200
Watch for how Solana works differently: account lists declared up front, PDAs, and rent.

20
00:01:20.400 --> 00:01:24.200
And watch the security checks: owner and type, the Signer, has_one and seeds.

21
00:01:24.400 --> 00:01:27.600
Plus checked math and the rent floor.

22
00:01:28.400 --> 00:01:33.400
Before we run it, make a prediction: if we delete the constraints from Withdraw, who can take the money?

23
00:01:33.600 --> 00:01:37.600
Write down your answer. We will check it in the demo.

24
00:01:38.400 --> 00:01:42.000
This is the real demo page. We will click through it.

25
00:01:42.200 --> 00:01:46.000
Choose the program version, who signs, and which vault to pass in.

26
00:01:46.200 --> 00:01:50.400
The transaction updates. Every line was chosen by the sender.

27
00:01:50.600 --> 00:01:54.200
Now send it to the vulnerable program.

28
00:01:54.400 --> 00:01:57.200
The signer, owner and type checks all pass.

29
00:01:57.400 --> 00:02:00.600
Then the missing check: nothing compares the vault’s authority to the signer.

30
00:02:00.800 --> 00:02:04.600
Five SOL leave Alice’s vault and land in Mallory’s wallet.

31
00:02:04.800 --> 00:02:09.000
Every check that ran was correct. The bug is the check that never ran.

32
00:02:09.400 --> 00:02:13.400
Now switch to the fixed program and send the same transaction.

33
00:02:14.000 --> 00:02:18.200
The first three checks still pass, but now the relationship check runs.

34
00:02:18.600 --> 00:02:23.000
It fails with ConstraintSeeds: the vault is not derived from Mallory’s key. No lamports move.

35
00:02:23.400 --> 00:02:27.800
Presets set up other cases. First, pass a wallet as the vault.

36
00:02:28.400 --> 00:02:32.000
The owner check rejects it: the wallet belongs to the System Program.

37
00:02:32.400 --> 00:02:36.000
Next, try withdrawing more than the vault can spare.

38
00:02:36.400 --> 00:02:41.000
The rent floor stops it: a vault cannot drop below its rent-exempt minimum.

39
00:02:41.400 --> 00:02:45.600
Same page, same transaction. The only difference is the check that runs.

40
00:02:46.000 --> 00:02:49.600
Now, back to our prediction.

41
00:02:50.400 --> 00:02:54.600
So, the prediction: with those constraints deleted, who can take the money?

42
00:02:54.800 --> 00:02:59.600
Anyone who can sign. Any signer can take from any genuine vault.

43
00:03:00.400 --> 00:03:03.800
Here is the attack again, step by step.

44
00:03:04.000 --> 00:03:08.000
She signs with her own key. She passes in Alice’s vault. The type tag matches.

45
00:03:08.200 --> 00:03:11.800
All three checks pass. Then nothing compares the vault’s authority to the signer.

46
00:03:12.000 --> 00:03:15.600
Every check that ran was correct. The bug is the check that never ran.

47
00:03:16.400 --> 00:03:20.200
The fix is two constraints. On the left, the vulnerable Withdraw struct.

48
00:03:20.400 --> 00:03:23.800
It checks the signer and the type, but never relates the two.

49
00:03:24.000 --> 00:03:27.600
On the right, has_one requires the vault’s stored authority to equal the signer.

50
00:03:27.800 --> 00:03:30.800
Seeds require the vault’s address to be derived from the signer’s key.

51
00:03:31.000 --> 00:03:33.800
Either one closes the hole. Using both is defense in depth.

52
00:03:34.400 --> 00:03:38.400
Now, why Solana is different from Ethereum.

53
00:03:38.600 --> 00:03:42.200
On the EVM, state lives inside the contract. On Solana, it lives in separate accounts the program owns.

54
00:03:42.400 --> 00:03:45.800
The EVM has one implicit msg.sender. On Solana, signers are explicit accounts, possibly several.

55
00:03:46.000 --> 00:03:49.600
The EVM finds what a transaction touches as it runs. Solana declares it up front.

56
00:03:50.400 --> 00:03:54.000
Storage is paid for differently: gas per write on the EVM, a refundable rent-exempt deposit on Solana.

57
00:03:54.200 --> 00:03:58.200
Contracts are immutable unless you add a proxy. Solana programs are upgradeable by default.

58
00:03:58.400 --> 00:04:01.800
Reentrancy is a classic EVM attack class. On Solana it is only direct self-recursion, with capped call depth.

59
00:04:02.000 --> 00:04:05.600
So the typical Solana bug is different: missing signer, owner or relationship checks.

60
00:04:06.400 --> 00:04:09.600
Here is the checklist for reading any Solana program.

61
00:04:09.800 --> 00:04:13.400
Is every authority a Signer? Is every data account checked for owner and type?

62
00:04:13.600 --> 00:04:17.000
Is each account tied to the others with has_one or seeds? Are PDAs derived with the canonical bump?

63
00:04:17.200 --> 00:04:21.400
Is the target of every cross-program call pinned? Is all arithmetic checked?

64
00:04:21.600 --> 00:04:25.000
Can a closed account be revived or reinitialized? And who holds the upgrade authority?

65
00:04:25.200 --> 00:04:27.600
Ask all eight, every time.

66
00:04:28.400 --> 00:04:32.000
Two questions to discuss. Take a moment with each.

67
00:04:32.200 --> 00:04:37.800
Pick a Solana program you’ve used. Who holds its upgrade authority, and what could they change tomorrow?

68
00:04:38.000 --> 00:04:44.600
Mallory needed no exploit code, only a crafted account list. What does that mean for how we test these programs?

69
00:04:46.400 --> 00:04:50.400
That is the whole lesson: read the program the way an attacker does.

70
00:04:50.600 --> 00:04:55.000
Every check that ran was correct. The bug is the check that never ran.

71
00:04:55.200 --> 00:04:59.400
Next time you read a program, look for the check that is not there.

