CakeCTF2021 Crypto

discrete log

ランダムな素数 $p,q,r$ があり、フラグの各文字 $m_i$ について $c_i = g^{rm_i}\ \mathrm{mod}\ p$ が計算されている。$p,g,c_i$ が既知。

フラグの先頭、つまり $c_0$ と $c_1$ の平文 $m_0,m_1$ が ‘C’ 及び ‘a’ で有ることを利用すれば RSA の Common Modulus Attack と同じように解くことができる。 今 $m_0 x + m_1 y = 0$ の解を $s_0, s_1$ とすれば、 $$c_0^{s_0} c_1^{s_1} \equiv g^{rm_0 s_0} g^{rm_1 s_1} \equiv g^{r(m_0 s_0 + m_1 s_1)} \equiv g^r \mod p$$ となり、$g^r$ が計算できる。

$g^r$ がわかれば、あとは $c_i$ に対して $(g^r)^{m_i} \equiv c_i \mod p$ となるような $m_i$ を全探索すればフラグが求まる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import long_to_bytes

with open("output.txt") as f:
	buf = f.read().split("\n")

p = eval(buf[0])
g = eval(buf[1])
cs = eval(buf[2])

m1 = ord("C")
m2 = ord("a")

_, s1, s2 = xgcd(m1, m2)
gr = (pow(cs[0], s1, p) * pow(cs[1], s2, p)) % p
print(gr)

m = ""
for c in cs:
	for i in range(1, 0xff):
		if c == pow(gr, i, p):
			m += chr(i)
			print(m)
			break

improvisation

LFSR でビット単位で暗号化されたフラグが与えられる。フラグの先頭が CakeCTF{ なので 63 ビット分の情報が手に入り、LFSR の状態をシミュレートできてしまう。よくある疑似乱数予測問。

 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
from Crypto.Util.number import *

c = 0x58566f59979e98e5f2f3ecea26cfb0319bc9186e206d6b33e933f3508e39e41bb771e4af053

def get_top(i):
	return int(("0" + bin(c)[2:])[i])

ss = b"CakeCTF{"
mm = int.from_bytes(ss, "little")

cs = []
cnt = 0
while mm:
	cc = get_top(cnt) ^^ (mm & 1)
	cs.append(cc)
	cnt += 1
	mm >>= 1

for rbits in reversed(range(63, 64)):
	csr = [1] + list(reversed(cs[:rbits]))
	for i in range(len(csr)):
		csr[i] = str(csr[i])
	r = int("".join(csr), 2)
	print(r)
	
	rs = []
	for i in range(300):
		rs.append(r & 1)
		b = (r & 1) ^^ ((r & 2) >> 1) ^^ ((r & 8) >> 3) ^^((r & 16) >> 4)
		r = (r >> 1) | (b << 63)

	print(rs)

	m = ""
	for i in range(len(bin(c)[2:])):
		m += str(get_top(i) ^^ rs[i])
	
	print("")
	print(long_to_bytes(int(m[::-1], 2))[::-1])

Together as one

$N = pqr$ の RSA。$x \equiv (p + q)^r \mod n$ と $y \equiv (p + qr)^r \mod n$ が与えられる。

今 $x$ について、二項定理を使って展開すれば、$p^r$ と $q^r$ の項以外の二項係数は $r$ を約数に含むため $pqr$ で割り切れるので $$x \equiv (p + q)^r \equiv p^r + q^r \mod n$$ である。また $y$ についても展開すれば $pqr$ の項が出てくるので $$y \equiv (p + qr)^r \equiv p^r + (qr)^r \mod n$$ である。よって $$y - x \equiv q^r r^r - q^r \equiv q^r (r^r - 1) \mod n$$

したがって $y - x = q^r (r^r - 1) + kpqr$ なので、$n$ と最大公約数を取れば $q$ が求まる。 さらに、$y - x \equiv -q^r \mod r$ なので、オイラーの定理から $y - x \equiv -q \mod r$ であることがわかる。よって $y - x + q = kr$ なのでこれと $n$ との gcd を取れば $r$ も求まる。 よって $q,r$ が求まったので $p$ も求まり、素因数分解をすることができた。勝ち。

 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
from Crypto.Util.number import *

with open("output.txt") as f:
	buf = f.read().split("\n")

n = eval(buf[0].split()[2])
c = eval(buf[1].split()[2])
x = eval(buf[2].split()[2])
y = eval(buf[3].split()[2])
e = 0x10001

q = gcd(y - x, n)
print(q)

pr = n // q

r = gcd(y - x + q, pr)
print(r)

p = pr // r

phi = (p - 1) * (q - 1) * (r - 1)
d = inverse_mod(e, phi)

m = pow(c, d, n)
print(long_to_bytes(m))

続きは書いてる途中

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
テーマ StackJimmy によって設計されています。