---
# System prepended metadata

title: TJCTF 2024

---

![image](https://hackmd.io/_uploads/SkW-Jmd7C.png)
# Write up for Cryptography challenges
* Ở giải này mình giải được **5/12** chall trong thời gian diễn ra giải.

## 1. weird-crypto
![image](https://hackmd.io/_uploads/Sygn17OXA.png)

**Attachment:** [`a.py`](https://tjctf-2024.storage.googleapis.com/uploads/f52edecdb1b0e1a6909e1f5feee2116c90ca808845f5374c329466ea02176b23/a.py)   [`output.txt`](https://tjctf-2024.storage.googleapis.com/uploads/3889f951209e0a4786057e426af27d3d6379641fda4379fa63b2ad388e4036c2/output.txt)

---
```python=
from math import lcm
from Crypto.Util.number import bytes_to_long, getPrime

with open('flag.txt', 'rb') as f:
    flag = bytes_to_long(f.read().strip())

oops = getPrime(20)
p1 = getPrime(512)
p2 = getPrime(512)

haha = (p1-1)*(p2-1)
crazy_number = pow(oops, -1, haha)
discord_mod = p1 * p2
hehe_secret = pow(flag, crazy_number, discord_mod)

#admin = 115527789319991047725489235818351464993028412126352156293595566838475726455437233607597045733180526729630017323042204168151655259688176759042620103271351321127634573342826484117943690874998234854277777879701926505719709998116539185109829000375668558097546635835117245793477957255328281531908482325475746699343
#hehe_secret = 10313360406806945962061388121732889879091144213622952631652830033549291457030908324247366447011281314834409468891636010186191788524395655522444948812334378330639344393086914411546459948482739784715070573110933928620269265241132766601148217497662982624793148613258672770168115838494270549212058890534015048102
#crazy number = 13961211722558497461053729553295150730315735881906397707707726108341912436868560366671282172656669633051752478713856363392549457910240506816698590171533093796488195641999706024628359906449130009380765013072711649857727561073714362762834741590645780746758372687127351218867865135874062716318840013648817769047

```

* Bài này khá đơn giải ta chỉ cần brute-force `oops`.

**script:**
```python=
from Crypto.Util.number import *
from sympy import nextprime

admin = 115527789319991047725489235818351464993028412126352156293595566838475726455437233607597045733180526729630017323042204168151655259688176759042620103271351321127634573342826484117943690874998234854277777879701926505719709998116539185109829000375668558097546635835117245793477957255328281531908482325475746699343
hehe_secret = 10313360406806945962061388121732889879091144213622952631652830033549291457030908324247366447011281314834409468891636010186191788524395655522444948812334378330639344393086914411546459948482739784715070573110933928620269265241132766601148217497662982624793148613258672770168115838494270549212058890534015048102
crazy_number = 13961211722558497461053729553295150730315735881906397707707726108341912436868560366671282172656669633051752478713856363392549457910240506816698590171533093796488195641999706024628359906449130009380765013072711649857727561073714362762834741590645780746758372687127351218867865135874062716318840013648817769047
i = 2**19
while 1:
    try:
        flag = pow(hehe_secret, i, admin)
        if b'tjctf{' in long_to_bytes(flag):
            print(long_to_bytes(flag))
            break
        i = nextprime(i)
    except:
        continue
```

> *Flag: tjctf{congrats_on_rsa_e_djfkel2349!}*

## 2. accountleak
![image](https://hackmd.io/_uploads/ByfOb7_mA.png)
* Attachment: [`server.py`](https://tjctf-2024.storage.googleapis.com/uploads/e31a1ad02ba2db75ee6a9eb99d1a38b11e4259b4a14d8fa5f0a7212fdf85f5c4/server.py)
* `nc tjc.tf 31601`

```python=
#!/usr/local/bin/python3.10 -u

import time
from Crypto.Util.number import getPrime, getRandomInteger, getRandomNBitInteger

flag = open("flag.txt").read().strip()
p = getPrime(512)
q = getPrime(512)

sub = getRandomInteger(20)

# hehe u cant guess it since its random :P
my_password = getRandomNBitInteger(256)

n = p*q
c = pow(my_password, 65537, n)
dont_leak_this = (p-sub)*(q-sub)

def gamechat():
    print("<Bobby> i have an uncrackable password maybe")
    print(f"<Bobby> i'll give you the powerful numbers, {c} and {n}")
    print("<Bobby> gl hacking into my account")
    print("<Bobby> btw do you want to get my diamond stash")
    resp = input("<You> ")
    if (resp.strip() == "yea"):
        print("<Bobby> i'll send coords")
        print(f"<Bobby> {dont_leak_this}")
        print("<Bobby> oop wasnt supposed to copypaste that")
        print("<Bobby> you cant crack my account tho >:)")
        tic = time.time()
        resp = input("<You> ")
        toc = time.time()
        if (toc-tic >= 2.5):
            print("<Bobby> you know I can reset my password faster than that lol")
        elif (resp.strip() != str(my_password)):
            print("<Bobby> lol nice try won't give password that easily")
        else:
            print("<Bobby> NANI?? Impossible?!?")
            print("<Bobby> I might as wel give you the flag")
            print(f"<Bobby> {flag}")
    else:
        print("<Bobby> bro what, who denies free diamonds?")
    print("Bobby has left the game")


gamechat()
```

Ta cần tìm `sub` để khôi phục lại $p$ và $q$. Vì `sub` cũng khá bé nên mình sẽ tiếp tục brute-force. Nhưng vấn đề ở đây là thời gian để tìm ra `password` chỉ là 2.5s nên ta cần tối ưu.

Ta có:
* $N = p*q$
* $(p-sub)*(q-sub) = leak$
$\to N - sub(p+q) + sub^2 = leak$
$\to N - leak = sub(p+q) - sub^2$

**funfact:** ban đầu mình định factor cho đống này để tìm `sub` nhưng mà bất thành vì nó ko factor được với lại nếu factor được thì cũng khó mà lấy chính xác được giá trị của `sub` (nó có phải prime đâu 🙉).

Mình chuyển qua giải phương trình nhưng cũng không được tối ưu nên mình cầu cứu anh Việt và có được một script hoàn chỉnh.

**Sage:**
```python=
from sage.all import *
from Crypto.Util.number import *
from pwn import *

# io = process(["python3", "server.py"])
io = remote("tjc.tf", 31601)
io.recvuntil(b"<Bobby> i'll give you the powerful numbers, ")
c = Integer(io.recvuntil(b' and ', drop=True).decode())
n = Integer(io.recvuntil(b'\n', drop=True).decode())

io.recvuntil(b'<Bobby> btw do you want to get my diamond stash\n')
io.recvuntil(b'<You> ')
io.sendline(b'yea')
io.recvuntil(b"<Bobby> i'll send coords\n")
io.recvuntil(b"<Bobby> ")
l = Integer(io.recvuntil(b'\n', drop=True).decode())

for i in range(2**19 - 1, 2, -1):
    if (n - l + i ** 2) % i == 0:
        s = (n - l + i ** 2) // i
        x = var('x')
        result = solve(x**2 - s*x + n, x)
        result = str(result)
        if 'sqrt' not in result:
            result = eval(result)
            p = str(result[0])[5:]
            q = str(result[1])[5:]
            p = Integer(p)
            q = Integer(q)
            assert p * q == n
            d = inverse_mod(65537, (p - 1) * (q - 1))
            plain = pow(c, d, n)
            io.recvuntil(b'<You> ')
            io.sendline(str(plain).encode())
            io.interactive()
```

Chạy mở DEBUG lên để thấy flag nhá, cứ chạy cho đến khi nào thấy flag thì thôi.
## 3. assume
![image](https://hackmd.io/_uploads/BJ0GnXuQC.png)

* **Attachment:** [`main.sage`](https://tjctf-2024.storage.googleapis.com/uploads/1d22c5b1beac01221451479a49c48d1546743a468088cd345f8da7bbed251295/main.sage)  [`log.txt`](https://tjctf-2024.storage.googleapis.com/uploads/70877e11250f82945c8f6eba364c83c8b30e9882a21c2f3029cc9eb9c1e558de/log.txt)

Bài này mình lấy flag bằng tay nhé (do mình lười code 😅).

## 4. iodomethane
![image](https://hackmd.io/_uploads/SydQaQdmR.png)

**Attachment:** [`output.txt`](https://tjctf-2024.storage.googleapis.com/uploads/bc721e6e95e934475574351070fa70f74b48e0b78b39f189084b1acf361d1e45/out.txt) [`main.py`](https://tjctf-2024.storage.googleapis.com/uploads/6497e49446d3f2633e9bf3a6679a74c6803cfc7c00cd8b3e9e268afc805fbb23/main.py)
```python=
import secrets

flag = "flagtextttttttttttttttttttttt"

matrix = [[],[],[]]

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0192834756{}_!@#$%^&*()"

modulus = 15106021798142166691 #len(alphabet)

flag = [alphabet.index(a) for a in flag]


while len(flag) % 3 != 0:
    flag.append(secrets.randbelow(modulus))


def det(matrix):
    a = matrix[0][0]
    b = matrix[0][1]
    c = matrix[0][2]
    d = matrix[1][0]
    e = matrix[1][1]
    f = matrix[1][2]
    g = matrix[2][0]
    h = matrix[2][1]
    i = matrix[2][2]
    return ((a * e * i - a * f * h) + (b * f * g - b * d * i) + (c * d * h - c * e * g)) % modulus

def randkey():
    test = [[secrets.randbelow(modulus) for i in range(3)] for J in range(3)]
    while (not det(test)):
           test = [[secrets.randbelow(modulus) for i in range(3)] for J in range(3)]
    return test

def dot(a,b):
    return sum([a[i] * b[i] for i in range(len(a))]) % modulus

def mult(key, row):
    return [dot(key[i], row) for i in range(len(key))]

rows = list(zip(flag[::3], flag[1::3], flag[2::3]))
print(rows)

key = randkey()
print(key, det(key), modulus)
enc = [mult(key, snip) for snip in rows]

open("out.txt", "w+").write(str(enc))

```
**output:**

```python=
[8103443654527565038, 9131436358818679900, 4957881569325453096, 10608823513649500284, 6675039786579943629, 6611905972844131155, 1244757961681113340, 7547487070745190563, 1913848887301325654, 9737862765813246630, 2820240734893834667, 4787888165190302097, 11681061051439179359, 11976272630379115896, 2884226871403054033, 13149362434991348085, 2676520484503789480, 6933002550284269375, 6634913706901406922, 3790038065981008837, 7593117393518680210, 1266282031812681717, 14297832010203960867, 6803759075981258244, 2235840587449302546, 9573113061825958419, 7208484535445728720, 3230648965441849617, 14844603229849620928, 2548590493342454145, 12648684202717570605, 8664656898390315577, 13502288186462622020, 8391628990279857365, 5501744205282111713, 5327399420219427046, 904912426181632886, 4805354280735678357, 12915117098149429818, 12340346813869037506, 9907136040657333887, 12822605127735793613]
```

Đây là mã hóa [Hill cipher](https://en.wikipedia.org/wiki/Hill_cipher). Ta sẽ recover lại `key` để giải mã được ciphertext.

Để lấy được key mình sẽ thực hiện như sau:
- Key là một ma trận $3 \times 3$ 
- Form flag là `tjctf{` ta đã có 6 ký tự chỉ còn thiếu 3 ký tự nữa là đủ một ma trận $3 \times 3$. 
- Ta sẽ brute-force 3 ký tự đó nó chỉ rơi vào khoảng $75*74*73 = 405150$ nên ta có thể brute một cách nhanh chóng.

**Sage**
```python=
flag_enc = [8103443654527565038, 9131436358818679900, 4957881569325453096, 10608823513649500284, 6675039786579943629, 6611905972844131155, 1244757961681113340, 7547487070745190563, 1913848887301325654, 9737862765813246630, 2820240734893834667, 4787888165190302097, 11681061051439179359, 11976272630379115896, 2884226871403054033, 13149362434991348085, 2676520484503789480, 6933002550284269375, 6634913706901406922, 3790038065981008837, 7593117393518680210, 1266282031812681717, 14297832010203960867, 6803759075981258244, 2235840587449302546, 9573113061825958419, 7208484535445728720, 3230648965441849617, 14844603229849620928, 2548590493342454145, 12648684202717570605, 8664656898390315577, 13502288186462622020, 8391628990279857365, 5501744205282111713, 5327399420219427046, 904912426181632886, 4805354280735678357, 12915117098149429818, 12340346813869037506, 9907136040657333887, 12822605127735793613]
flag_enc = [flag_enc[i*3:(i+1)*3] for i in range(len(flag_enc)//3)]

print(len(flag_enc))
modulus = 15106021798142166691
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0192834756{}_!@#$%^&*()"

import itertools
combinations = list(itertools.combinations(alphabet, 3))


for i in combinations:
    try:
        guess = "tjctf{" + ''.join(i)
        guess = [alphabet.index(a) for a in guess]
        rows = list(zip(guess[::3], guess[1::3], guess[2::3]))
        
        A = matrix(GF(modulus), rows)
        B = matrix(GF(modulus), flag_enc[:3])
        key = A.inverse()*B
        flag_mat = matrix(GF(modulus), flag_enc)
        flag_dec = flag_mat * key.inverse()
        # print(flag_dec)
        flag = ''
        for i in flag_dec:
            for j in i:
                if j < 76:
                    flag += alphabet[j]
        if flag[-1] == '}': 
            print(flag)
    except:
        continue

```
Mình chạy thì ra được như này
![image](https://hackmd.io/_uploads/SJEGs4d7R.png)

Thấy chữ hillll nên mình biết nó chính là flag :)))))))

Flag: `tjctf{aint_no_hillllll_55e4$S56a356^#@!$}`

## 5. lightweight-crypto-guard-system

![image](https://hackmd.io/_uploads/H1Hl24uQ0.png)

**Attachment:** [`out.txt`](https://tjctf-2024.storage.googleapis.com/uploads/449fcf7379f2d3f2ba3bf0fe8696be65872e48114fb2a10a098096cb3e21c302/out.txt) [`encode.py`](https://tjctf-2024.storage.googleapis.com/uploads/3b712257e0c28aee57b8db567d619a48fab68d915b707722c5e4cf9cda905a66/encode.py) 

```python=
#!/usr/local/bin/python3.10 -u

from Crypto.Util.number import *
import random

a = getRandomNBitInteger(30)
c = getRandomNBitInteger(15)
m = getPrime(32)
x0 = getRandomNBitInteger(30)

n = random.randint(2**8, 2**10)

flag = open("flag.txt").read().strip()

class Random():
    global m, a, c

    def __init__(self, x0):
        self.x0 = x0

    def random(self):
        self.x0 = (a*self.x0+c) % m
        return self.x0


encryptor = Random(x0)

assert m < 2**32
assert isPrime(m)

x = [ord(i) for i in flag]

with open("out.txt", "w") as wfile:
    for ind in range(len(x)):
        next = encryptor.random()
        if ind < 6:
            print(str(next))
            wfile.write(str(next) + "\n")
        for __ in range(n-1):
            x[ind] ^= encryptor.random()
    print(f"n = {n}")
    print(" ".join([str(i) for i in x]))
    wfile.write("n = " + str(n) + "\n")
    wfile.write(" ".join([str(i) for i in x]) + "\n")

```
**output** hơi dài nên bạn có thể tải về rồi xem nhé.

Đây là một bài về [Linear congruential generator(lcg)](https://en.wikipedia.org/wiki/Linear_congruential_generator) đọc qua nếu bạn chưa biết gì về nó.
Với giá trị $x_0$ ban đầu ta gen ra được các giá trị tiếp theo:
$$x_i = x_{i-1}*a +  c \pmod n$$
Nhưng ở đây có điều đặc biệt là những số từ LCG mà ta nhận được không phải 6 số liên tiếp mà nó kiểu như thế này:
$[x_1, x_{n +1}, x_{n*2 +1}, \dots x_{n*5 +1}], \text{với n = 987}$

**Ta thấy rằng:**

$\begin{split}\begin{align*}
    x_1 & = (a x_0 + c) \bmod m \\
    x_2 & = (a x_1 + c) \bmod m = (a \cdot (a x_0 + c) + c) = (a^2 x_0 + c \cdot (a+1)) \bmod m \\ 
    x_3 & = (a x_2 + c) \bmod m = \ldots = a^3 x_0 + a^2 c + ac + c = a^3 x_0 + c \cdot (1 + a + a^2) \cdot c \bmod m \\ 
\vdots
\end{align*}\end{split}$

* Vì $1, (a+1), (1 + a + a^2)$ lần lượt là tổng các cấp số nhân nên ta sẽ có một công thức tổng quát như sau:
$$x_i = a^i \cdot x_0 + c \cdot \dfrac{a^i - 1}{a-1}$$

Với dãy mà đề bài cho ta có thể viết lại như sau:
$$X_{i+1} = A \cdot X_i + K \pmod m$$
Trong đó:
* $n = 987$
* $X_0 = x_1$
* $K = c \cdot \dfrac{a^n - 1}{a-1}$
* $A = a^n$

Giờ ta một LCG mới với hệ số là $A, K$ giải bình thường để tìm $A, K$ sau đó là tìm $c$ và $a$.
### 5.1 Tìm m
Ở đây mình sẽ nêu cách tìm tổng quát nhé. Mình đọc được ở cái [link](https://math.stackexchange.com/questions/2724959/how-to-crack-a-linear-congruential-generator-lcg) này.
Xét $Y_n = X_{n+1} - X_n$
- Ta có:
$Y_{n+1} = X_{n+2} - X_{n+2} = (A \cdot X_{n+1} + K) - (A \cdot X_n + K) = A \cdot (X_{n+1} - X_n) = A \cdot Y_n$
- Với $Y_n= A \cdot Y_{n-1}$ ta sẽ có:
$\begin{split}\begin{align*}
Y_{n+1} &\equiv A \cdot Y_{n} \equiv A^2 \cdot Y_{n-1} \pmod m\\
Y_{n+2} &\equiv A \cdot Y_{n+1} \equiv A^3 \cdot Y_{n-1} \pmod m\\
\end{align*}\end{split}$
- Suy ra:
$$Y_{n+2} \cdot Y_n - {Y_{n+1}}^2 \equiv Y_{n-1} \cdot (A^4-A^4) \equiv 0 \pmod m$$ 
Như vậy chúng ta đã tạo ra một bội của $m$, thực hiện gcd cho các bội này ta tìm được $m$.

```python=
s = [123855601, 1877660078, 2332452388, 2265666365, 2173629406, 2460275121]
t = []
for i in range(5):
    t.append(s[i+1]-s[i])

u = []
for i in range(3):
    u.append(abs(t[i+2]*t[i] - t[i+1]**2))

m = gcd(u)
#m = 2584844783
```

### 5.2 Tìm $a, c, x_0$
Trước tiên ta đi tìm $A, K$
Ta có:
$$\begin{split}\begin{align*}
X_2 - X_1 = A \cdot (X_1 - X_0) \bmod m \\
\Rightarrow A = (X_2 - X_1) \cdot {(X_1 - X_0)}^{-1}
\end{align*}\end{split}$$

Thế $A$ vào $X_1 = A \cdot X_0 + K$ và tìm được $K$
Tìm a:
$A = a^n \Rightarrow a = A^d \bmod m, \text{ với } d = n^{-1} \bmod (m-1)$
Tìm c:
$K = c \cdot \dfrac{a^n - 1}{a-1} \Rightarrow c = K \cdot (a-1) \cdot {(a^n -1)}^{-1} \bmod m$

Có $a, c$ thế vào $x_1 = a \cdot x_0 + c \bmod m$ và giải ra $x_0$
```python=
s = [GF(m)(i) for i in s]

A = (s[2] - s[1])*pow((s[1]-s[0]), -1, m)

a = pow(A, pow(987, -1, m-1), m)
#print(a)
k = (s[1] - A*s[0])
c = (k*(a-1)*pow(pow(a, 987, m) -1, -1, m)) % m
x0 = (s[0] -c) * pow(a, -1, m)

```

### 5.3 Lấy Flag
```python=
m = 2584844783
a = 779849675 
c = 23327 
x0 = 579927320
class Random():
    global m, a, c

    def __init__(self, x0):
        self.x0 = x0

    def random(self):
        self.x0 = (a*self.x0+c) % m
        return self.x0

encryptor = Random(x0)

flag_enc = flag_enc = open("out.txt", "r").readlines()[-1]
flag_enc = [int(i) for i in flag_enc.split()]
n = 987
for ind in range(len(flag_enc)):
    next = encryptor.random()
    if ind < 6:
        print(str(next))
    for __ in range(n-1):
        flag_enc[ind] ^= encryptor.random()

print(''.join([chr(i) for i in flag_enc]))
```

> Flag: tjctf{1t_15_a_p3r1od_of_c1v1l_war5_1n_th3_galaxy._a_brav3_all1anc3_of_und3rground_fr33dom_f1ght3r5_ha5_chall3ng3d_th3_tyranny_and_oppr3551on_of_th3_aw35om3_galact1c_3mp1r3._5tr1k1ng_from_a_fortr355_h1dd3n_among_th3_b1ll1on_5tar5_of_th3_galaxy,_r3b3l_5pac35h1p5_hav3_won_th31r_f1r5t_v1ctory_1n_a_battl3_w1th_th3_pow3rful_1mp3r1al_5tarfl33t._th3_3mp1r3_f3ar5_that_anoth3r_d3f3at_coult5_compl3t1on_5p3ll5_c3rta1n_doom_for_th3_champ1on5_of_fr33dom.}

## Loading .........