整体概况
rank 135/880
感觉可以更高的,毕竟没有一直打,这次主要水了一下cry和re,下面是整体题目情况
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
|
## crypto
### seesaw (100pt / 612 solves)
### 01-Translator (100pt / 280 solves)
### Elliptic4b (272pt / 171 solves)
### Golden Ticket (491pt / 35 solves)
### mathmyth (452pt / 79 solves)
## reversing
### CrazyLazyProgram1 (100pt / 654 solves)
### CrazyLazyProgram2 (100pt / 468 solves)
### D-compile (100pt / 335 solves)
### wasm_S_exp (100pt / 330 solves)
### MAFC (339pt / 144 solves)
### code_injection (441pt / 88 solves)
|
加*
号的是没出来的
Crypto
seesaw
task.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import os
from Crypto.Util.number import getPrime
FLAG = os.getenv("FLAG", "ctf4b{dummy_flag}").encode()
m = int.from_bytes(FLAG, 'big')
p = getPrime(512)
q = getPrime(16)
n = p * q
e = 65537
c = pow(m, e, n)
print(f"{n = }")
print(f"{c = }")
|
分解后用p即可
1
2
3
4
5
6
7
|
p =
n =
c =
from Crypto.Util.number import*
print(long_to_bytes(pow(c,inverse(65537,p-1),p)))
|
01-Translator
task.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long
def encrypt(plaintext, key):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(pad(plaintext.encode(), 16))
flag = os.environ.get("FLAG", "CTF{dummy_flag}")
flag_bin = f"{bytes_to_long(flag.encode()):b}"
trans_0 = input("translations for 0> ")
trans_1 = input("translations for 1> ")
flag_translated = flag_bin.translate(str.maketrans({"0": trans_0, "1": trans_1}))
key = os.urandom(16)
print("ct:", encrypt(flag_translated, key).hex())
|
一个很有意思的题目,把二进制里面的0/1
替换掉,然后再AES-ECB
加密。
主要解密的方法就是输入两个字符串,然后根据频率选出两块,分别尝试A->1,B->0/B->1,A->0,然后就可以解密成功了,这里脚本通过gemini编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
from Crypto.Util.number import long_to_bytes
ct = ''
zero = ''
one = ''
ct = ''
zero = ''
one = ''
block_size = 32
blocks = [ct[i:i+block_size] for i in range(0, len(ct), block_size)]
bitstring = ''
for block in blocks:
if block == one:
bitstring += '1'
elif block == zero:
bitstring += '0'
num = int(bitstring, 2)
print(long_to_bytes(num))
|
Elliptic4b
task.py
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
|
import os
import secrets
from fastecdsa.curve import secp256k1
from fastecdsa.point import Point
flag = os.environ.get("FLAG", "CTF{dummy_flag}")
y = secrets.randbelow(secp256k1.p)
print(f"{y = }")
x = int(input("x = "))
if not secp256k1.is_point_on_curve((x, y)):
print("// Not on curve!")
exit(1)
a = int(input("a = "))
P = Point(x, y, secp256k1)
Q = a * P
if a < 0:
print("// a must be non-negative!")
exit(1)
if P.x != Q.x:
print("// x-coordinates do not match!")
exit(1)
if P.y == Q.y:
print("// P and Q are the same point!")
exit(1)
print("flag =", flag)
|
这个ECC题目就很就简单了,题目生成了一个y值,我们要输入一个x并且确保在曲线上,然后输入a,并且满足后面的条件
解密方法很简单。你首先要知道secp256k1
曲线的方程是y² = x³ + 7 (mod p)
,x可以直接求出,后面就是ECC的常识(q−1)⋅P=−P
,那么a就出来了
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
|
import sympy
from pwn import remote
from fastecdsa.curve import secp256k1
p = secp256k1.p
q = secp256k1.q
while True:
try:
io = remote('elliptic4b.challenges.beginners.seccon.jp', 9999)
io.recvuntil(b'y = ')
y = int(io.recvline().strip())
print(f"[*] Received y = {y}")
c = (y * y - 7) % p
x = sympy.nthroot_mod(c, 3, p, all_roots=False)
io.sendlineafter(b'x = ', str(x).encode())
print("[*] Sent x.")
a = q - 1
io.sendlineafter(b'a = ', str(a).encode())
print("[*] Sent a.")
# 4. 接收 flag 并退出循环
print("\n[+] Success! Flag:")
response = io.recvall(timeout=2)
print(response.decode())
io.close()
break
except Exception as e:
print(f"[!] An error occurred: {e}. Retrying...")
if 'io' in locals() and io:
io.close()
|
mathmyth
task.py
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
44
45
46
|
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
import os, hashlib, secrets
def next_prime(n: int) -> int:
n += 1
while not isPrime(n):
n += 1
return n
def g(q: int, salt: int) -> int:
q_bytes = q.to_bytes((q.bit_length() + 7) // 8, "big")
salt_bytes = salt.to_bytes(16, "big")
h = hashlib.sha512(q_bytes + salt_bytes).digest()
return int.from_bytes(h, "big")
BITS_q = 280
salt = secrets.randbits(128)
r = 1
for _ in range(4):
r *= getPrime(56)
for attempt in range(1000):
q = getPrime(BITS_q)
cand = q * q * next_prime(r) + g(q, salt) * r
if isPrime(cand):
p = cand
break
else:
raise RuntimeError("Failed to find suitable prime p")
n = p * q
e = 0x10001
d = pow(e, -1, (p - 1) * (q - 1))
flag = os.getenv("FLAG", "ctf4b{dummy_flag}").encode()
c = pow(bytes_to_long(flag), e, n)
print(f"n = {n}")
print(f"e = {e}")
print(f"c = {c}")
print(f"r = {r}")
|
一个有趣的数学题。
$$
n=p*q=q^3*rr+g*q*r \\
n=q^3*rr \mod r
$$
r是四个素数相乘,自然而然crt,q = t+kr
是不能求出的,后面发现直接硬爆,假设g那一块为0,我们可以轻易的求出q,那么随着k的下降,g的值就会上升,g大约在512bits一下,那么爆破的次数就是几百次。很好的一种方法
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
from sage.all import*
from Crypto.Util.number import*
n =
e = 65537
c =
r =
def solve(n, e, c, r, max_steps=600):
rp = next_prime(r)
prime_factors_r = [p for p, _ in factor(r)]
residues_per_prime = []
for pi in prime_factors_r:
Z_pi = IntegerModRing(pi)
Ai = Z_pi(n) * Z_pi(rp)^-1
roots = Ai.nth_root(3, all=True)
residues_per_prime.append(list(map(int, roots)))
# 使用 itertools.product 生成所有余数组合,然后用CRT求解
from itertools import product
t_list = [crt(list(res_combo), prime_factors_r) for res_combo in product(*residues_per_prime)]
print(t_list)
for t in t_list:
Q0, _ = (n // rp).nth_root(3, truncate_mode=True)
# q = t + k*r, 因此 k ≈ (Q0 - t) / r
k0 = (Q0 - t) // r
for k in range(k0, k0 - max_steps, -1):
q_candidate = t + k * r
if q_candidate <= 1 or not is_prime(q_candidate):
continue
num = n - q_candidate^3 * rp
den = r * q_candidate
if num > 0 and num % den == 0:
S = num // den
p_candidate = q_candidate^2 * rp + r * S
if p_candidate * q_candidate == n and is_prime(p_candidate):
p, q = p_candidate, q_candidate
print(f"✅ 成功找到因子!")
print(f" p = {p}")
print(f" q = {q}")
# 3. 解密
phi = (p - 1) * (q - 1)
d = inverse_mod(e, phi)
m = power_mod(c, d, n)
return long_to_bytes(m)
return None
flag = solve(n, e, c, r)
print(flag)
|
copper的写法
最近cuso
库公开了,可以用这个直接求,非常的nb
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
|
from sage.all import *
from Crypto.Util.number import *
import cuso
import ast
n =
e = 65537
c =
r =
a = next_prime(r) - r
for Q in Zmod(r)(n/a).nth_root(3, all=True):
Q = int(Q)
x, p = var("x, p")
roots = cuso.find_small_roots(
relations=[Q+x*r],
bounds={x: (0, 2**59)},
modulus_multiple=n,
modulus_lower_bound=2**279,
modulus_upper_bound=2**280
)
for root in roots:
p = int(root[p])
q = n // p
print(long_to_bytes(pow(c, pow(e, -1, (p-1)*(q-1)), n)))
break
|
Golden Ticket(*)
task.py
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
flag = os.environ.get("FLAG", "ctf4b{dummy_flag}")
iv = os.urandom(16)
key = os.urandom(16)
challenge = os.urandom(16 * 6)
ENC_TICKET = 3
DEC_TICKET = 3
GOLDEN_TICKET = 0
def menu() -> int:
print("Your tickets:")
if ENC_TICKET > 0:
print(f"{ENC_TICKET} encryption ticket(s)")
if DEC_TICKET > 0:
print(f"{DEC_TICKET} decryption ticket(s)")
if GOLDEN_TICKET > 0:
print(f"{GOLDEN_TICKET} golden ticket(s)")
print()
print(f"1. Encrypt")
print(f"2. Decrypt")
print(f"3. Get ticket")
print(f"4. Get flag")
print(f"5. Quit")
while True:
i = int(input("> "))
if 1 <= i <= 5:
return i
print("Invalid input!")
def consume_ticket(enc: int = 0, dec: int = 0, golden: int = 0):
global ENC_TICKET, DEC_TICKET, GOLDEN_TICKET
if ENC_TICKET < enc or DEC_TICKET < dec or GOLDEN_TICKET < golden:
print("Not enough tickets.")
exit(1)
ENC_TICKET -= enc
DEC_TICKET -= dec
GOLDEN_TICKET -= golden
while True:
i = menu()
if i == 1:
consume_ticket(enc=1)
pt = bytes.fromhex(input("pt> "))
if len(pt) > 16:
print("Input must not be longer than 16 bytes.")
continue
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
print(f"ct:", cipher.encrypt(pad(pt, 16)).hex())
if i == 2:
consume_ticket(dec=1)
ct = bytes.fromhex(input("ct> "))
if len(ct) > 16:
print("Input must not be longer than 16 bytes.")
continue
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
print("pt:", cipher.decrypt(pad(ct, 16)).hex())
if i == 3:
print("challenge:", challenge.hex())
answer = bytes.fromhex(input("answer> "))
if len(answer) != len(challenge) + 16:
print("Wrong length.")
continue
cipher = AES.new(key, AES.MODE_CBC, iv=answer[:16])
if cipher.decrypt(answer[16:]) == challenge:
print("Correct!")
GOLDEN_TICKET += 1337
else:
print("Wrong :(")
if i == 4:
consume_ticket(golden=1)
print("flag:", flag)
if i == 5:
print("Bye!")
exit(0)
|
我对aes的了解还是太少了,需要补充一下这方面的知识了。
整个代码应该挺好阅读的吧
- 选项一就是加密
- 选项二就是解密
- 选项三,服务器给一个challenge,我们传一个answer并且加密后要满足
AES-CBC-Decrypt(ciphertext, iv, key) == challenge
- 选项四得到flag
首先我们肯定要获取challenge的值,
然后我们要求出iv,求iv的方法老生常谈了,
我们输入b"\x10"*16
,加密时会padding,也就是实际加密了b"\x10"*32
,那么我们可以得到的是d1和d2,分别是d1 = xor(encrypt(b"\x10"*16), iv)
、d2 = xor(encrypt(b"\x10"*16), b"\x10"*16)
,这里xor就可以求出iv
那么前面几段可以通过xor求出来
后面的部分通过encrypt就可以推出来
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
44
45
46
47
48
49
50
|
import os
from pwn import *
sc = remote("golden-ticket.challenges.beginners.seccon.jp", 9999)
def enc_oracle(pt):
sc.recvuntil(b"> ")
sc.sendline(b"1")
sc.recvuntil(b"> ")
sc.sendline(pt.hex().encode())
sc.recvuntil(b": ")
return bytes.fromhex(sc.recvline().decode())
def dec_oracle(pt):
sc.recvuntil(b"> ")
sc.sendline(b"2")
sc.recvuntil(b"> ")
sc.sendline(pt.hex().encode())
sc.recvuntil(b": ")
return bytes.fromhex(sc.recvline().decode())
def get_ticket(answer):
sc.recvuntil(b"> ")
sc.sendline(b"3")
sc.recvuntil(b": ")
challenge = bytes.fromhex(sc.recvline().decode())
sc.recvuntil(b"> ")
sc.sendline(answer.hex().encode())
sc.recvline()
return challenge
chal = get_ticket(b"a")
chal = [chal[i:i+16] for i in range(0, len(chal), 16)]
f3 = b"\x10"*16
d = dec_oracle(f3)
print(d)
iv = xor(b"\x10"*16, d[:16], d[16:])
f2 = xor(f3, d[16:], chal[2])
f1 = xor(dec_oracle(f2)[:16], iv, chal[1])
f0 = xor(dec_oracle(f1)[:16], iv, chal[0])
f4 = enc_oracle(xor(f3, iv, chal[3]))[:16]
f5 = enc_oracle(xor(f4, iv, chal[4]))[:16]
f6 = enc_oracle(xor(f5, iv, chal[5]))[:16]
get_ticket(f0+f1+f2+f3+f4+f5+f6)
sc.recvuntil(b"> ")
sc.sendline(b"4")
print(sc.recvline().decode())
|
总结
seccon beginner的题还是一如既往的又新又好