⬅ BACK
CryptoCTF 2019 Writeups

Challenges:

  • Crypto
    • Time Capsule
    • roXen

Crypto

Time Capsule

Crypto - 120 solves

You neither need 35 years nor even 20 years to solve this problem!

#!/usr/bin/python

from Crypto.Util.number import *
from secret import flag, n, t, z



def encrypt_time_capsule(msg, n, t, z):
	m = bytes_to_long(msg)
	l = pow(2, pow(2, t), n)
	c = l ^ z ^ m
	return (c, n, t, z) 

print encrypt_time_capsule(flag, n, t, z)
(30263951492003430418944035844723976843761515320480688994488846431636782360488888188067655841720110193942081554547272176290791213962513701884837856823209432209367951673301622535940395295826053396595886942990258678430777333636450042181585837395671842878310404080487115827773100028876775230121509570227303374672524063165714509957850966189605469484201028704363052317830254920108664916139026741331552127849056897534960886647382429202269846392809641322613341548025760209280611758326300214885296175538901366986310471066687700879304860668964595202268317011117634615297226602309205086105573924029744405559823548638486054634428L, 16801166465109052984956796702219479136700692152603640001472470493600002617002298302681832215942994746974878002533318970006820414971818787350153626339308150944829424332670924459749331062287393811934457789103209090873472485865328414154574392274611574654819495894137917800304580119452390318440601827273834522783696472257727329819952363099498446006266115011271978143149347765073211516486037823196033938908784720042927986421555211961923200006343296692217770693318701970436618066568854673260978968978974409802211538011638213976732286150311971354861300195440286582255769421094876667270445809991401456443444265323573485901383L, 6039738711082505929, 13991757597132156574040593242062545731003627107933800388678432418251474177745394167528325524552592875014173967690166427876430087295180152485599151947856471802414472083299904768768434074446565880773029215057131908495627123103779932128807797869164409662146821626628200600678966223382354752280901657213357146668056525234446747959642220954294230018094612469738051942026463767172625588865125393400027831917763819584423585903587577154729283694206436985549513217882666427997109549686825235958909428605247221998366006018410026392446064720747424287400728961283471932279824049509228058334419865822774654587977497006575152095818L)

Solution

If we can calculate l, we can retrieve the flag. Since t is large, calculating l directly as we see it in the given code will be very hard. However, we can use the property from modular arithmetic:

cd(modϕ(n))    acad(modn)c \equiv d \pmod{\phi(n)} \implies a^c \equiv a^d \pmod n

which allows us to calculate l efficiently if we can factorise n. It turns out that n is easily factorised and therefore we can easily compute l:

from functools import reduce
# from factordb.factordb import FactorDB
from Crypto.Util.number import long_to_bytes

flag, n, t, z = (30263951492003430418944035844723976843761515320480688994488846431636782360488888188067655841720110193942081554547272176290791213962513701884837856823209432209367951673301622535940395295826053396595886942990258678430777333636450042181585837395671842878310404080487115827773100028876775230121509570227303374672524063165714509957850966189605469484201028704363052317830254920108664916139026741331552127849056897534960886647382429202269846392809641322613341548025760209280611758326300214885296175538901366986310471066687700879304860668964595202268317011117634615297226602309205086105573924029744405559823548638486054634428, 16801166465109052984956796702219479136700692152603640001472470493600002617002298302681832215942994746974878002533318970006820414971818787350153626339308150944829424332670924459749331062287393811934457789103209090873472485865328414154574392274611574654819495894137917800304580119452390318440601827273834522783696472257727329819952363099498446006266115011271978143149347765073211516486037823196033938908784720042927986421555211961923200006343296692217770693318701970436618066568854673260978968978974409802211538011638213976732286150311971354861300195440286582255769421094876667270445809991401456443444265323573485901383, 6039738711082505929, 13991757597132156574040593242062545731003627107933800388678432418251474177745394167528325524552592875014173967690166427876430087295180152485599151947856471802414472083299904768768434074446565880773029215057131908495627123103779932128807797869164409662146821626628200600678966223382354752280901657213357146668056525234446747959642220954294230018094612469738051942026463767172625588865125393400027831917763819584423585903587577154729283694206436985549513217882666427997109549686825235958909428605247221998366006018410026392446064720747424287400728961283471932279824049509228058334419865822774654587977497006575152095818)

# f = FactorDB(n)
# f.connect()
# fn = f.get_factor_list()
fn = [15013, 583343756982313, 585503197547927, 609245815680559, 612567235432583, 634947980859229, 635224892351513, 639438000563939, 654170414254271, 654269804672441, 667954470985657, 706144068530309, 721443717105973, 737993471695639, 744872496387077, 746232585529679, 795581973851653, 815694637597057, 817224718609627, 841183196554507, 864339847436159, 873021823131881, 884236929660113, 899583643974479, 922745965897867, 942872831732189, 951697329369323, 971274523714349, 1017566110290559, 1018452110902339, 1025985735184171, 1027313536626551, 1059774237802229, 1067609726096989, 1070689247726159, 1079289330417443, 1098516592571807, 1107673252158281, 1108654254305327, 1110918654474373, 1111516996694389, 1112193819715441]
phi = reduce(lambda a, v: (v-1) * a, fn, 1)
l = pow(2, pow(2, t, phi), n)
m = flag ^ l ^ z
print(long_to_bytes(m).decode())
> python ./solve.py
CCTF{_______________________________________________Happy_Birthday_LCS______________________________________________}

roXen

Crypto - 22 solves

Relationship with a cryptographer!

The Girlfriend: All you ever care about is crypto! I am sick of it! It's me or crypto!

The Cryptographer boyfriend: You meant to say it's you XOR cryptography.

The Girlfriend: I am leaving you.

#!/usr/bin/env python

from Crypto.Util.number import *
from secret import exp, flag, nbit

assert exp & (exp + 1) == 0

def adlit(x):
    l = len(bin(x)[2:])
    return (2 ** l - 1) ^ x

def genadlit(nbit):
    while True:
        p = getPrime(nbit)
        q = adlit(p) + 31337
        if isPrime(q):
            return p, q

p, q = genadlit(nbit)
e, n = exp, p * q

c = pow(bytes_to_long(flag), e, n)

print 'n =', hex(n)
print 'c =', hex(c)
n = 0x3ff77ad8783e006b6a2c9857f2f13a9d896297558e7c986c491e30c1a920512a0bad9f07c5569cf998fc35a3071de9d8b0f5ada4f8767b828e35044abce5dcf88f80d1c0a0b682605cce776a184e1bcb8118790fff92dc519d24f998a9c04faf43c434bef6c0fa39a3db7452dc07ccfced9271799f37d91d56b5f21c51651d6a9a41ee5a8af17a2f945fac2b1a0ea98bc70ef0f3e37371c9c7b6f90d3d811212fc80e0abcd5bbefe0c6edb3ca6845ded90677ccd8ff4de2c747b37265fc1250ba9aa89b4fd2bdfb4b4b72a7ff5b5ee67e81fd25027b6cb49db610ec60a05016e125ce0848f2c32bff33eed415a6d227262b338b0d1f3803d83977341c0d3638fL
c = 0x2672cade2272f3024fd2d1984ea1b8e54809977e7a8c70a07e2560f39e6fcce0e292426e28df51492dec67d000d640f3e5b4c6c447845e70d1432a3c816a33da6a276b0baabd0111279c9f267a90333625425b1d73f1cdc254ded2ad54955914824fc99e65b3dea3e365cfb1dce6e025986b2485b6c13ca0ee73c2433cf0ca0265afe42cbf647b5c721a6e51514220bab8fcb9cff570a6922bceb12e9d61115357afe1705bda3c3f0b647ba37711c560b75841135198cc076d0a52c74f9802760c1f881887cc3e50b7e0ff36f0d9fa1bfc66dff717f032c066b555e315cb07e3df13774eaa70b18ea1bb3ea0fd1227d4bac84be2660552d3885c79815baef661L

Solution

We start by analysing the adlit function. All it does is flip the bits of its input, so algebraically, it is equivalent to: adlit(x)=2l1xadlit(x) = 2^l - 1 - x, where ll is the bit length of xx. We can see that this is used to generate the RSA primes; p is generated using getPrime, while q is generated from p using the adlit function. Therefore, we can construct a quadratic equation in p which we can easily solve:

q=2l1p+31337    n=pq=p(2l+31336p)=2lp31336pp2\begin{aligned} q = 2^l - 1 - p + 31337 \implies n = pq &= p(2^l + 31336 - p) \cr &= 2^lp - 31336p - p^2 \end{aligned}

so we get p2+(313362l)p+n=0p^2 + (31336-2^l)p + n = 0

We can guess/bruteforce ll based on the number of bits in nn. The point is that we now know the value of p and can therefore find q and ϕ(n)\phi(n).

The next part of the problem is finding the public exponent e and decrypting the ciphertext properly. We notice that e must satisfy the condition e & (e+1) == 0, which is equivalent to saying that e is one less than a power of 2. We have no guarantee that gcd(e,ϕ(n))=1\gcd(e, \phi(n)) = 1 (we can try it and we won't find the flag, so we know this most likely is not the case).

So we assume that ee and ϕ(n)\phi(n) are not coprime. Let g=gcd(e,ϕ(n))g = \gcd(e, \phi(n)). Then we can let

d(eg)1(modϕ(n))d \equiv (\frac{e}{g})^{-1} \pmod{\phi(n)}

(note that eg\frac{e}{g} is an integer). dd should exist if gcd(eg,ϕ(n))=1\gcd(\frac{e}{g}, \phi(n)) = 1. From this, we get

d×eg1(modϕ(n))d\times\frac{e}{g} \equiv 1 \pmod{\phi(n)}

which implies

d×eg1=kϕ(n)    d=ge(1+kϕ(n))d\times\frac{e}{g} - 1 = k\phi(n) \\ \implies d = \frac{g}{e} (1 + k\phi(n))

Then we perform the calculation:

cd(me)d(modn)med(modn)mege(1+kϕ(n))(modn)mgmgkϕ(n)(modn)mg(mgk)ϕ(n)(modn)mg(modn)\begin{aligned} c^d &\equiv (m^e)^d \pmod n \cr &\equiv m^{ed} \pmod n \cr &\equiv m^{e\frac{g}{e}(1+k\phi(n))} \pmod n \cr &\equiv m^gm^{gk\phi(n)} \pmod n \cr &\equiv m^g(m^{gk})^{\phi(n)} \pmod n \cr &\equiv m^g \pmod n \end{aligned}

The last line follows from Euler's theorem

To find mm, we simply take the ggth root of cdmodnc^d \mod n.

Implementation:

n = 0x3ff77ad8783e006b6a2c9857f2f13a9d896297558e7c986c491e30c1a920512a0bad9f07c5569cf998fc35a3071de9d8b0f5ada4f8767b828e35044abce5dcf88f80d1c0a0b682605cce776a184e1bcb8118790fff92dc519d24f998a9c04faf43c434bef6c0fa39a3db7452dc07ccfced9271799f37d91d56b5f21c51651d6a9a41ee5a8af17a2f945fac2b1a0ea98bc70ef0f3e37371c9c7b6f90d3d811212fc80e0abcd5bbefe0c6edb3ca6845ded90677ccd8ff4de2c747b37265fc1250ba9aa89b4fd2bdfb4b4b72a7ff5b5ee67e81fd25027b6cb49db610ec60a05016e125ce0848f2c32bff33eed415a6d227262b338b0d1f3803d83977341c0d3638f
c = 4853661856538137406464034645906094152913630881745619406489837142176663842103161345726810796014324314815721708473241794990602572186090153630178109119000213575671689095484555801915325918814943882491883912939695163112247489821713563958885663470619257776668644302133854563122818923079903345131503071641985122522971974296790978299728014731290028116679328055229119298349730647468258800098306200875957662303051682669135862889528217767209821797678213735216907093405968738909692976414606843097568371074804700587363228896629583675336567001789346679322477777578268489982160639014959437742274169277165123841304006603130339784289

from gmpy2 import iroot, invert
from Crypto.Util.number import *

def quadratic(a, b, c):
    try:
        (d, t) = iroot(b*b - (4*a*c),2)
        if not t:
            return 0
        return ((-b-d)//(2*a), (-b+d)//(2*a))
    except:
        return 0


for l in range(10,2050):
    p_try = quadratic(1, -(2**l + 31336), n)
    if p_try:
        print('nbit=',l)
        p = p_try[1]
        break

q = n//p
assert(isPrime(int(q)))
assert(isPrime(int(p)))
assert(n == p*q)
print('p=',int(p))
print('q=',int(q))
for e in [int('1'*l, 2) for l in range(1, 50000)]:
    phi = (p-1)*(q-1)
    g = GCD(e, phi)
    if g == 1: continue
    print('trying with e bits:', e.bit_length(), 'gcd=', g)
    d = inverse(e//g, phi)
    m = pow(c, d, n)
    m, t = iroot(m, g)
    if not t: continue
    flag = long_to_bytes(m)
    if b'CCTF' in flag:
        print(flag)
        exit(0)

It turns out e was 3729 bits long!

The flag is: CCTF{it5_3a5y_l1k3_5uNd4y_MOrn1N9}