Writeups
  • UMASS CTF 2024
    • Crypto-Brutal Mogging
    • Crypto - Shuffling as a Service
  • GPN CTF 2024
    • Crypto-Boombox
  • WANICTF 2024
    • Crypto - Replacement
    • Crypto - Easy Calc
    • Crypto - dance
    • Crypto - speedy
    • Crypto - Many Xor Shift
    • Crypto - uf
  • UIUCTF 2024
    • Crypto-Without a Trace
    • Crypto-Determined
    • Crypto-Naptime
    • Crypto-Snore Signatures
    • Crypto-Groups
  • DeadSec CTF 2024
    • Crypto - Raul Rosas
    • Crypto - SSP
    • Crypto - Password Guesser
  • corCTF 2024
    • Crypto - Steps
    • Crypto - Monkfish
    • Crypto - Anglerfish
  • LITCTF 2024
    • Crypto- Symmetric RSA
    • Crypto -Truly Symmetric RSA
  • IrisCTF 2025
    • Crypto - knutsacque
  • UofTCTF 2025
    • Misc - Simple Signing
  • HTB CyberApocalypse
    • Crypto - Copperbox
  • BreachCTF 2025
    • Crypto - Taaffeite Encryption
    • Crypto - Big Stuff
Powered by GitBook
On this page
  1. HTB CyberApocalypse

Crypto - Copperbox

Cedric found a mysterious box made of pure copper in the old archive. He is convinced that it contains the secrets he is looking for, but he is unable to penetrate the metal case. Can you help?

Challenge

import secrets

p = 0x31337313373133731337313373133731337313373133731337313373133732ad
a = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
b = 0xdeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0dedeadc0de

def lcg(x, a, b):
    while True:
        yield (x := a*x + b)

x = int.from_bytes(flag + secrets.token_bytes(30-len(flag)), 'big')
gen = lcg(x, a, b)

h1 = next(gen) * pow(next(gen), -1, p) % p
h2 = next(gen) * pow(next(gen), -1, p) % p

with open('output.txt', 'w') as o:
    trunc = 48
    # oops, i forgot the last part
    o.write(f'hint1 = {h1 >> trunc}\n')
    o.write(f'hint2 = {h2 >> trunc}\n')

This looks like a Truncated LCG question, and the challenge name hints at us to use Coppersmith's method.

The hints that are given to us are not directly truncated outputs, rather a pair of the product of the state and inverse of the next state.

To find the flag, we need to find the seed of the LCG, first we need to see the equations given. Let sis_isi​ denote the states, then

s1=ax+bs2=a2x+ab+bs3=a3x+a2b+ab+bs4=a4x+a3b+a2b+ab+bh1=s1/s2mod  ph2=s3/s4mod  ps_1 = ax + b \\ s_2 = a^2x + ab + b \\ s_3 =a^3x + a^2b + ab + b \\ s_4 = a^4x + a^3b + a^2b + ab + b \\ h_1 = s_1/s_2 \mod p \\ h_2 = s_3/s_4 \mod ps1​=ax+bs2​=a2x+ab+bs3​=a3x+a2b+ab+bs4​=a4x+a3b+a2b+ab+bh1​=s1​/s2​modph2​=s3​/s4​modp

The hints given to us are f(x)=x∗e2piiξxf(x) = x * e^{2 pi i \xi x}f(x)=x∗e2piiξxh1>>48 and h2>> 48, since h1 and h2 are given modulus p, we can recover h1 and h2 using Coppersmith's method. Let's try forming the equations

We need to form an equation with h1 and h2 such that it is 0 mod p.

One approach that we can try is trying to find an approximation to x from h1 and substituting it in h2.

h1=(ax+b)/(a2x+ab+b)mod  pa2h1x+abh1+bh1−ax−b=0mod  px=(b−abh1−bh1)/(a2h1−a) modph_1 = (ax+b)/(a^2x+ab+b) \mod p \\ a^2h_1x+abh_1+bh_1 -ax -b = 0\mod p \\ x = (b - abh_1-bh_1)/(a^2h_1-a) \ mod ph1​=(ax+b)/(a2x+ab+b)modpa2h1​x+abh1​+bh1​−ax−b=0modpx=(b−abh1​−bh1​)/(a2h1​−a) modp

The problem here is that Coppersmith's method doesn't take inverse polynomials. Even if it did, we need to calculate (a2h1−a)−1(a^2h_1-a)^{-1}(a2h1​−a)−1, which is a problem, because inverse calculation needs to precise, even a small change can cause a large change in the inverse, which virtually gives us no information about the approximation of x, even though we have an approximation of h1h_1h1​.

We can consider another approach which doesn't use xxx . To do this, we need to find some relation between h1h1h1 and h2h2h2. We can do this using Groebner basis.

R.<a,b,x,h1,h2> = PolynomialRing(ZZ)
gen = lcg(x, a, b)
f = next(gen) - h1*next(gen)
g = next(gen) - h2*next(gen)
I = Ideal(f,g)
I.elimination_ideal(x)
#Ideal (a^2*b*h1*h2 - a^2*b*h2 + a*b*h1*h2 - a*b*h1 - a*b*h2 + a*b - b*h2 + b) of Multivariate Polynomial Ring in a, b, x, h1, h2 over Integer Ring

Let l1l_1l1​ and l2l_2l2​ be the 48-bits of LSB of h1h_1h1​ and h2h_2h2​

R.<l1,l2> = PolynomialRing(GF(p))
hint1 = 77759147870011250959067600299812670660963056658309113392093130
hint2 = 50608194198883881938583003429122755064581079722494357415324546
h1 = l1 + int(hint1<<48)
h2 = l2 + int(hint2<<48)
h = (a^2*b*h1*h2 - a^2*b*h2 + a*b*h1*h2 - a*b*h1 - a*b*h2 + a*b - b*h2 + b)
roots = coppersmith_multivariate_heuristic(h,[2^48,2^48],1)
k11 = roots[0][0] + int(hint1<<48)
x = GF(p)((-a*b*k11-b*k11+b)*(a^2*k11-a)^-1)
long_to_bytes(int(x))
#b'HTB{sm1th1ng_mY_c0pp3r_fl4G}L\xc6'
PreviousHTB CyberApocalypseNextBreachCTF 2025

Last updated 2 months ago

Now that we have an equation using h1h_1h1​ and h2h_2h2​ , we can use this of Coppersmith's bivariate method and solve the for the complete h1h_1h1​ and h2h_2h2​.

implementation