# WRITE-UP
## ECCIBIDI
Bài này có 2 điểm mấu chốt:
- Đầu tiên là hàm random không secure, khi ta mở 2 connect tới server cùng lúc thì cả 2 sẽ sử dụng chung 1 seed
```py
def getrandbits(nbits):
rng = random.Random(int(time.time()) * S)
return rng.getrandbits(nbits)
def randbelow(p):
l = p.bit_length() // 2
return ((random.getrandbits(l) << l) | getrandbits(l)) % p
```
- Điều này dẫn đến khi gọi hàm:
```py
k = randbelow(curve_order)
```
Thì $k$ sẽ có một nửa bit cuối giống nhau.
- Thứ hai, server sử dụng chung một secret với mọi instance.
```python!
from secret import sk, S, FLAG
```
Tiếp theo ta sẽ cần biết về Hidden Number Problem:
https://hackmd.io/@nomorecaffeine/r1xstVfxC#III-Hidden-Number-Problem
Vậy từ những điều trên ta sẽ có được gì? Chú ý vào đoạn code tạo cookie sau:
```python!
s = (sk * tmp_hash + k) % curve_order
```
Và ta biết được với mỗi 2 connect đến server ta sẽ có được:
$$l = \frac{\text{curve_order.bit_length()}}{2}$$
$$s1 = (sk * \text{tmp_hash1} + k1) \mod \text{curve_order}$$
$$s2 = (sk * \text{tmp_hash2} + k2) \mod \text{curve_order}$$
$$\rightarrow s1 - s2 = (sk * (\text{tmp_hash1} - \text{tmp_hash2}) + (k1 - k2)) \mod \text{curve_order}$$
$$\rightarrow (s1 - s2) * 2^{-l} = (sk * (\text{tmp_hash1} - \text{tmp_hash2}) * 2^{-l}) \mod \text{curve_order}$$
Sau đó giải bài toán HNP là sẽ tìm được sk.
```python!
from pwn import *
from Crypto.Util.number import *
from hashlib import sha256
from sage.all import *
import json
# context.log_level = 'error'
curve_order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
Xs = []
Cs = []
l = curve_order.bit_length() // 2
l_ = 124
for i in range(4):
conn1 = process(["python3", "server.py"])
conn2 = process(["python3", "server.py"])
conn1.sendlineafter(b'>> ', b'1')
conn1.recvuntil(b'Account information: ')
hsh1 = bytes_to_long(sha256(conn1.recvline()[:-1]).digest())
conn1.recvuntil(b'Cookie: ')
cookie = conn1.recvlineS()
s1, h = cookie.split(".")
s1 = bytes_to_long(bytes.fromhex(s1))
conn2.sendlineafter(b'>> ', b'1')
conn2.recvuntil(b'Account information: ')
hsh2 = bytes_to_long(sha256(conn2.recvline()[:-1]).digest())
conn2.recvuntil(b'Cookie: ')
cookie = conn2.recvlineS()
s2, h = cookie.split(".")
s2 = bytes_to_long(bytes.fromhex(s2))
conn1.close()
conn2.close()
Xs.append((hsh1 - hsh2) * inverse(2 ** l, curve_order) % curve_order)
Cs.append((s1 - s2) * inverse(2 ** l, curve_order) % curve_order)
k = 2 ** (l_ + 1)
B = Matrix(QQ, len(Xs) + 2, len(Xs) + 2)
for i in range(len(Xs)):
B[i, i] = curve_order
B[-2, i] = Xs[i]
B[-1, i] = Cs[i]
B[-1, -1] = k
B[-2, -2] = QQ(k) / QQ(curve_order)
B = B.LLL()
for i in B:
if abs(i[-1]) == k:
sk = (-i[-2] * curve_order // k) % curve_order
print(f"{sk = }")
conn = process(["python3", "server.py"])
conn.sendlineafter(b'>> ', b'1')
conn.recvuntil(b'Account information: ')
hsh = bytes_to_long(sha256(conn.recvline()[:-1]).digest())
conn.recvuntil(b'Cookie: ')
cookie = conn.recvlineS()
s, h = cookie.split(".")
s = bytes_to_long(bytes.fromhex(s))
k = (s - sk * hsh) % curve_order
tmp = {"username" : 'ADP', "isAdmin" : True}
tmp["userID"] = os.urandom(16).hex()
tmp_hash = bytes_to_long(sha256(json.dumps(tmp).encode()).digest())
s_new = (sk * tmp_hash + k) % curve_order
cookie = long_to_bytes(s_new).hex() + '.' + h[:-1]
print(cookie)
conn.sendlineafter(b'>> ', b'2')
conn.sendlineafter(b'information: ', json.dumps(tmp).encode())
conn.sendlineafter(b'cookie: ', cookie.encode())
# print(conn.recvlineS())
conn.interactive()
```
## Warmup RSA
Với bài này ta có thể tính lại xấp xỉ của $p + q$ và $p - q$
Ta có biểu thức:
$$e * u = w + (p ^ 4 - 1) * (q ^ 4 - 1) * v$$
$$\rightarrow (p ^ 4 - 1) * (q ^ 4 - 1) = \frac{e * u}{v} - \frac{w}{v} \ (1) $$
Ta cũng có:
$$ (p ^ 4 - 1) * (q ^ 4 - 1) = n ^ 4 - (p ^ 4 + q ^ 4) + 1 $$
Đầu tiên ta sẽ xử lí $p + q$:
$$(p ^ 4 - 1) * (q ^ 4 - 1) = n ^ 4 - [(p + q) ^ 4 - 4n(p + q)^2 + 2n^2] + 1$$
$$(p ^ 4 - 1) * (q ^ 4 - 1) = (n^2 - 1)^2 - (p + q)^4 + 4n(p + q)^2 \ (2)$$
Từ $(1)$ và $(2)$:
$$(n^2 - 1)^2 - (p + q)^4 + 4n(p + q)^2 = \frac{e * u}{v} - \frac{w}{v}$$
$$\rightarrow ((p + q)^2)^2 - 4n(p + q)^2 - ((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v}) = 0$$
$$\Delta = 16n^2 + 4((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v}) \geq 0$$
$$(p + q)^2 = \frac{4n + 2\sqrt{((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v})}}{2}$$
$$(p + q) = \sqrt{2n + \sqrt{((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v})}}$$
$$\widehat{(p + q)} = \sqrt{2n + \sqrt{((n^2 - 1)^2 - \frac{e * u}{v}}}$$
Tương tự với $p - q$, ta có:
$$(p ^ 4 - 1) * (q ^ 4 - 1) = n ^ 4 - [(p - q) ^ 4 + 4n(p - q)^2 + 2n^2] + 1$$
$$(p ^ 4 - 1) * (q ^ 4 - 1) = (n^2 - 1)^2 - (p - q)^4 - 4n(p - q)^2 \ (3)$$
Từ $(1)$ và $(3)$:
$$((p - q)^2)^2 + 4n(p - q)^2 - ((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v}) = 0$$
$$\Delta = 16n^2 + 4((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v}) \geq 0$$
$$(p - q) = \sqrt{-2n + \sqrt{((n^2 - 1)^2 - \frac{e * u}{v} + \frac{w}{v})}}$$
$$\widehat{(p - q)} = \sqrt{-2n + \sqrt{((n^2 - 1)^2 - \frac{e * u}{v}}}$$
$$\hat{p} = \frac{\widehat{(p - q)} + \widehat{(p + q)}}{2}$$
```python!
from sage.all import *
from Crypto.Util.number import *
from sympy import sqrt
u = ...
v = ...
N = ...
e = ...
c = ...
appq = sqrt(2 * N + sqrt((N ** 2 + 1) ** 2 - e * u // v))
apnq = sqrt(-2 * N + sqrt((N ** 2 + 1) ** 2 - e * u // v))
ap = int(apnq + appq) // 2
def flatter(M):
import re
from subprocess import check_output
# compile https://github.com/keeganryan/flatter and put it in $PATH
z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]"
ret = check_output(["flatter"], input=z.encode())
return matrix(M.nrows(), M.ncols(), list(map(int, re.findall(b"-?\\d+", ret))))
def small_roots(self, X=None, beta=1.0, epsilon=None, **kwds):
from sage.misc.verbose import verbose
from sage.matrix.constructor import Matrix
from sage.rings.real_mpfr import RR
N = self.parent().characteristic()
if not self.is_monic():
raise ArithmeticError("Polynomial must be monic.")
beta = RR(beta)
if beta <= 0.0 or beta > 1.0:
raise ValueError("0.0 < beta <= 1.0 not satisfied.")
f = self.change_ring(ZZ)
P, (x,) = f.parent().objgens()
delta = f.degree()
if epsilon is None:
epsilon = beta / 8
verbose("epsilon = %f" % epsilon, level=2)
m = max(beta**2 / (delta * epsilon), 7 * beta / delta).ceil()
verbose("m = %d" % m, level=2)
t = int((delta * m * (1 / beta - 1)).floor())
verbose("t = %d" % t, level=2)
if X is None:
X = (0.5 * N ** (beta**2 / delta - epsilon)).ceil()
verbose("X = %s" % X, level=2)
# we could do this much faster, but this is a cheap step
# compared to LLL
g = [x**j * N ** (m - i) * f**i for i in range(m) for j in range(delta)]
g.extend([x**i * f**m for i in range(t)]) # h
B = Matrix(ZZ, len(g), delta * m + max(delta, t))
for i in range(B.nrows()):
for j in range(g[i].degree() + 1):
B[i, j] = g[i][j] * X**j
B = flatter(B)
f = sum([ZZ(B[0, i] // X**i) * x**i for i in range(B.ncols())])
R = f.roots()
ZmodN = self.base_ring()
roots = set([ZmodN(r) for r, m in R if abs(r) <= X])
Nbeta = N**beta
return [root for root in roots if N.gcd(ZZ(self(root))) >= Nbeta]
x = polygen(Zmod(N))
f = (ap + x).monic()
rs = small_roots(f, X=2 ** 256, beta=0.499, epsilon=0.015)
p = int(ap + rs[0])
q = N // p
phi = (p - 1) * (q - 1)
print(long_to_bytes(pow(c, inverse(e, phi), N)))
```
```
W1{3z_c0pp3r5m1th_RSA!!!!!!!!!!!!!!}
```