WaniCTF 2024 Writeup [Crypto全完]
めちゃめちゃ楽しかったです。学びもあってすごくいい回でした。
beginners_rsa [530 solves]
Do you know RSA?
chall.py
from Crypto.Util.number import *
p = getPrime(64)
q = getPrime(64)
r = getPrime(64)
s = getPrime(64)
a = getPrime(64)
n = p*q*r*s*a
e = 0x10001
FLAG = b'FLAG{This_is_a_fake_flag}'
m = bytes_to_long(FLAG)
enc = pow(m, e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'enc = {enc}')
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
e = 65537
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265
from Crypto.Util.number import *
n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347
e = 65537
enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265
p = 9953162929836910171
q = 11771834931016130837
r = 12109985960354612149
s = 13079524394617385153
a = 17129880600534041513
phi = (p-1)*(q-1)*(r-1)*(s-1)*(a-1)
d = pow(e, -1, phi)
m = pow(enc, d, n)
print(long_to_bytes(m))
FLAG{S0_3a5y_1254!!}
beginners_aes [453 solves]
AES is one of the most important encryption methods in our daily lives.
chall.py
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from os import urandom
import hashlib
key = b'the_enc_key_is_'
iv = b'my_great_iv_is_'
key += urandom(1)
iv += urandom(1)
cipher = AES.new(key, AES.MODE_CBC, iv)
FLAG = b'FLAG{This_is_a_dummy_flag}'
flag_hash = hashlib.sha256(FLAG).hexdigest()
msg = pad(FLAG, 16)
enc = cipher.encrypt(msg)
print(f'enc = {enc}') # bytes object
print(f'flag_hash = {flag_hash}') # str object
enc = b'\x16\x97,\xa7\xfb_\xf3\x15.\x87jKRaF&"\xb6\xc4x\xf4.K\xd77j\xe5MLI_y\xd96\xf1$\xc5\xa3\x03\x990Q^\xc0\x17M2\x18'
flag_hash = 6a96111d69e015a07e96dcd141d31e7fc81c4420dbbef75aef5201809093210e
key と iv について、考えうるものが
# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from os import urandom
import hashlib
key = b'the_enc_key_is_'
iv = b'my_great_iv_is_'
key += urandom(1)
iv += urandom(1)
cipher = AES.new(key, AES.MODE_CBC, iv)
FLAG = b'FLAG{This_is_a_dummy_flag}'
flag_hash = hashlib.sha256(FLAG).hexdigest()
msg = pad(FLAG, 16)
enc = cipher.encrypt(msg)
print(f'enc = {enc}')
print(f'flag_hash = {flag_hash}')
replacement [431 solves]
No one can read my diary!
from secret import cal
import hashlib
enc = []
for char in cal:
x = ord(char)
x = hashlib.md5(str(x).encode()).hexdigest()
enc.append(int(x, 16))
with open('my_diary_11_8_Wednesday.txt', 'w') as f:
f.write(str(enc))
my_diary_11_8_Wednesday.txt
は省略。
全ての 1 文字に対して md5.hexdigest() した結果を事前に求めておけばよい。
cs = [] # my_diary_11_8_Wednesday.txt の内容
ms = []
for c in cs:
for i, h in enumerate(hs):
if c == h:
ms.append(i)
print(''.join(chr(x) for x in ms))
# Wednesday, 11/8, clear skies. This morning, I had breakfast at my favorite cafe. Drinking the freshly brewed coffee and savoring the warm buttery toast is the best. Changing the subject, I received an email today with something rather peculiar in it. It contained a mysterious message that said "This is a secret code, so please don't tell anyone. FLAG{13epl4cem3nt}". How strange!
#
# Gureisya
FLAG{13epl4cem3nt}
Easy calc [95 solves]
😆
chall.py
import os
import random
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, getPrime
FLAG = os.getenvb(b"FLAG", b"FAKE{THIS_IS_NOT_THE_FLAG!!!!!!}")
def encrypt(m: bytes, key: int) -> bytes:
iv = os.urandom(16)
key = long_to_bytes(key)
key = md5(key).digest()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
return iv + cipher.encrypt(m)
def f(s, p):
u = 0
for i in range(p):
u += p - i
u *= s
u %= p
return u
p = getPrime(1024)
s = random.randint(1, p - 1)
A = f(s, p)
ciphertext = encrypt(FLAG, s).hex()
print(f"{p = }")
print(f"{A = }")
print(f"{ciphertext = }")
すなわち
ここで、フェルマーの小定理を思い出すと、
となり、これと合致する
solve_s.sage
p = 108159532265181242371960862176089900437183046655107822712736597793129430067645352619047923366465213553080964155205008757015024406041606723580700542617009651237415277095236385696694741342539811786180063943404300498027896890240121098409649537982185247548732754713793214557909539077228488668731016501718242238229
A = 60804426023059829529243916100868813693528686280274100232668009387292986893221484159514697867975996653561494260686110180269479231384753818873838897508257692444056934156009244570713404772622837916262561177765724587140931364577707149626116683828625211736898598854127868638686640564102372517526588283709560663960
F = GF(p)
R.<s> = PolynomialRing(F)
numerator = s
denominator = (1 - s)
polynomial = numerator - A * denominator
roots = polynomial.roots()
for root, multiplicity in roots:
print(f"s = {root}")
solve.py
p = 108159532265181242371960862176089900437183046655107822712736597793129430067645352619047923366465213553080964155205008757015024406041606723580700542617009651237415277095236385696694741342539811786180063943404300498027896890240121098409649537982185247548732754713793214557909539077228488668731016501718242238229
A = 60804426023059829529243916100868813693528686280274100232668009387292986893221484159514697867975996653561494260686110180269479231384753818873838897508257692444056934156009244570713404772622837916262561177765724587140931364577707149626116683828625211736898598854127868638686640564102372517526588283709560663960
ciphertext = '9fb749ef7467a5aff04ec5c751e7dceca4f3386987f252a2fc14a8970ff097a81fcb1a8fbe173465eecb74fb1a843383'
s = 1687413597309293800744215069073564700111464906280803472827126056011788534089081297767956072498777988960765236816249236506915467068679173946738276390422326894133097728262062079690201267739279447117445097896824910096682450843424480831170129071861123241366800056648768192097429858942658575325552357170973141110
import os
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.number import *
def decrypt(c: bytes, key: int) -> bytes:
iv = c[:16]
ciphertext = c[16:]
key = long_to_bytes(key)
key = md5(key).digest()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
decrypted = cipher.decrypt(ciphertext)
return decrypted
m = decrypt(bytes.fromhex(ciphertext), s)
print(m)
FLAG{Do_the_math396691ba7d7270a}
dance [85 solves]
step by step
chall.py
from mycipher import MyCipher
import hashlib
import datetime
import random
isLogged = False
current_user = ''
d = {}
def make_token(data1: str, data2: str):
sha256 = hashlib.sha256()
sha256.update(data1.encode())
right = sha256.hexdigest()[:20]
sha256.update(data2.encode())
left = sha256.hexdigest()[:12]
token = left + right
return token
def main():
print('Welcome to the super secure encryption service!')
while True:
print('Select an option:')
print('1. Register')
print('2. Login')
print('3. Logout')
print('4. Encrypt')
print('5. Exit')
choice = input('> ')
if choice == '1':
Register()
elif choice == '2':
Login()
elif choice == '3':
Logout()
elif choice == '4':
Encrypt()
elif choice == '5':
print('Goodbye!')
break
else:
print('Invalid choice')
def Register():
global d
username = input('Enter username: ')
if username in d:
print('Username already exists')
return
dt_now = datetime.datetime.now()
minutes = dt_now.minute
sec = dt_now.second
print(minutes, sec)
data1 = f'user: {username}, {minutes}:{sec}'
data2 = f'{username}'+str(random.randint(0, 10))
d[username] = make_token(data1, data2)
print('Registered successfully!')
print('Your token is:', d[username])
return
def Login():
global isLogged
global d
global current_user
username = input('Enter username: ')
if username not in d:
print('Username does not exist')
return
token = input('Enter token: ')
if d[username] != token:
print('Invalid token')
return
isLogged = True
current_user = username
print(f'Logged in successfully! Hi {username}!')
return
def Logout():
global isLogged
global current_user
isLogged = False
current_user = ''
print('Logged out successfully!')
return
def Encrypt():
global isLogged
global current_user
if not isLogged:
print('You need to login first')
return
token = d[current_user]
sha256 = hashlib.sha256()
sha256.update(token.encode())
key = sha256.hexdigest()[:32]
nonce = token[:12]
cipher = MyCipher(key.encode(), nonce.encode())
plaintext = input('Enter plaintext: ')
ciphertext = cipher.encrypt(plaintext.encode())
print('username:', current_user)
print('Ciphertext:', ciphertext.hex())
return
if __name__ == '__main__':
main()
username = 'gureisya'
ciphertext = '061ff06da6fbf8efcd2ca0c1d3b236aede3f5d4b6e8ea24179'
MyCipher
等は省略。共通鍵暗号の一種だった。
data1 = f'user: {username}, {minutes}:{sec}'
data2 = f'{username}'+str(random.randint(0, 10))
の部分に着目すると、key や nonce などを生成している data1, data2 としてありうるパターンが
solve.py
from mycipher import MyCipher
import hashlib
import datetime
import random
username = 'gureisya'
ciphertext = bytes.fromhex('061ff06da6fbf8efcd2ca0c1d3b236aede3f5d4b6e8ea24179')
def make_token(data1: str, data2: str):
sha256 = hashlib.sha256()
sha256.update(data1.encode())
right = sha256.hexdigest()[:20]
sha256.update(data2.encode())
left = sha256.hexdigest()[:12]
token = left + right
return token
for minutes in range(60):
for sec in range(60):
for n in range(10):
data1 = f'user: {username}, {minutes}:{sec}'
data2 = f'{username}'+str(random.randint(0, 10))
token = make_token(data1, data2)
sha256 = hashlib.sha256()
sha256.update(token.encode())
key = sha256.hexdigest()[:32]
nonce = token[:12]
cipher = MyCipher(key.encode(), nonce.encode())
plaintext = cipher.encrypt(ciphertext)
if plaintext.startswith(b'FLAG'):
print(plaintext)
FLAG{d4nc3_l0b0t_d4nc3!!}
speedy [60 solves]
I made a super speedy keystream cipher!!
chall.py
from cipher import MyCipher
from Crypto.Util.number import *
from Crypto.Util.Padding import *
import os
s0 = bytes_to_long(os.urandom(8))
s1 = bytes_to_long(os.urandom(8))
cipher = MyCipher(s0, s1)
secret = b'FLAG{'+b'*'*19+b'}'
pt = pad(secret, 8)
ct = cipher.encrypt(pt)
print(f'ct = {ct}')
cipher.py
from Crypto.Util.number import *
from Crypto.Util.Padding import *
def rotl(x, y):
x &= 0xFFFFFFFFFFFFFFFF
return ((x << y) | (x >> (64 - y))) & 0xFFFFFFFFFFFFFFFF
class MyCipher:
def __init__(self, s0, s1):
self.X = s0
self.Y = s1
self.mod = 0xFFFFFFFFFFFFFFFF
self.BLOCK_SIZE = 8
def get_key_stream(self):
s0 = self.X
s1 = self.Y
sum = (s0 + s1) & self.mod
s1 ^= s0
key = []
for _ in range(8):
key.append(sum & 0xFF)
sum >>= 8
self.X = (rotl(s0, 24) ^ s1 ^ (s1 << 16)) & self.mod
self.Y = rotl(s1, 37) & self.mod
return key
def encrypt(self, pt: bytes):
ct = b''
for i in range(0, len(pt), self.BLOCK_SIZE):
ct += long_to_bytes(self.X)
key = self.get_key_stream()
block = pt[i:i+self.BLOCK_SIZE]
ct += bytes([block[j] ^ key[j] for j in range(len(block))])
return ct
また、flag の長さはパディングも含めると get_key_stream()
は系4回行われていることが分かる。
そして、毎ラウンドの
どうにかしてそれぞれの key を求めたい。
1回目について、最終的な self.X = (rotl(s0, 24) ^ s1 ^ (s1 << 16)) & self.mod
について式を追ってみると、以下の図のような計算を行っていることが分かる。
というように、順番に
肝心の
最後の }\x07\x07\x07\x07\x07\x07\x07
なのが事前にわかっているため、心配する必要は無い。
solve.py
from Crypto.Util.number import *
ct = b'"G:F\xfe\x8f\xb0<O\xc0\x91\xc8\xa6\x96\xc5\xf7N\xc7n\xaf8\x1c,\xcb\xebY<z\xd7\xd8\xc0-\x08\x8d\xe9\x9e\xd8\xa51\xa8\xfbp\x8f\xd4\x13\xf5m\x8f\x02\xa3\xa9\x9e\xb7\xbb\xaf\xbd\xb9\xdf&Y3\xf3\x80\xb8'
def rotl(x, y):
x &= 0xFFFFFFFFFFFFFFFF
return ((x << y) | (x >> (64 - y))) & 0xFFFFFFFFFFFFFFFF
def get_key(X: int, nxt_X: int):
X_rot24 = rotl(X, 24)
S1_0 = (X_rot24 & 0x000000000000FFFF)^(nxt_X & 0x000000000000FFFF)
S1_1 = (X_rot24 & 0x00000000FFFF0000)^(nxt_X & 0x00000000FFFF0000)^(S1_0 << 16)
S1_2 = (X_rot24 & 0x0000FFFF00000000)^(nxt_X & 0x0000FFFF00000000)^(S1_1 << 16)
S1_3 = (X_rot24 & 0xFFFF000000000000)^(nxt_X & 0xFFFF000000000000)^(S1_2 << 16)
S1_new = S1_0 + S1_1 + S1_2 + S1_3
S1 = S1_new^X
sum = (X+S1) & 0xFFFFFFFFFFFFFFFF
key = []
for _ in range(8):
key.append(sum & 0xFF)
sum >>= 8
return key
pt = b''
for i in range(0, 48, 16):
X = bytes_to_long(ct[i:i+8])
nxt_X = bytes_to_long(ct[i+16:i+24])
key = get_key(X, nxt_X)
block = ct[i+8:i+16]
pt += bytes([block[j] ^ key[j] for j in range(8)])
pt += b'}'
print(pt)
FLAG{x013_ro74te_5hif7!!}
Many Xor Shift [29 solves]
はるか昔の記憶
Memories of long long ago.
chall.py
FLAG = b'FAKE{XXXXXXXXXXXXXXXXXXXXXX}'
N = 7
M = 17005450388330379
WORD_SIZE = 32
WORD_MASK = (1 << WORD_SIZE) - 1
def encrypt(m):
state = [int.from_bytes(m[i:i+4]) for i in range(0, len(m), 4)]
assert len(state) == N
def xor_shift():
nonlocal state
t = state[0] ^ ((state[0] << 11) & WORD_MASK)
for i in range(N-1):
state[i] = state[i+1]
state[-1] = (state[-1] ^ (state[-1] >> 19)) ^ (t ^ (t >> 8))
for _ in range(M):
xor_shift()
return state
print("N = ", N)
print("M = ", M)
print("WORD_SIZE = ", WORD_SIZE)
print("state = ", encrypt(FLAG))
N = 7
M = 17005450388330379
WORD_SIZE = 32
state = [1927245640, 871031439, 789877080, 4042398809, 3950816575, 2366948739, 935819524]
state を列ベクトルとみなし、操作がどのような行列になっているかを考える。
例えば、以下の操作を考えてみる。
for i in range(N-1):
state[i] = state[i+1]
これは、以下のような行列で表すことができる。
同様に、xor_shift についても、各要素の
これで行列を準備することができた。逆行列をかけることによって、
solve.sage
N = 7
M = 17005450388330379
WORD_SIZE = 32
state = [1927245640, 871031439, 789877080, 4042398809, 3950816575, 2366948739, 935819524]
import numpy as np
V = np.matrix([[0] for j in range(N*WORD_SIZE)])
for j in range(N):
for k in range(WORD_SIZE):
V[WORD_SIZE*j + k] = state[j] >> k & 1
A = np.zeros((N*WORD_SIZE, N*WORD_SIZE))
# shift
for i in range((N-1)*WORD_SIZE):
A[i][i+WORD_SIZE] += 1
# 0
for i in range(WORD_SIZE):
A[(N-1)*WORD_SIZE+i][i] = 1
for i in range(11, WORD_SIZE, 1):
A[(N-1)*WORD_SIZE+i][i-11] = 1
for i in range(8, WORD_SIZE, 1):
A[(N-1)*WORD_SIZE+i-8][i] = 1
for i in range(11, WORD_SIZE, 1):
A[(N-1)*WORD_SIZE+i-8][i-11] = 1
# 6
for i in range(WORD_SIZE):
A[(N-1)*WORD_SIZE+i][(N-1)*WORD_SIZE+i] = 1
for i in range(19, WORD_SIZE, 1):
A[(N-1)*WORD_SIZE+i-19][(N-1)*WORD_SIZE+i] = 1
MS = MatrixSpace(GF(2), N*WORD_SIZE, N*WORD_SIZE)
A = MS(A)
def power(a, k):
ret = np.identity(N*WORD_SIZE)
ret = MS(ret)
while k:
if k & 1:
ret = ret * a
a = a * a
k >>= 1
return ret
inv_A = A^(-1)
inv = power(inv_A, M)
begin_state_v = inv*V
begin_state = [0 for _ in range(N)]
for i in range(N):
for j in range(WORD_SIZE):
if begin_state_v[i*WORD_SIZE + j]:
begin_state[i] += 1 << j
from Crypto.Util.number import *
print(b''.join(long_to_bytes(x) for x in begin_state))
FLAG{m47r1x_!n_8inary_w0rld}
uf [14 solves]
🙄
chall.py
import os
from secrets import randbits
from Crypto.Util.number import bytes_to_long
FLAG = os.environb.get(b"FLAG", b"FAKE{THIS_IS_DUMMY_FLAG}")
m = bytes_to_long(FLAG)
assert m.bit_length() >= 512
def encrypt(m: int, n: int = 512) -> int:
x = 0
for i in range(n):
x <<= 1
x += m * randbits(1)
if i >= n // 2:
x ^= randbits(1)
return x
X = [encrypt(m) for _ in range(4)]
print(X)
output.txt
[6643852762092641655051592752286380661448697120839285262713138738793179330857521051418707355387198243788554658967735136760757552410466512939791351078152197994352930016306075464400264019640466277732596022216246131141036813931972036259910390741311141390889450882074162723823607552591155184799627590418587536982033939537563823, 4495106960532238798978878322218382764459613684889887356979907395021294655849239390809608204284927849117763119933285899077777162943233437728643056322845118660545730870443735090094400144586494098834221418487123653668703665085461676013454922344247818407399456870636622800919629442727075235809213114639237367651539678560390951, 7622226387024225267485603541284038981214490586915816777231024576546652676746968149372915915975325662783469952634025859954515971134032563991925283958708572235632178937041656690377178266198211581176947491463237398083133658483056792368618417698027992083481412961301906342594056438180675328433412539805240307255787971167535638, 1149407465454162408488208063367931363888120160126632926627929705372269921465081968665764846439238807939361247987642326885758277171318666479752274577607727935160689442316433824450832192798328252739495913920016290902086534688608562545166349970831960156036289570935410160077618096614135121287858428753273136461851339553609896]
「
「
そういえば、誤差込みでの計算が得意な
頑張って紹介されている論文を見たりしながら、行列を作って計算させた。
solve.py
x = [6643852762092641655051592752286380661448697120839285262713138738793179330857521051418707355387198243788554658967735136760757552410466512939791351078152197994352930016306075464400264019640466277732596022216246131141036813931972036259910390741311141390889450882074162723823607552591155184799627590418587536982033939537563823, 4495106960532238798978878322218382764459613684889887356979907395021294655849239390809608204284927849117763119933285899077777162943233437728643056322845118660545730870443735090094400144586494098834221418487123653668703665085461676013454922344247818407399456870636622800919629442727075235809213114639237367651539678560390951, 7622226387024225267485603541284038981214490586915816777231024576546652676746968149372915915975325662783469952634025859954515971134032563991925283958708572235632178937041656690377178266198211581176947491463237398083133658483056792368618417698027992083481412961301906342594056438180675328433412539805240307255787971167535638, 1149407465454162408488208063367931363888120160126632926627929705372269921465081968665764846439238807939361247987642326885758277171318666479752274577607727935160689442316433824450832192798328252739495913920016290902086534688608562545166349970831960156036289570935410160077618096614135121287858428753273136461851339553609896]
rho = 256
x_zero = x[0]
K = 1 << (rho+1)
M = matrix([
[K, x[0], x[1], x[2], x[3]],
[0, -x_zero, 0, 0, 0],
[0, 0, -x_zero, 0, 0],
[0, 0, 0, -x_zero, 0],
[0, 0, 0, 0, -x_zero],
])
A = M.LLL()
guess_m = []
for i in range(5):
q_zero = A[i][0] // K
if q_zero == 0:
continue
r_zero = x_zero % q_zero
p = (x_zero-r_zero) // q_zero
p = abs(p)
guess_m.append(p)
from Crypto.Util.number import *
for x in guess_m:
print(long_to_bytes(x))
FLAG{hope_this_chal_is_not_automatically_solved_by_AI_c14ef1732e87a6c}
初めて LLL を使った問題が解けて、とっても嬉しい。
Cheat Code [44 solves]
チートがあれば何でもできる
You can do anything with cheats.
server.py
from hashlib import sha256
import os
from secrets import randbelow
from secret import flag, cheat_code
import re
challenge_times = 100
hash_strength = int(os.environ.get("HASH_STRENGTH", 10000))
def super_strong_hash(s: str) -> bytes:
sb = s.encode()
for _ in range(hash_strength):
sb = sha256(sb).digest()
return sb
cheat_code_hash = super_strong_hash(cheat_code)
print(f"hash of cheat code: {cheat_code_hash.hex()}")
print("If you know the cheat code, you will always be accepted!")
secret_number = randbelow(10**10)
secret_code = f"{secret_number:010d}"
print(f"Find the secret code of 10 digits in {challenge_times} challenges!")
def check_code(given_secret_code, given_cheat_code):
def check_cheat_code(given_cheat_code):
return super_strong_hash(given_cheat_code) == cheat_code_hash
digit_is_correct = []
for i in range(10):
digit_is_correct.append(given_secret_code[i] == secret_code[i] or check_cheat_code(given_cheat_code))
return all(digit_is_correct)
given_cheat_code = input("Enter the cheat code: ")
if len(given_cheat_code) > 50:
print("Too long!")
exit(1)
for i in range(challenge_times):
print(f"=====Challenge {i+1:03d}=====")
given_secret_code = input("Enter the secret code: ")
if not re.match(r"^\d{10}$", given_secret_code):
print("Wrong format!")
exit(1)
if check_code(given_secret_code, given_cheat_code):
print("Correct!")
print(flag)
exit(0)
else:
print("Wrong!")
print("Game over!")
怪しい計算をしているところを探してみると
for i in range(10):
digit_is_correct.append(given_secret_code[i] == secret_code[i] or check_cheat_code(given_cheat_code))
わざわざ check_cheat_code()
を
各桁について
※プログラムはチームメイトに作ってもらいました。
solve.py
from pwn import *
import time
server = 'chal-lz56g6.wanictf.org'
port = 5000
def find_correct_digit(current_code, position):
fastest_time = float('inf')
correct_digit = 0
for i in range(10):
test_code = current_code[:position] + str(i) + current_code[position+1:]
conn.recvuntil(b"Enter the secret code: ")
start_time = time.time()
conn.sendline(test_code.encode())
response = conn.recvline()
elapsed_time = time.time() - start_time
if b"Correct" in response:
print(conn.recvuntil(b"}").decode())
conn.close()
return test_code, True
if elapsed_time < fastest_time:
fastest_time = elapsed_time
correct_digit = i
return current_code[:position] + str(correct_digit) + current_code[position+1:], False
code = "0000000000"
conn = remote(server, port)
print(conn.recvuntil(b"Enter the cheat code: "))
conn.sendline(b"pwn")
for position in range(10):
code, found = find_correct_digit(code, position)
print(f"{position}: {code}")
if found:
break
print(f"Found code: {code}")
FLAG{t1m!ng_a774ck_1s_f34rfu1}
Discussion