Skip to content

Commit 23d4940

Browse files
committed
Add Codegate CTF 2024 Final
1 parent 25c4311 commit 23d4940

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+5947
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# my-ctf-challenges
22

3+
## [Codegate CTF 2024 Finals](https://ctftime.org/event/2347/)
4+
- [Quantitative-Easing](codegate2024/Final/Quantitative-Easing) (Blockchain, Crypto)
5+
36
## [WACon 2023 Finals](https://wacon.world/)
47

58
- [Blob Me Maybe](https://github.com/pcw109550/blob-me-maybe) (Blockchain, Crypto)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Crypto - Quantitative Easing
2+
3+
### Author
4+
- Park Changwan (@diff72840089)
5+
6+
### Infra
7+
8+
```sh
9+
cd prob/for_organizer
10+
# setup port by tweaking docker compose
11+
docker compose up --build -d
12+
```
13+
14+
Connect with
15+
16+
```sh
17+
nc 54.180.139.83 13337
18+
```
19+
20+
### Description
21+
22+
```
23+
I rolled my own b{lockchain|ank}. Please stimulate our economy!
24+
```
25+
26+
### Writeup
27+
28+
Keywords: Zero Knowledge, Range Proofs, Weak Fiat-Shamir Transformation, Mimblewimble Transaction
29+
30+
The challenge implements basic blockchain with mimblewimble protocol enabled. Mimblewimble transactions gives privacy, by leveraging pederson commitments, schnorr signatures, and range proofs(or bulletproofs) scheme. Range proofs are zk scheme, which requires Fiat-Shamir transformation for non-interactive protocols.
31+
32+
- [1] BulletProof paper: https://eprint.iacr.org/2017/1066.pdf
33+
- [2] Mimblewimble transactions: https://tlu.tarilabs.com/protocols/mimblewimble-transactions-explained
34+
35+
When weak Fiat-Shamir transformation is used for range proofs, the attacker may forge range proofs and the scheme becomes provably insecure. Article [4] amounts the actual attack on range proofs(not aggregated).
36+
37+
- [3] Weak Fiat-Shamir Attacks on
38+
Modern Proof Systems: Section 4: https://eprint.iacr.org/2023/691.pdf
39+
- [4] Frozen Heart Vulnerability in bulletproofs: https://blog.trailofbits.com/2022/04/15/the-frozen-heart-vulnerability-in-bulletproofs/
40+
41+
The challenge is implemented based on open source range proof implementations that is vulnerable to weak Fiat-Shamir transformation.
42+
43+
- [5] Range proof implementation: https://github.com/wborgeaud/python-bulletproofs
44+
45+
By mounting the attack introduced at [3], we may forge aggregated range proofs, eventually forging `tx_fees` to random value sampled at `GF(secp256k1.order)`.
46+
47+
The main implementation of the attack is at `exploit/src/solve.py`'s `Agent::request_with_forgery` method implementation, which uses `exploit/src/rangeproofs/rangeproof_aggregate_prover_forgery.py`. You may compare the attack code with original `Agent::request` implementation.
48+
49+
Run solver script by
50+
51+
```sh
52+
cd exploit/src
53+
python3 -m pip install requirements.txt
54+
python3 solve.py
55+
```
56+
57+
Example stdout:
58+
```log
59+
[<] Opening connection to localhost on port 13337: Trying 127.0.0.
60+
[+] Opening connection to localhost on port 13337: Done
61+
[*] PoW token = s.AAfQ.AACYZXwYSfPurIbV2NIn5pfw
62+
[*] PoW solution = s.AABRUfkKJVF9ume9LRFRTpRISn17UVySLaiRYfb3qHIuP17D7C8mfVEkeUF0ua6NM/uNU055KGNYBSZj7sQtx46IhvOtMibNyr1fBePF0pD7bYqcznHR4AzomzYOVAsvO0St91aFSSIwvUtY6IA4YxJGYPyR8EkJctgthXgJ7C591PCf4SJWBsjpmsu1ZRS2IR97G9Q8IOnwKX/3iDgoyvHX
63+
[*] PoW validation = 'Correct'
64+
[*] Received Protocol Parameters
65+
[*] Received transaction
66+
[*] Received spending key
67+
[*] Sent transaction
68+
[*] Check flag
69+
{'flag': 'CODEGATE{Times_29/Aug/2024_Chancellor_on_brink_of_third_bailout_for_banks}'}
70+
CODEGATE{Times_29/Aug/2024_Chancellor_on_brink_of_third_bailout_for_banks}
71+
[*] Switching to interactive mode
72+
[*] Got EOF while reading in interactive
73+
```
74+
75+
### Flag
76+
77+
```
78+
CODEGATE{Times_29/Aug/2024_Chancellor_on_brink_of_third_bailout_for_banks}
79+
```
80+
81+
## Stats
82+
83+
- General Division: 2 solves (DiceGang, Oops)
84+
85+
### External Writeups
86+
87+
- 0ops: https://github.com/hch257/CTF-Writeups/tree/main/2024-Codegate/Quantitative%20Easing
88+
- Unintended using Arithmetic overflow attack

codegate2024/Final/Quantitative-Easing/exploit/src/innerproduct/__init__.py

Whitespace-only changes.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
"""Contains classes for the prover of an inner-product argument"""
2+
3+
from typing import Optional
4+
5+
from utils.commitments import vector_commitment
6+
from utils.transcript import Transcript
7+
from utils.utils import inner_product
8+
9+
from .inner_product_verifier import Proof1, Proof2
10+
11+
12+
class NIProver:
13+
"""Class simulating a NI prover for the inner-product argument (Protocol 1)"""
14+
15+
def __init__(self, g, h, u, P, c, a, b, group, seed=b""):
16+
assert len(g) == len(h) == len(a) == len(b)
17+
self.g = g
18+
self.h = h
19+
self.u = u
20+
self.P = P
21+
self.c = c
22+
self.a = a
23+
self.b = b
24+
self.group = group
25+
self.transcript = Transcript(seed)
26+
27+
def prove(self) -> Proof1:
28+
"""
29+
Proves the inner-product argument following Protocol 1 in the paper
30+
Returns a Proof1 object.
31+
"""
32+
# x = mod_hash(self.transcript.digest, self.group.order)
33+
x = self.transcript.get_modp(self.group.q)
34+
self.transcript.add_number(x)
35+
P_new = self.P + (x * self.c) * self.u
36+
u_new = x * self.u
37+
Prov2 = FastNIProver2(
38+
self.g,
39+
self.h,
40+
u_new,
41+
P_new,
42+
self.a,
43+
self.b,
44+
self.group,
45+
self.transcript.digest,
46+
)
47+
return Proof1(u_new, P_new, Prov2.prove(), self.transcript.digest)
48+
49+
50+
class FastNIProver2:
51+
"""Class simulating a NI prover for the inner-product argument (Protocol 2)"""
52+
53+
def __init__(self, g, h, u, P, a, b, group, transcript: Optional[bytes] = None):
54+
assert len(g) == len(h) == len(a) == len(b)
55+
assert len(a) & (len(a) - 1) == 0
56+
self.log_n = len(a).bit_length() - 1
57+
self.n = len(a)
58+
self.g = g
59+
self.h = h
60+
self.u = u
61+
self.P = P
62+
self.a = a
63+
self.b = b
64+
self.group = group
65+
self.transcript = Transcript()
66+
if transcript:
67+
self.transcript.digest += transcript
68+
self.init_transcript_length = len(transcript.split(b"&"))
69+
else:
70+
self.init_transcript_length = 1
71+
72+
def prove(self):
73+
"""
74+
Proves the inner-product argument following Protocol 2 in the paper
75+
Returns a Proof2 object.
76+
"""
77+
gp = self.g
78+
hp = self.h
79+
ap = self.a
80+
bp = self.b
81+
82+
xs = []
83+
Ls = []
84+
Rs = []
85+
86+
while True:
87+
if len(ap) == len(bp) == len(gp) == len(hp) == 1:
88+
return Proof2(
89+
ap[0],
90+
bp[0],
91+
xs,
92+
Ls,
93+
Rs,
94+
self.transcript.digest,
95+
self.init_transcript_length,
96+
)
97+
np = len(ap) // 2
98+
cl = inner_product(ap[:np], bp[np:])
99+
cr = inner_product(ap[np:], bp[:np])
100+
L = vector_commitment(gp[np:], hp[:np], ap[:np], bp[np:]) + cl * self.u
101+
R = vector_commitment(gp[:np], hp[np:], ap[np:], bp[:np]) + cr * self.u
102+
Ls.append(L)
103+
Rs.append(R)
104+
self.transcript.add_list_points([L, R])
105+
# x = mod_hash(self.transcript.digest, self.group.order)
106+
x = self.transcript.get_modp(self.group.q)
107+
xs.append(x)
108+
self.transcript.add_number(x)
109+
gp = [x.inv() * gi_fh + x * gi_sh for gi_fh, gi_sh in zip(gp[:np], gp[np:])]
110+
hp = [x * hi_fh + x.inv() * hi_sh for hi_fh, hi_sh in zip(hp[:np], hp[np:])]
111+
ap = [x * ai_fh + x.inv() * ai_sh for ai_fh, ai_sh in zip(ap[:np], ap[np:])]
112+
bp = [x.inv() * bi_fh + x * bi_sh for bi_fh, bi_sh in zip(bp[:np], bp[np:])]

0 commit comments

Comments
 (0)