ssss
https://www.zkdocs.com/docs/zkdocs/protocol-primitives/alt-shamir/#moving-away-from-the-constant-term
nc ctfi.ng 31555
Attachment:
#!/usr/local/bin/python3
import random
p = 2**255 - 19
k = 15
SECRET = random.randrange(0, p)
print("welcome to ssss")
# Step 1: Generate a random, degree-(k−3) polynomial g(x)
g = [random.randrange(p) for _ in range(k - 2)]
# Step 2: Select a random c in Fp
c = random.randrange(0, p)
# Step 3: Set f(x)=g(x)x^2+Sx+c
f = [c] + [SECRET] + g
def evaluate_poly(f, x):
return sum(c * pow(x, i, p) for i, c in enumerate(f)) % p
for _ in range(k - 1):
x = int(input())
assert 0 < x < p, "no cheating!"
print(evaluate_poly(f, x))
if int(input("secret? ")) == SECRET:
FLAG = open("flag.txt").read()
print(FLAG)
We are given a polynomial of 14 degree with 15 random coefficients. We can query 14 points on the polynomial, and need to extract the coefficient before \(x\): \(S\) in \(f(x)=g(x)x^2+Sx+c\).
Initially, I though it can be solved in a similar way to ssss in SekaiCTF 2025, by using a primitive root. However, the field here, does not have a 14-order root anymore:
sage: (2**255-19).factor()
2^2 * 3 * 65147 * 74058212732561358302231226437062788676166966415465897661863160754340907
We can have 12-order root, but we cannot compute S: If we use the same algorithm from ssss in SekaiCTF 2025, we can use 12 points to make \(x^{12}\) equals to \(1\), but at the same time, \(x^{13}\) equals to \(x\) and \(x^{14}\) equals to \(x^2\). We cannot distinguish the coefficient before \(x^{13}\) and \(x\). Maybe there is some way to solve this, I did not figure it out in the competition.
After the competition ends, I found a solution at c240030/corCTF-2025, which uses a novel way to solve the problem:
Since we want to compute the coefficient before \(x\), we can eliminate the coefficients for the even-ordered coefficients by using a pair of \(x_1\) and \(-x_1\):
This way, we only have 7 unknown coefficients of a 6-order polynomial: \(a_0, a_3, \cdots, a_{13}\). We can get 7 equations by asking \(7*2\) points on the polynomial, which exactly matches the requirement. The solution:
from pwn import *
from sage.all import *
context(log_level="debug")
# p = process(["python3", "./ssss.py"])
p = remote(host="ctfi.ng", port="31555")
prime = 2**255 - 19
R = Integers(prime)
def mod_inverse(a, m):
"""Calculates the modular multiplicative inverse of a modulo m."""
return pow(a, m - 2, m)
p.recvuntil("ssss")
p.recvline()
shares = []
for i in range(1, 8):
# evaluate at i
x = i
p.sendline(str(x).encode())
y1 = int(p.recvline().strip())
# evaluate at -i
x = prime - i
p.sendline(str(x).encode())
y2 = int(p.recvline().strip())
real_x = pow(i, 2, prime)
numerator = (y1 - y2 + prime) % prime
denominator_inv = mod_inverse(2 * i, prime)
real_y = (numerator * denominator_inv) % prime
shares.append((real_x, real_y))
print(shares)
R = GF(2**255 - 19)["x0"]
res = list(R.lagrange_polynomial(shares))
p.recvuntil("secret?")
p.sendline(str(res[0]).encode())
p.interactive()
Get flag: corctf{ill_come_up_with_a_good_flag_later_maybe}.