Skip to content
All writeups
POST-EVENT RECONSTRUCTIONCrypto

V1T CTF 2026

CuRvE

How attacker-controlled curve parameters expose a reused ECDSA scalar.

Published
Category
Crypto

The interesting failure was not ECDSA itself. The service generated one secret scalar, then allowed a player to replace the elliptic-curve parameters while silently reusing that same scalar. That turned a signing key into a discrete logarithm on a group the player could influence.

Where the trust boundary broke

The vulnerable flow was equivalent to:

curve = curve_from_player_parameters()
base = curve.random_point()
public = secret_scalar * base

For a fixed, carefully selected curve, recovering secret_scalar from base and public should be hard. Here, however, the player could choose a curve whose group order had a large smooth component. The public pair then exposed the same scalar inside a group where discrete logarithms were intentionally much cheaper.

The input checks also operated on the submitted integers before the curve library reduced them modulo the field prime. An integer congruent to a small coefficient could therefore satisfy a size check while producing the desired small coefficient inside the field.

Engineering a useful group order

Two curve families make the order search easier:

  • the j = 1728 family, represented by curves with a zero constant term;
  • the j = 0 family, represented by curves with a zero linear coefficient.

For each candidate, the reconstruction computed the curve order and factored it. The goal was not a completely smooth order. It was enough to collect prime-power factors into a smooth modulus M large enough to constrain the reused scalar, while leaving any service-required large factor in the remaining cofactor.

There is one practical detail: a random base point does not necessarily have the full curve order. The attack must work with the order of the actual point and retain only factors that genuinely divide that subgroup.

Recovering the scalar residue

With Q = dG, Pohlig–Hellman splits the discrete logarithm into smaller problems for each prime power dividing the smooth portion of the point order:

  1. project G and Q into each prime-power subgroup;
  2. solve the smaller discrete logarithm in that subgroup;
  3. combine the residues with the Chinese Remainder Theorem;
  4. obtain d mod M rather than guessing the whole scalar at once.

The challenge added a second constraint: the scalar was the big-endian integer representation of exactly 21 lowercase ASCII characters. If r is the recovered residue, every candidate has the form:

d = r + kM

The known length gives a tight numeric interval. Checking that all 21 bytes fall between lowercase a and z removes almost every candidate. The remaining value can be verified without ambiguity by testing whether dG equals the supplied public point.

Forging a valid signature

Once the scalar is known, the final step is ordinary ECDSA arithmetic in the selected subgroup. Choose a nonce invertible modulo the point order, derive r from the nonce point, and compute:

s = inverse(nonce) * (message_digest + r * d) mod point_order

The reconstruction verified the forged signature locally before using the challenge's verification path.

Defensive takeaways

  • Do not reuse a secret scalar across attacker-controlled groups or domain parameters.
  • Validate canonical field elements after reduction, not only the bit length of their submitted representation.
  • Validate the curve, subgroup order, and base point together.
  • Treat structured secret generation, such as a fixed lowercase string, as a major reduction in entropy once any modular information leaks.

Event page: V1T CTF 2026 on CTFtime.