Simple Crypto 0x11(2023 HW - invalid_curve_attack)
Background
這邊我會嘗試用簡單的講法把這個攻擊簡述一遍,詳細還是建議 Crypton 或是其他地方的說明。
Invalid Curve Attack 大致上來說利用的是當一個不在原本曲線 $E$ 上的 $P$ 進行 scalar multiplication 的一些特性,使用類似 Pohlig–Hellman algorithm 的辦法在不同的 subgroup 解 DLP 然後用 CRT 解回原本的 private key。
一個 Short Weierstrass curve 長這樣:
\[y^2 = x^3 + ax + b\]而它的 point doubling formula ($R=2P$) 是:
\[\begin{aligned} s &= \frac{3x_P^2+a}{2y_P} \\ x_R &= s^2 - 2x_P \\ y_R &= y_P + s(x_R - x_P) \end{aligned}\]由此可見一個 Short Weierstrass curve 在做 scalar multiplication 時並沒有使用到 $b$, 因此對一個 $P \notin E$ 的點做 scalar multiplication 相當於在另一個 $b’ \neq b$ 的 $E’: y^2 = x^3 + ax + b’$ 上運算。
這會帶來的問題是 $E’$ 通常和特別選過的 $E$ 不同,它的 curve order $#(E’)=n$ 分解後不一定都有個 large prime order subgroup 存在。當 $E’$ 上存在一個 order 為 $f$ 的 small subgroup 時,我們可以將原本 $Q=dP$ 的問題轉換成 $(n/f)Q=d((n/f)P)$,然後就能在短時間內解出 $d \bmod{f}$ 的值。
所以只要有多個夠小的 $f_1, f_2, f_3, \cdots$,利用上面的方法找出 $d_i \equiv d \pmod{f_i}$,然後利用 CRT 就能算出 $d \bmod{\prod_{i=1}^{b} f_i}$ 的結果。因此要得到真正的 $d$ 就得找出足夠多的 $f_i$ 使得 $\prod_{i=1}^{b} f_i > n > d$ 才行。
當然,一個 $E’$ 通常不會提供這麼多的 $f_i$ 能達成這個條件,所以會有多個 $E’, E’’, E’’’, \cdots$ 分別提供不同的 $f_i$,然後用一樣的方法在 subgroup 中解 DLP,最後應用 CRT 即可求出需要的 $d$。
這題原先的曲線 $E$ 是 NIST P-256,所以我先將 $a$ 固定,然後暴力搜尋其他不同的 $b’$ 得到 $E’$,把夠小的 $f_i$ 紀錄下來。這部分可以參考 find_curves.sage。
為了減少之後的計算量,我把 $b’$, $E’$ 上的 generator $G’$, $#(E’)$ 還有 $f_i$ 都記錄了下來
剩下就是利用這些預先計算好的參數,將各個 $E’$ 的 $G’$ 當作 public key $P$ 傳給 oracle,然後得到 $Q=dP$,然後用前面的方法得到 $d \equiv d \pmod{f_i}$ 的值,最後使用 CRT 求回 $d$ 即可。
Source code
1 | |
Self-Defined Elliptic Curve
1 | |
Recon
-
觀察source code會發現maple實作了一個沒有檢查我們傳送的點是否在一開始創的橢圓曲線上的elliptiv curve class,然後他把我們給的point當作參數,創立一個初始點,可以看一下下面裡個範例,如果是maple的實作,給予一個根本不在該Elliptic Curve的點他還是會算一個G+G的點給你,只是該點其實是在別的曲線上的2G這個點,反觀正常的sage中的實作會發現只要給予的點不在該曲線上就會直接報錯

maple 實作的Elliptic Curve
1
2
3
4
5
6
7
8
9
10
11
12
13>>> from elliptic_curve_97cadb52fbd7b2cd import Curve, Point >>> p=23 >>> a=5 >>> b=1 >>> E = Curve(p, a, b) >>> G = Point(E, 4, 4) >>> print(G) (4, 4) >>> print(G+G) (19, 3) >>> fake_G = Point(E, 4, 3) >>> print(fake_G+fake_G) (17, 1)正常的Elliptic Curve
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43>>> from sage.all import * >>> p=23 >>> a=5 >>> b=1 >>> E = EllipticCurve(Zmod(p), [a, b]) >>> G = E(4, 4) >>> print(G) (4 : 4 : 1) >>> fake_G = E(4, 3) Traceback (most recent call last): File "sage/structure/category_object.pyx", line 839, in sage.structure.category_object.CategoryObject.getattr_from_category (build/cythonized/sage/structure/category_object.c:7216) KeyError: 'point_homset' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/projective/projective_subscheme.py", line 122, in point return self._point(self.point_homset(), v, check=check) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/elliptic_curves/ell_point.py", line 259, in __init__ point_homset = curve.point_homset() ^^^^^^^^^^^^^^^^^^ File "sage/structure/category_object.pyx", line 833, in sage.structure.category_object.CategoryObject.__getattr__ (build/cythonized/sage/structure/category_object.c:7135) File "sage/structure/category_object.pyx", line 848, in sage.structure.category_object.CategoryObject.getattr_from_category (build/cythonized/sage/structure/category_object.c:7301) File "sage/cpython/getattr.pyx", line 356, in sage.cpython.getattr.getattr_from_other_class (build/cythonized/sage/cpython/getattr.c:2717) AttributeError: 'IntegerModRing_generic_with_category' object has no attribute '__custom_name' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 582, in __call__ return plane_curve.ProjectivePlaneCurve.__call__(self, *args, **kwds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/generic/scheme.py", line 266, in __call__ return self.point(args) ^^^^^^^^^^^^^^^^ File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/projective/projective_subscheme.py", line 124, in point return self._point(self, v, check=check) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/sbk6401/anaconda3/envs/sageenv/lib/python3.11/site-packages/sage/schemes/elliptic_curves/ell_point.py", line 298, in __init__ raise TypeError("Coordinates %s do not define a point on %s" % (list(v), curve)) TypeError: Coordinates [4, 3, 1] do not define a point on Elliptic Curve defined by y^2 = x^3 + 5*x + 1 over Ring of integers modulo 23 - 有了這個性質就可以回去參考一下maple在github上的說明,我們要解決的問題是$hint=Gflag$中的flag到底是甚麼,如果是像前面舉例的那樣($p=23/a=5/b=1/order=31$)很小的order,其實只要直接算
discrete_log(K, G, operation='+')就可以了,範例如下,可以看到我先定義K = E(19, 3),算出discete log=28,事後驗證也證明$K=28G$。但是,像題目中這樣這麼大的order,如果要計算discrete_log的話會非常非常久的時間,總之我先往smooth order的方向思考,也就是說order被factor後其實是由好幾個小的prime所組成,我是直接調整$b$這個不會被Elliptic Curve Multiplication運算使用到的參數(代表其他參數$p, a$要照舊),然後factor曲線的order看夠不夠smooth,但這樣找也一樣要非常非常久,或者說找到的$b$所得到的order都不夠smooth,最大的prime都還是超過$2^{65}$(e.g. 範例如下)1
2
3
4
5
6
7
8>>> G = E.gen(0) >>> print(G) (15 : 1 : 1) >>> K = E(19, 3) >>> discrete_log(K, G, operation='+') 28 >>> 28 * G (19 : 3 : 1)sage: p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff ....: a = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc ....: b = 56 sage: E = EllipticCurve(Zmod(p), [a, b]) sage: factor(E.order()) 3^3 * 13967 * 67679 * 559243 * 11024719 * 127273871 * 1213196727283 * 171447020014729 * 27796463802665410393 sage: 27796463802665410393.bit_length() 65 - 所以我開始朝maple的說明繼續前進,如果有invalid curve的問題就可以考慮用Pohlig–Hellman algorithm的方法求出flag為多少,就如同maple在background中提到的,我們選擇不同的$b$所產生的Elliptic Curve Order被factor後不一定有一個超大prime存在,因此我們就可以把問題簡化($n$就是改變$b$之後取得的Elliptic Curve Order)
- 等我們找到很多個$b$就可以找到很多不同的$flag’$,最後我們再用CRT找出真正的$flag$為何就可以了,也就是
所以重點在於要找到足夠多的$flag’$和$prime_n$組合
Exploit
實作的部分主要是參考1的幫忙,大致上就和上面提到的差不多
1 | |
1 | |
Flag: FLAG{YouAreARealECDLPMaster}