crypto
pingctf
Task: ECDH on NIST P-192 with MAC oracle, flag encrypted using private key. Solution: Invalid-curve attack exploiting missing point validation to recover private scalar via Pohlig-Hellman on smooth-order invalid curves, then CRT reconstruction.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
crypto — pingCTF
Description
ECDH key exchange server on NIST P-192. The flag is encrypted with AES-CTR using a key derived from the server's private scalar. The server accepts arbitrary public points and returns a MAC of the shared secret.
Given: server.py (server implementation), ECDH.py (elliptic curve library), docker-compose.yml, DOCKERFILE.
Remote: nc 178.104.42.20 55137
Authors: mixer + Wintoch/Kubson
Analysis
Server behavior
- Server generates a random private scalar
b_privon NIST P-192 - Flag is encrypted with AES-CTR using
sha256(str(b_priv).encode()).digest()as the key - Server prints
nonce + ciphertextin hex - Server accepts arbitrary public points
R_Aas JSON[x, y] - Server computes
shared_secret = ECDH.ecdh_responder(R_A, b_priv)and returnsMAC = sha256(shared_secret).hexdigest()
# server.py - key derivation aes_key = hashlib.sha256(str(b_priv).encode()).digest() cipher = AES.new(aes_key, AES.MODE_CTR) ciphertext = cipher.encrypt(flag) # server.py - MAC oracle shared_secret, R_B = ECDH.ecdh_responder(R_A, b_priv) mac = hashlib.sha256(shared_secret).hexdigest() print(f"MAC: {mac}")
The vulnerability
The critical flaw is in ECDH.py. The point_add and scalar_mult functions use only the curve parameters p (field prime) and a (curve coefficient), but never validate that the input point lies on the intended curve and never check subgroup membership:
# ECDH.py - point_add only uses p and a, not b! def point_add(P: Point, Q: Point, curve: Curve = NIST_P192) -> Point: p = curve.p # ... if P == Q: m = (3 * x1 * x1 + curve.a) * pow(2 * y1, -1, p) % p # Only uses 'a' else: m = (y2 - y1) * pow(x2 - x1, -1, p) % p # ...
This enables an invalid-curve attack: we can send points from curves with the same a and p but different b values. The server will happily compute b_priv * R_A on whatever curve R_A actually belongs to.
...
$ grep --similar
Similar writeups
- [crypto][free]MadMath— hackthebox
- [crypto][free]Easy DSA— gpnctf
- [crypto][free]Twisted Entanglement— HackTheBox
- [crypto][Pro]Abnormal Ellipse— tamuctf
- [crypto][free]Rhome— HackTheBox