---
# System prepended metadata

title: CTF CHILL
tags: [CTF TIME]

---

# CTF CHILL

> Nhan_laptop |
> --


> Một số chall mình thấy hay 
> --- 



## Flagtrix - HCMUS-CTF2025

chall: 
```python!
import string 
from sage.all import * 
from hashlib import sha256 
from Crypto.Util.number import getPrime, bytes_to_long as btl 

FLAG = b'HCMUS-CTF{surely_this_is_the_real_flag_xdd_136825d2dc8a9658e}'

assert len(FLAG) == 61 
assert sha256(FLAG).hexdigest() == '136825d2dc8a9658e7e41d9c9a9180dc7eeed802b7801b9836f9d012c4986f7e'
assert all(x in (string.ascii_letters + string.digits + '_').encode() for x in FLAG.lstrip(b'HCMUS-CTF{')[:-1])

p = getPrime(1024)
q = getPrime(1024)
n = p * q 

M = Matrix(Zmod(n), [
            [btl(FLAG[:len(FLAG) // 4]), btl(FLAG[len(FLAG) // 4:2 * len(FLAG) // 4]) ], 
            [btl(FLAG[2 * len(FLAG) // 4:3*len(FLAG) // 4]), btl(FLAG[3 * len(FLAG) // 4:])]
])

print((M ** 137).list())
print(n)
```

Bài này thì mình symbolic lên và xem trạng thái biến đổi. 
Chú ý ở đây: M * C  = C * M   vì M ^ 137 * M = M * M ^ 137, mình sẽ sử dụng phương trình này để giải bằng `groebner_basis`.

solve:
```python!

import string 
from hashlib import sha256 
from Crypto.Util.number import *
from sage.all import * 
c = '...'
ci = c
n = 25093610144908461464059057408568647065852509496910506454219029231729615790182838521750666866220278951668213036781863233070073888264737231955166698931256387186706709967683366405223674886514417461424423811600936935913459851157466271803054923435838872834203098196847763934619584820142686420544929795531754633813915442819237995875546815902669291787704729075851900787370032144290248373787526232788049121242923165373815660903262194195632414516883985594279799045995517317604084920418118272895521418338434234634526455272161460427621237242918538081864414570820209101902145835308168984602150410855452852774414322666120594785481
e = 137
k = 2
c = Matrix(Zmod(n),k,k,c)
FLAG = b'HCMUS-CTF{surely_this_is_the_real_flag_xdd_136825d2dc8a9658e}'

assert len(FLAG) == 61 
##################Part1############################### 


Pring,flag =  PolynomialRing(Zmod(n), 'k',4 ).objgens()

FLAG = [i for i in flag]

M  = Matrix(2,2,FLAG)
T = M * c - c* M 

sol = ideal([T[0][0],T[0][1],T[1][0],T[1][1] ]).groebner_basis()
"""
sol = [k0 + a*k2 + b*k3, k1 + c*k2]
Vì phương trình 2 có thể giải bằng Lattice nên ta sẽ giải trước!
"""
mat1, v1 = Sequence([sol[1]]).coefficients_monomials(sparse=False)

mat1 = mat1.T.change_ring(ZZ)

mat1 = mat1.augment(matrix.identity(2)).stack(vector(ZZ, [n, 0, 0]))
W = Matrix.diagonal(ZZ, [2 ** 256, 1, 1])

lll = (mat1 * W).LLL() / W 

mat = lll[0]
Flag1 = mat[1]
Flag2 = mat[2]
Alphabet = string.ascii_letters + string.digits + '_' 
Alphabet = Alphabet.encode()
for k in range(1,100):
    f1 = Flag1 * (-k)    
    f2 = Flag2 * (-k)

    if all([i in Alphabet for i in long_to_bytes(int(f1))]) and all([i in Alphabet for i in long_to_bytes(int(f2))]):
        break

# print(long_to_bytes(int(f1)),long_to_bytes(int(f2)))


##################Part2############################### 
FLAG = [i for i in flag]
FLAG = matrix(2,2,[FLAG[0]] + [Zmod(n)(f1),Zmod(n)(f2)] + [FLAG[-1]])

C  = matrix(Zmod(n),2,2,ci)
CI = FLAG**137

tmp = CI - C 

sol = ideal(tmp.list()).groebner_basis()

print(sol)

tmp = [Zmod(n)(-i.constant_coefficient()) for i in sol]
f0, f3 = [long_to_bytes(int(i)) for i in tmp]
print(f0 +long_to_bytes(int(f1)) + long_to_bytes(int(f2))+ f3 )
```

## TFCCTF2025 - DEEZERRORS

chall:
```python!
from Crypto.Util.number import long_to_bytes, bytes_to_long
import random
from sage.all import * 
from secret import flag

mod = 0x225fd
flag = bytes_to_long(flag)
e_values = [97491, 14061, 55776]
S = (lambda f=[flag], sk=[]: ([sk.append(f[0] % mod) or f.__setitem__(0, f[0] // mod) for _ in iter(lambda: f[0], 0)],sk)[1])()
S = vector(GF(mod), S)

A_save = []
b_save = []

for i in range(52):
    A = VectorSpace(GF(mod), 44).random_element()
    e = random.choice(e_values)
    b = A * S + e
    #print(b)

    A_save.append(A)
    b_save.append(b)

open('out.txt', 'w').write('A_values = ' + str(A_save) + ' ; b_values = ' + str(b_save))
```
Hmm tóm lại là ta sẽ có được 3 error và rất lớn. 
Nhận xét: 
- `[97491, 14061, 55776]` là dãy tăng dần đều ( sắp lại ) và `97491 - 55776 = 55776 - 14061 != 0 mod p `.
- Cái hàm người ta cho: `S = (lambda f=[flag], sk=[]: ([sk.append(f[0] % mod) or f.__setitem__(0, f[0] // mod) for _ in iter(lambda: f[0], 0)],sk)[1])()` - thật chất chỉ là băm nhỏ theo mod -> tạo vector : S . 

Solve: 
Mình biến đổi như sau: 

\begin{align*}
a = 14061, \quad b = 55776, \quad c = 97591; \quad t = b; \quad \Delta = b - a 
\end{align*}

\begin{align*}
\text{Ta có:} \quad a = t - \Delta,\quad b = t,\quad c = t + \Delta \
\end{align*}
Ta sẽ đưa về dạng `{-1,0,1}` tiêu chuẩn như sau: 
$$
e \in \{t-\Delta,\ t,\ t+\Delta\}
$$

$$
\Rightarrow e' \in \{-1,0,1\} = (e - t ) * delta ^{-1}
$$

$$
\Rightarrow A*s + b = e \mod{p} \Leftrightarrow A*s *  delta^{-1}  - (b_{vector} + t) *  delta ^ {-1}  =  (e - t)  *  delta ^ {-1} \mod{p}
$$
Thì lattice như này: 


$$
\begin{aligned}
& s_0 \cdot
\begin{bmatrix}
A_{0,0} \\ A_{1,0} \\ \vdots \\ A_{m,0}
\end{bmatrix} \cdot delta^{-1}
\;+\;
s_1 \cdot
\begin{bmatrix}
A_{0,1} \\ A_{1,1} \\ \vdots \\ A_{m,1}
\end{bmatrix} \cdot delta^{-1}
\;+\;\cdots +\;
s_n \cdot
\begin{bmatrix}
A_{0,n} \\ A_{1,n} \\ \vdots \\ A_{m,n}
\end{bmatrix}\cdot delta^{-1} -\,1 \cdot
\begin{bmatrix}
b_0 + t  \\ b_1 + t \\ \vdots \\ b_m + t 
\end{bmatrix} \cdot delta^{-1} +\,
\begin{bmatrix}
1_0   \\ 1_1  \\ \vdots \\ 1_m  
\end{bmatrix} 
\;+\;
k_0 \cdot
\begin{bmatrix}
p \\ 0 \\ \vdots \\ 0
\end{bmatrix} 
\;+\;
k_1 \cdot
\begin{bmatrix}
0 \\ p \\ \vdots \\ 0
\end{bmatrix}
\;+\;\cdots +\;
k_m \cdot
\begin{bmatrix}
0 \\ 0 \\ \vdots \\ p
\end{bmatrix} 
 = \begin{bmatrix}
e_0' \\ e_1' \\ \vdots \\ e_m'
\end{bmatrix}
\end{aligned}
$$

Chú ý: trong lattice này mình phải thêm vector toàn `1` vào vì vector ngắn nhất của chúng ta là có số -1 nên lúc tính sẽ bị mũ lên và trở thành dương -> típ là  ta phải `+1`.

```python!

from Crypto.Util.number import *
import random
from sage.all import * 
import json 
from lll_cvp import reduce_mod_p
from out import A_values as A , b_values as b


e = [97491, 14061, 55776]
es = sorted(e)
Ai = A
bii = b 
a,bi,c = es 
p = 0x225fd
K = GF(p)
m = 52
n = 44 

delta = bi -a 
inv_delta = int(inverse(delta,p))
A = matrix(ZZ,m,n,A) * inv_delta
b = (vector(ZZ,b) + vector(ZZ,[bi]*len(b)) ) * inv_delta  

mat = A.augment(b).augment(vector(ZZ,m*[1])).augment(identity_matrix(ZZ,m)*p).T

matlll = mat.BKZ(block_size = 20)

for rr in matlll:
    if (all([i == 1 or i == 0 or i == -1 for i in rr]) and 
            all([i == 1  for i in rr]) != 1 and 
                all([i == 0  for i in rr]) != 1 ) :
                    
                    print(rr)
                    break

mat = mat.change_ring(K)
sol = mat.solve_left(rr)

print(sol)
s  = sol[:n]
s   = s.list()

def revert_from_sk(sk, mod):
    res = 0
    for d in reversed(sk):
        res = res * mod + int(d)
    return res
print(long_to_bytes(int(revert_from_sk(s,p))).decode())

"""
As *  delta ^ -1  - (b + t) *  delta ^ -1  =  (e - t)  *  delta ^ -1   
t - e = -1   , t  = 0 , t + e = 1  
"""
```


## ImaginaryCTF 2025 - scalar division: 

### Unintended
chall:
```python!
assert ((E:=EllipticCurve(GF(0xbde3c425157a83cbe69cee172d27e2ef9c1bd754ff052d4e7e6a26074efcea673eab9438dc45e0786c4ea54a89f9079ddb21),[5,7])).order().factor(limit=2**10)[3][0]*E.lift_x(ZZ(int.from_bytes((flag:=input('ictf{')).encode())))).x() == 0x686be42f9c3f431296a928c288145a847364bb259c9f5738270d48a7fba035377cc23b27f69d6ae0fad76d745fab25d504d5 and not print('\033[53C\033[1A}')
```
Ta có: 
 - `Q  = r * P ` với: r là prime thứ 3 của factor(order)  và P là phần tử flag. 
 - Biết được curve gốc, `Q,r`.

Solve:

Ban đầu mình nghĩ là chỉ cần dịch ngược  `r` lại rồi nhân cho `Q` là ra. Nhưng vì `r | order` nên thất bại. 
Nên ý tưởng là ta sẽ giải bài toán con trên các phần tử prime còn lại - tương tự bài toán Pohlig Hellman.
Vì N là tích các thừa số nguyên tố thì theo định lí phân rã nhóm abelian hữu hạn, ta có phân rã trực giao:![image](https://hackmd.io/_uploads/BJbd35a5ee.png)
Trong đó $G_i$ là Sylow qi- component (các phần tử có phần bậc lũy thừa của thừa số nguyên tố). Mỗi phần tử $R\in E(F_p)$ được viết dưới dạng: $P = ∑_i*P_i$ với $G_i$.
Sau cùng chỉ là bước dịch ngược $r$ trên từng nhóm con. 
Và cuối cùng ta tính tổng các giá trị đó lại: ![image](https://hackmd.io/_uploads/rkoUJjaqel.png)
Vì P mới chỉ là tổng của các phân rã - không có r-subgroup nên ta cần tính lại: lấy random một point rồi nhân với `N//r` tạo point thuộc nhóm `E[r]`, và cuối cùng là sẽ là tìm toàn bộ điểm trên nhóm. 

solve.py:
```python!
# preimages_of_scalar_fixed.sage
from sage.all import *
from Crypto.Util.number import *
p = 0xbde3c425157a83cbe69cee172d27e2ef9c1bd754ff052d4e7e6a26074efcea673eab9438dc45e0786c4ea54a89f9079ddb21
a = 5
b = 7
target_x = 0x686be42f9c3f431296a928c288145a847364bb259c9f5738270d48a7fba035377cc23b27f69d6ae0fad76d745fab25d504d5
order_ = 1915401112669764832155688444967632063685280552714174698559105795993909088154715053733286568457561836692127326936769038088
r = 457

F = GF(p)
E = EllipticCurve(F, [a, b])

x = E.lift_x(F(target_x))
assert x * (order_//r) == E(0)
list_x = []
list_x.append(x)
list_x.append(-x)


res = []
pr = order_ // r 
tmp = order_ // pr 
q = (x)*inverse(r,pr)
T = E.random_element() * (order_ // r)
import string
for i in range(r):
    P  = q  + i * T

    x = P.xy()[0]
    if  all([i in (string.ascii_letters + string.digits + '_').encode() for i in long_to_bytes(int(x))]):
            print(long_to_bytes(int(x)))

```

### Intended 
Một cách giải khác. 
Vì ta biết được giá trị flag là tọa độ `x - P.x`, và hơn hết là giá trị `a - a * P = Q` là nhỏ.
Solve:
Nhìn vào công thức cộng trên Elliptic: 
![image](https://hackmd.io/_uploads/S1mymgksll.png)
Hình 1: https://giapppp.notion.site/Elliptic-Curve-Cryptography-50d4401770b641349ad235e287b326d0
để ý rằng vì ta chỉ cộng các tọa độ giống nhau nên $\lambda$ sẽ bằng phần sau, mà $\lambda = y^2 = x^3 + a*x + b$ vậy thì $(P*r).x$ chỉ là đa thức 1 biến. 
Rồi bây giờ ta symbolic lên rồi giải. 
Vì đa thức bậc rất lớn - giải tốn thời gian, và hơn nữa là ta chỉ cần tìm 1 nghiệm duy nhất `x = flag`. 
Để ý thấy - fermat nhỏ : 

$$
a ^{p} ≡ a \mod p
$$

Biểu thức này luôn cho ta một nghiệm `a`.
Vì có 2 biểu thức nên ta sẽ dùng `GCD` để tìm lại nghiệm: 
chi tiết cách tấn công: https://adib.au/2025/lance-hard/#speedup-by-using-gcd

```python!
from Crypto.Util.number import *
p = 0xbde3c425157a83cgiá trị be69cee172d27e2ef9c1bd754ff052d4e7e6a26074efcea673eab9438dc45e0786c4ea54a89f9079ddb21
a = 5
b = 7
target_x = 0x686be42f9c3f431296a928c288145a847364bb259c9f5738270d48a7fba035377cc23b27f69d6ae0fad76d745fab25d504d5
order_ = 1915401112669764832155688444967632063685280552714174698559105795993909088154715053733286568457561836692127326936769038088
r = 457
F = GF(p)
E = EllipticCurve(F, [a, b])

f = E.multiplication_by_m(r,1)
f = f.numerator() - target_x * f.denominator()

R = PolynomialRing(F,'x')
r = R.gen()
f = R(f)
g = pow(r, p, f) - r

tmp = f.gcd(g).roots()
for i in tmp:
    x = i[0]
    print(long_to_bytes(int(x)))

"""
b"\xbd\xa7\xdbR\xa1\x89\x1a,UFs\x0f\xfeW\xe7\x94\x9f\xc9z\xc1U|xm\xa4qj\xeb\xc7~\xd3\xcd['\xbeb\x1cj\xe9W\xaa$\x18=\r|vl\xc0Q"
...
b"o\x01\x1cG\xa3\x07\xe4\x1a\xed\xf8\xcd\xd0Qt\xe2\xd0\xc8]B\x83R\xb1\xc5\xbe\x93\xd7\xab\x9c\xd5#gU9\n\xaa\xd4\x88\xd10\x89t\xb5'\x06\x87v\x0b0H"
b'7\x9fQB\xb5k^\xc5\x1e\xdas\xa3;\x08(\x07\xf7\xe9\x1b\xacte\xe39\x1e\x8cH\xf8\x88f\xa9\xd9\xc6\xb3\x08!M\xf1\xa7\xe1\x97@\x8d\xf9\xe8\x06\xf8\xbc\xaa'
b"\x0e\x9e\xde\x9a\x1c\x98\xd0\xd6L}\xd0\x8c\xe6o\xfd'\xd2_H8z\xa6y\x96\xe2\xdf\xbbn\xc8nF\t\xf2?\x8a\xd8+_\xb3\xee>1\xe5h\xb0\xc1\x84\xb5\x04"
b'mayb3_d0nt_m4ke_th3_sca1ar_a_f4ctor_0f_the_ord3r'
"""
```


