配布ファイルは以下の通り。
assert(len(open('flag.txt', 'rb').read()) <= 50)
assert(str(int.from_bytes(open('flag.txt', 'rb').read(), byteorder='big') << 10000).endswith('1002773875431658367671665822006771085816631054109509173556585546508965236428620487083647585179992085437922318783218149808537210712780660412301729655917441546549321914516504576'))
フラグを整数値に変換して左に10000ビットシフトした値の末尾175桁の値が与えられる。すなわち
からflagを復元するのが本問の趣旨である。
正しくは
を考えて、オイラーの定理より
を計算して
よりflagを得る。
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'))
答えはTSGCTF{0K4y_Y0U_are_r3aDy_t0_Go_aNd_dO_M0r3_CryPt}
である。
とするところまでは同じ。
において、上なら偶数で下なら奇数であることを利用すると、
def divide_by_two(x):
if x % 2 == 0:
return x // 2
else:
return (x + mod) // 2
のようにして2で割る操作を実現できる。
これを10000回繰り返せばflagを得られる。
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'))
とおく (modが無いことに注意)。
いま
とおける。
ここで、
いま、
より、
よって、
flagは
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)