Post

Tuwaiq Nights Ctf L3aks

Tuwaiq Nights Ctf L3aks

L3aks — Crypto

Category: Crypto
Difficulty: Easy
Author: Flagyard

Challenge Summary

challenge The challenge implements a Diffie–Hellman key exchange and then derives an AES key from the shared secret.
However, the server leaks partial information about the secret twice. By exploiting this leak we can recover the full secret and decrypt the flag.


Diffie–Hellman Setup

The server generates:

  • a strong prime p
  • generator g = 2
  • secret values a and b

Then computes:

1
2
A = g^a mod p
B = g^b mod p

The shared secret becomes:

1
s = B^a mod p = g^(ab) mod p

The AES key is derived from s:

1
key = SHA1(str(s))[:16]

The flag is encrypted using AES-CBC.


First Information Leak

The server prints:

1
2
mask = ((1 << 256) - 1 << 256) + (1 << 255)
r1 = s & mask

This means we learn the most significant bits of the shared secret.

We can write:

1
s = r1 + x

where

1
0 ≤ x < 2^255

Second Information Leak

Later the server computes:

1
2
3
AC = g^(a + c)
s2 = AC^b
r2 = s2 & mask

So we also know the high bits of another value s2.

We write:

1
s2 = r2 + y

with

1
0 ≤ y < 2^255

Relationship Between the Secrets

From the definitions:

1
2
s  = g^(ab)
s2 = g^((a+c)b)

Expanding:

1
s2 = g^(ab + bc)

Which equals:

1
s2 = g^(ab) * g^(bc)

Since:

1
g^(bc) = (g^b)^c = B^c

Therefore:

1
s2 = s * B^c mod p

Let:

1
k = B^c mod p

Then:

1
s2 ≡ k * s (mod p)

Substituting the Leaks

Since:

1
2
s = r1 + x
s2 = r2 + y

Substitute into the equation:

1
r2 + y ≡ k(r1 + x) mod p

Rearrange:

1
k x - y ≡ r2 - k r1 (mod p)

Let:

1
D = r2 - k*r1

Then:

1
k x - y ≡ D (mod p)

Which implies:

1
n*p + kx + D = y

Because x and y are small (< 2\^255), we can solve this with LLL lattice reduction.


Lattice Construction

We build the lattice matrix:

1
2
3
[p   0   0]
[k  -1   0]
[D   0   X]

where

1
X = 2^255

Running LLL reveals a short vector containing x and y.


Solver Script (Sage)

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
from sage.all import *
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

p  = 13243062138526201284447970770448985497487835935046195151033172870362190226263571343026139270696938219188495760269911620417720944383208589957958028917476379
B  = 12804907211022889015278628012415679960043121396540707259624421939419807646452519300380372940419262121964832208229011315629370674083955124625987102911578414
r1 = 760378900730138567523985812784164677484768378525866553519207455906967297645062813186975476419680956655829496837046596277141393914017271133326426899480576
c  = 10514128938118845773883292289080487620886740574448840718981396583099184943396464503944071765146807497504664152156644409092109452724863376568702011718657467
r2 = 2997362278023140488456292886638406002550622618115848944703928922011753356376940947542648823845754235109234595450483225545693777310720402100337903455436800

ct_hex = "ee7af60d97f400747d8a7cf609ab64c823b7fb2870fc5dc740927344f223a96837c260731d66b56d8f7206b6de5404f1"
iv_hex = "0f098017c38cffaba366c9a4b9caff79"

X = 2^255

k = power_mod(B, c, p)
D = r2 - k*r1

M = Matrix(ZZ, [
    [p,0,0],
    [k,-1,0],
    [D,0,X]
])

L = M.LLL()

for row in L:
    print(row)

Recovering the Shared Secret

Running the solver yields:

1
s = 760378900730138567523985812784164677484768378525866553519207455906967297645087986355760841037730606778245244662827166878771596680226596338163713703910418

Decrypting the Flag

The AES key is:

1
key = SHA1(str(s))[:16]

Using the provided IV and ciphertext, we decrypt the flag.


Final Flag

1
FlagY{37c37f30fc67f98f376a1c30b25b3969}
This post is licensed under CC BY 4.0 by the author.