Try   HackMD

TSG CTF 2020 Beginner's Crypto

Problem

The problem is given in the form of the follewing file:

assert(len(open('flag.txt', 'rb').read()) <= 50)
assert(str(int.from_bytes(open('flag.txt', 'rb').read(), byteorder='big') << 10000).endswith('1002773875431658367671665822006771085816631054109509173556585546508965236428620487083647585179992085437922318783218149808537210712780660412301729655917441546549321914516504576'))

The flag is converted to an integer value and shifted 10,000 bits to the left to give the last 175 decimal digits of the value. In other words, the purpose of this problem is to restore the flag value from:

c=flag210000mod10175

Solution

At first you may try taking the inverse of

210000 over
F10175
, but that should be impossible because they are not coprime.

The correct way is, first think of

c=flag210000mod5175

and, using Euler’s theorem, calculate:

(210000)1mod5175=(210000)φ(5175)1=(210000)517551741

and get the flag by:

flag=c(210000)1mod5175

Since

25650<5175, this is the only value that can satisfy the equation.

Solver Script

c = 1002773875431658367671665822006771085816631054109509173556585546508965236428620487083647585179992085437922318783218149808537210712780660412301729655917441546549321914516504576
mod = 5 ** 175
phi = 5 ** 175 - 5 ** 174
inv = pow(pow(2, 10000, mod), phi - 1, mod)
print(((c * inv) % mod).to_bytes(50, byteorder='big'))

The answer is TSGCTF{0K4y_Y0U_are_r3aDy_t0_Go_aNd_dO_M0r3_CryPt}.

Alternative Solution #1 (by @naan112358)

Let's start with

c=flag210000mod5175

for the previous solution.

By considering:

2x={2x  (2x<M)2xM (2xM)modM

we can assume that the above case produces even number and the below case produces odd number. So,

def divide_by_two(x):
    if x % 2 == 0:
        return x // 2
    else:
        return (x + mod) // 2

simulates division by 2 over the given modulo.

We can repeat this 10000 times and get the flag.

Solver Script

mod = 5 ** 175
c = 1002773875431658367671665822006771085816631054109509173556585546508965236428620487083647585179992085437922318783218149808537210712780660412301729655917441546549321914516504576 % mod

def divide_by_two(x):
    if x % 2 == 0:
        return x // 2
    else:
        return (x + mod) // 2

for i in range(10000):
    c = divide_by_two(c)

print(c.to_bytes(50, byteorder='big'))

Alternative Solution #2 (by @satos___jp)

Now we put:

C=flag210000

Note that there's no modulo here.

On the other hand we know

c, which is the last 175 digits of
C
, so we can put:

C=c010175+c

Now we want to calculate

c0 which makes the last 10000 digits of the binary form of
C
all zero.

Then consider

C, which can be obtained by replacing
c0
to
c0=c0+2x
. Now we get:

C=c010175+c=(c0+2x)10175+c=c010175+c+2x10175=C+51752x+175

And the last

x+175 digits of the binary form of
C
is unchanged.

So, by determining

c0 from the least significant bits, we can obtain
C
such that all the last 10000bit is zero.

Flag can be obtained by right-shifting

C 10000bits.

Solver Script

c = 1002773875431658367671665822006771085816631054109509173556585546508965236428620487083647585179992085437922318783218149808537210712780660412301729655917441546549321914516504576
c0 = 0
bit = 1
mask = 1 << 175
while '1' in bin(c0 * 10 ** 175 + c)[-10000:]:
  if (c0 * 10 ** 175 + c) & mask != 0:
    c0 += bit
  bit <<= 1
  mask <<= 1
C = c0 * 10 ** 175 + c
flag = (C >> 10000).to_bytes(50, byteorder='big')
print(flag)