team-logo
Published on

Nullcon Goa HackIM 2025 - CRYPTO challenges

Authors

Table of contents

Next-Level

alt text

Writeup author: zBlxst

This challenge is an implementation of Multi-RSA.

p = number.getPrime(512)
q = nextprime(p)
r = nextprime(q)

Then, the three primes are very close to each other, and therefore to the cube root of n.

from sympy import integer_nthroot
from Crypto.Util.number import long_to_bytes

# nc 52.59.124.14 5028

n = #
c = #


# Be sure that the base is odd to keep (base +- i) odd
base = integer_nthroot(n, 3)[0] | 1 

primes = []
i = 0
while len(primes) != 3:
    if n % (base - i) == 0:
        primes.append(base - i)
    if n % (base + i) == 0:
        primes.append(base + i)
    i += 2
    
p = primes[0]
q = primes[1]
r = primes[2]

e = 0x10001
phi = (p-1)*(q-1)*(r-1)
d = pow(e, -1, phi)
m = pow(c, d, n)

print(long_to_bytes(m).decode(), end="")

Flag: ENO{1_l0ve_7h3_pr1me_numb3r_the0r3m}

Registration

alt text

Writeup author: zBlxst

This challenge is a weird implementation of an RSA signature. The public key is (a, e, n) where a is a random integer. The private key is d. Then, the signature of h (the sha256 of the message actually) is pow(self.a, h * self.d, self.n) With 2 signed messages we can recover pow(a, d, n) by the Bezout Theorem.

from pwn import *
from egcd import egcd
from hashlib import sha256
from Crypto.Util import number


# ctx = process(["python3", "chall.py"])
ctx = remote("52.59.124.14", 5026)

begin = ctx.recvuntil(b"> ").split(b"\n")
n = int(begin[0].decode().split(" ")[2])
a = int(begin[1].decode().split(" ")[2])
e = int(begin[2].decode().split(" ")[2])

ctx.sendline(b"1")
answer = ctx.recvuntil(b"> ").split(b"\n")
c1 = bytes.fromhex(answer[0].decode().split(" ")[1])
s1 = int(answer[1].decode().split(" ")[1])
h1 = number.bytes_to_long(sha256(c1).digest())


ctx.sendline(b"1")
answer = ctx.recvuntil(b"> ").split(b"\n")
c2 = bytes.fromhex(answer[0].decode().split(" ")[1])
s2 = int(answer[1].decode().split(" ")[1])
h2 = number.bytes_to_long(sha256(c2).digest())

gcd, u, v = egcd(h1, h2)
assert gcd == 1 # Just restart if it's the case
print(u*h1 + v*h2)

ctx.sendline(b"2")
answer = ctx.recvuntil(b"Signature: ").split(b"\n")
c3 = bytes.fromhex(answer[0].decode().split(" ")[1])
h3 = number.bytes_to_long(sha256(c3).digest())


to_send = pow(pow(s1, u, n)*pow(s2, v, n), h3, n)

ctx.sendline(str(to_send).encode())

ctx.interactive()

Flag: ENO{ha5h_ev3ryth1ng_th3y_s4id_s00o_s3cur3}

Matrixfun

Matrixfun

Writeup author: zBlxst

Just solution:

from base64 import b64decode
import pickle
from sage.all import *
import hashlib
import numpy as np

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import sympy


p, g, order = pickle.loads(b64decode(bytes.fromhex("6741535677774541414141414141434b43586b76713079793733665141497757626e567463486b7558324e76636d55756258567364476c68636e4a68655a534d444639795a574e76626e4e30636e566a644a53546c497746626e567463486d556a4164755a474679636d46356c4a4f55537743466c454d42597053486c464b554b4573425377524c4249615561414f4d425752306558426c6c4a4f556a414a504f4a534a694965555570516f53774f4d41587955546b354f53762f2f2f2f394b2f2f2f2f2f30732f644a5269695632554b49775363336c7463486b75593239795a533575645731695a584a7a6c497748535735305a57646c637053546c496f49466d487751774d7a66334b466c4947556142574b43475a576b594f7557667070685a53426c47675669676d4d58776a3665704131764143466c4947556142574b434251436142757569517878685a53426c47675669676a6268794b4d344b494b52495755675a526f46596f4a523873786a4c586931616f41685a53426c4767566967695254534446724b7835576f5755675a526f46596f494c4b4d5145373543655175466c4947556142574b434f64474d694477704e4d66685a53426c47675669676a4933672f354e4173344d345755675a526f46596f4a775032476747523275707341685a53426c47675669676734676e785555314c6c56595755675a526f46596f4963616151323379597a7a36466c4947556142574b434f784e7a576e4a75423169685a53426c476756696768344c7244772b37422b4a6f5755675a526f46596f49766962587067476646794f466c4947555a585355596f6f476f78434e66697373683551750a")))
A = pickle.loads(b64decode(bytes.fromhex("6741535673514541414141414141434d466d3531625842354c6c396a62334a6c4c6d31316248527059584a7959586d556a417866636d566a6232357a64484a31593353556b35534d42573531625842356c497748626d5268636e4a68655a53546c457341685a524441574b55683552536c43684c41557345537753476c4767446a41566b64486c775a5a53546c497743547a6955695969486c464b554b4573446a4146386c45354f546b722f2f2f2f2f53762f2f2f2f394c50335355596f6c646c43694d456e4e35625842354c6d4e76636d5575626e5674596d56796335534d42306c756447566e5a584b556b35534b434754676e752f596b2b4269685a53426c476756696767357248547a50725743596f5755675a526f46596f496d4837556d52694d6e5557466c4947556142574b43466c54787761676c723875685a53426c47675669676b4a3337486c38553471735143466c4947556142574b4341754b59445a71784f734f685a53426c47675669676b6a315667794f3178577a7743466c4947556142574b43634c64395743667432693741495755675a526f46596f4a49326a4c7333436975356741685a53426c47675669676a2b4a306a5a62624d6a57495755675a526f46596f4a786b4e4751563733785a4541685a53426c476756696769705347656274796166436f5755675a526f46596f49656736532f786d67317a65466c4947556142574b43492b6d502b4a38352f5132685a53426c47675669676d7973642b2b5647786f7a7743466c4947556142574b43484b5654496b6b6e49396c685a53426c4756306c474975")))
B, iv, cipher = pickle.loads(b64decode(bytes.fromhex("674153562b774541414141414141434d466d3531625842354c6c396a62334a6c4c6d31316248527059584a7959586d556a417866636d566a6232357a64484a31593353556b35534d42573531625842356c497748626d5268636e4a68655a53546c457341685a524441574b55683552536c43684c41557345537753476c4767446a41566b64486c775a5a53546c497743547a6955695969486c464b554b4573446a4146386c45354f546b722f2f2f2f2f53762f2f2f2f394c50335355596f6c646c43694d456e4e35625842354c6d4e76636d5575626e5674596d56796335534d42306c756447566e5a584b556b35534b434e54533962432b774d6f52685a53426c47675669676b34597077734c79494e714143466c4947556142574b4361457878666f6f69586d6141495755675a526f46596f49784f75634b65346c313347466c4947556142574b4353775345584762494f484141495755675a526f46596f4978714e43717841504a4379466c4947556142574b435049305a6773436f726f35685a53426c47675669676b314f56676b34673957725143466c4947556142574b435a414c62346e597648584e41495755675a526f46596f4a556d364f783744744b4d3441685a53426c4767566967696f6538672b323941694b6f5755675a526f46596f49426c4332356b646d4a782b466c4947556142574b4362376b5a6550574636616741495755675a526f46596f4a322b744469714448544c5141685a53426c4767566967694c6e394d514d497a6665595755675a526f46596f49755744466a564878535379466c4947555a585355596b4d517551594678773346344e54537130574d6b386f2b4c4a52444d4e647957473465482f694c583733436f76644331736a385558724d4858624f4f41557473616f6d6146444e4a6b497974396b4e4b446f64493370766c395a566a3553486c43343d0a")))

# Define _F the Field and F it's extension
_F = GF(p)
F = GF(p**2)

# Take the matrices in the extension to get the Jordan Form
g = matrix(F, 4, list(map(int, g.flatten())))
gj, mult = g.jordan_form(transformation=True)

A = matrix(F, 4, list(map(int, A.flatten())))
Aj = ~mult*A*mult

# Discrete log
a = Aj[0,0].log(gj[0,0])


# Recover the key
B = matrix(_F, 4, list(map(int, B.flatten())))
K = B**a


# Do magic to format the key
new_K = [0]*16
for i in range(16):
    new_K[i] = int(str(K[i//4, i%4]))
K = new_K

print(K)
K = list(map(sympy.core.numbers.Integer, K))
print(K)
K = np.ndarray((4, 4), buffer=np.array(K), dtype=sympy.core.numbers.Integer)
print(K)

def dec(key: bytes, iv: bytes, ciphertext: bytes) -> str:
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    padded_data = decryptor.update(ciphertext) + decryptor.finalize()
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    plaintext = unpadder.update(padded_data) + unpadder.finalize()
    return plaintext.decode()

# FLAGGGGGG
print(iv)
print(cipher)

h = hashlib.sha256()
h.update(str(K).encode())
digest = h.digest()

print(dec(digest, iv, cipher))

Flag: ENO{dlp_0n_g3n3r4l_l1n34r_gr0up_wuuuuu7}

Odd Bacon

odd-bacon

Writeup author: zBlxst

Adapted https://github.com/inmcm/Simon_Speck_Ciphers C code.

pip install simonspeckciphers

from speck import SpeckCipher
from pwn import xor

cipher = SpeckCipher(0x0123456789abcdef, key_size = 64, block_size = 32)

k1 = bytes.fromhex("448a636a")
k2 = bytes.fromhex("7e4ab787")

flag = bytes.fromhex("9afd50ec81c67033c8f77a1b8a68c916bcffeaeec6d0c9cb306a0fb8866c6ceab0205c6e472c427f52c8b353")
for i in range(len(flag)//4):
    encrypted = int.from_bytes(xor(flag[4*i:4*i+4], k2), 'big')
    decrypted = cipher.decrypt(encrypted).to_bytes(4, 'big')
    print(xor(decrypted, k1).decode(), end="")
print()

Flag: ENO{3ven_m3ns0ur_re3l1y_1s_s3cur3_f0r_4_PRF}

Many Caesars

challenge description

Writeup author: ppp45

We are given a program written in Python and its output:

import string
import re

text = open('text.txt','r').read().lower()
flag = open('flag.txt','r').read().strip()[4:-1].replace('_','+')
chars = string.ascii_letters + string.digits + '+/='
regex = re.compile('[' + chars + ']{5,70}')
assert re.fullmatch(regex, flag)

def caesar(msg, shift):
	return ''.join(chars[(chars.index(c) + shift) % len(chars)] for c in msg)

i = 0
count = 0
while i < len(text):
	if text[i] not in string.ascii_lowercase:
		print(text[i], end = '')
		i += 1
	else:
		j = i
		while text[j] in string.ascii_lowercase: j += 1
		print(caesar(text[i:j], chars.index(flag[count % len(flag)])), end = '')
		count += 1
		i = j
AtvDxK lAopjz /i + vhw c6 uwnshnuqjx ymfy kymhi Kyv 47+3l/eh Bs kpfkxkfwcnu Als 9phdgj9 +ka ymzuBGxmFq 6fdglk8i CICDowC, sjxir bjme+pfwfkd 6li=fj=kp, nCplEtGtEJ, lyo qeb INKLNBM vm ademb7697. ollqba lq DitCmA xzhm fx ef7dd7ii, wIvv eggiww GB kphqtocvkqp, 3d6 MAx ilsplm /d rpfkd vnloov hc nruwtAj xDxyjrx vexliv KyrE +3hc Gurz, jcemgt ixlmgw 9f7gmj5/9k obpmlkpf/ib mzp 8k/=64c ECo sj qb=eklildv. =k loGznlEpD qzC qo+kpm+obk=v, vHEEtuHKtMBHG, huk h7if75j/d9 mofs+=v, zkloh lqAkwCzioqvo rfqnhntzx fhynAnynjx b/a7 JKvrCzEx hexe BE ecwukpi 63c397. MAxLx wypujpwslz 3/c ql irvwhu 9bbcj1h9cb fsi f tswmxmzi zDGrtK ed FBpvrGL vjtqwij ixlmgep 5f8 =lkpqor=qfsb tmowuzs.

Each word of text is encrypted with Caesar cipher and each corresponding character of flag serves as a shift parameter. The flag is relatively short, so we can work out these parameters manually.

words = ct.replace(".", "").replace(",", "").split()

for i, word in enumerate(words, start=1):
    print(f"\n{i = }\t{word = }")
    for shift in range(len(chars)):
        tmp = caesar(word, -shift)
        print(shift, tmp, sep="\t")

The above script prints the following data for each word of the ciphertext:

i = 1	word = 'AtvDxK'
0	AtvDxK
1	zsuCwJ
2	yrtBvI
3	xqsAuH
4	wprztG
5	voqysF
6	unpxrE
7	tmowqD
8	slnvpC
9	rkmuoB
10	qjltnA
11	piksmz
12	ohjrly
13	ngiqkx
14	mfhpjw
15	legoiv
16	kdfnhu
17	jcemgt
18	ibdlfs
19	hacker
20	g=bjdq
21	f/aicp
22	e+=hbo
23	d9/gan
24	c8+f=m
25	b79e/l
26	a68d+k
27	=57c9j
28	/46b8i
29	+35a7h
30	924=6g
31	813/5f
32	702+4e
33	6Z193d
34	5Y082c
35	4XZ71b
36	3WY60a
37	2VX5Z=
38	1UW4Y/
39	0TV3X+
40	ZSU2W9
41	YRT1V8
42	XQS0U7
43	WPRZT6
44	VOQYS5
45	UNPXR4
46	TMOWQ3
47	SLNVP2
48	RKMUO1
49	QJLTN0
50	PIKSMZ
51	OHJRLY
52	NGIQKX
53	MFHPJW
54	LEGOIV
55	KDFNHU
56	JCEMGT
57	IBDLFS
58	HACKER
59	GzBJDQ
60	FyAICP
61	ExzHBO
62	DwyGAN
63	CvxFzM
64	BuwEyL

We know that text is lowercase and we assume it comprises English words. Hence, 19 hacker contains the shift parameter and the first original word. Using this method we recover full plaintext and the flag.

hacker ethics is a set of principles that guide the behavior of individuals who explore and manipulate computer systems often emphasizing curiosity creativity and the pursuit of knowledge rooted in values such as openness free access to information and the belief in using skills to improve systems rather than harm them hacker ethics encourages responsible and ethical use of technology it advocates for transparency collaboration and respecting privacy while discouraging malicious activities like stealing data or causing damage these principles aim to foster innovation and a positive impact on society through ethical and constructive hacking

Flag: ENO{th3_d1ffer3nce5_m4ke_4ll_th3_diff3renc3}

Coinflip

challenge description

Writeup author: ppp45

We are given the source code:

#!/usr/bin/env python3
import os
import sys
from Crypto.Util.number import bytes_to_long, getRandomNBitInteger
import math

flag = open('flag','r').read().strip()
N = 64

def log(*err_messages):
	'''function for debugging purposes'''
	logs = open('err.log','a')
	for msg in err_messages:
		if type(msg) == bytes: msg = hexlify(msg).decode()
		logs.write(msg)
	logs.write('\n=====\n')
	logs.close()

class CRG(object):
	"""Cubic Random Generator"""

	def __init__(self, n):
		'''n - bitlength of state'''
		self.n = n
		self.m = getRandomNBitInteger(n)
		while True:
			self.a = bytes_to_long(os.urandom(n >> 3)) % self.m # n/8 bytes
			if math.gcd(self.a, self.m) == 1: break
		while True:
			self.state = bytes_to_long(os.urandom(n >> 3)) % self.m # n/8 bytes
			if math.gcd(self.state, self.m) == 1: break
		self.buffer = []

	def next(self):
		if self.buffer == []:
			self.buffer = [int(bit) for bit in bin(self.state)[2:].zfill(self.n)]
			self.state = self.a * pow(self.state, 3, self.m) % self.m
			#log('new state: ', self.state)
		return self.buffer.pop(0)

def loop():
	balance = 2
	coin = ['head','tails']
	crg = CRG(N)
	while True:
		if balance == 0:
			print('I do not talk to broke people.')
			return
		if balance >= 1000000000:
			print(f'Wow, here is your flag: {flag}')
			return
		print(f'How much do you want to bet? (you have {balance})')
		sys.stdout.flush()
		amount = int(sys.stdin.buffer.readline().strip())
		if amount > balance or amount <= 0:
			print('Ugh, cheater!')
			return
		print('What is your bet?')
		sys.stdout.flush()
		bet = sys.stdin.buffer.readline().strip().decode()
		if bet == coin[crg.next()]:
			print('you win')
			balance += amount
		else:
			print('you lose')
			balance -= amount

if __name__ == '__main__':
	try:
		loop()
	except Exception as err:
		print('Something went wrong')
		log('ERROR: ', repr(err))

Our goal is to predict coinflips and reach balance of at least billion points. In order to do that, we need to recover generator's a, m and state.

Knowing those values we can calculate the next state:

new_state = a * pow(state, 3, m) % m

Recovering modulus m is the first step. Given consecutive states x, y and z we can write equations:

y = a * x^3 mod m

z = a * y^3 mod m

After eliminating a we have:

y^4 - z * x^3 = 0 mod m

which means that expression y^4 - z * x^3 divisible by m. In order to recover m, we take a few more state triples and calculate greatest common divisors of those expressions.

Knowing two consecutive states and m we can calculate a using modular division.

We start with 2 points. To recover a single state we have to observe N (64) coinflips. If we manage to win a few initial rounds (placing bets 2, 4, 8, 16 and 32) we should be able to easily recover six consecutive states (384 coinflips in total).

The following script allows us to win the game and get the flag.

#!/usr/bin/python3

from math import gcd
import pwn

pwn.context.log_level = "warn"

HOST = "52.59.124.14"
PORT = 5032

COIN = [b"head", b"tails"]

initial_bets = [2, 4, 8, 16, 32]
bets = initial_bets + [1] * (6 * 64 - len(initial_bets))

data_collected = False
attempt = 0

while not data_collected:
    attempt += 1
    print(f"{'-'*64}\n{attempt = }")

    # conn = pwn.process(["python3", "server.py"])
    conn = pwn.remote(HOST, PORT)

    hist = ""

    for bet in bets:
        resp = conn.recvline().strip().decode("utf-8")
        # print(resp)
        if "broke" in resp:
            print("UNLUCKY")
            break

        conn.sendline(str(bet).encode("utf-8"))
        conn.recvline()

        # 0-first is much more common
        choice = 0
        conn.sendline(COIN[choice])
        resp = conn.recvline().strip().decode("utf-8")
        # print(resp)

        if "win" in resp:
            correct_choice = choice
        else:
            correct_choice = int(not choice)
        hist += str(correct_choice)

    else:
        data_collected = True

    print(f"{len(hist) = }")


states = [int(hist[64 * i : 64 * (i + 1)], 2) for i in range(6)]
s1, s2, s3, s4, s5, s6 = states


d1 = s2**4 - s1**3 * s3
d2 = s3**4 - s2**3 * s4
d3 = s4**4 - s3**3 * s5
d4 = s5**4 - s4**3 * s6


m = gcd(d1, d2, d3, d4)

inv = pow(s1**3, -1, m)
a = s2 * inv % m


s7 = a * pow(s6, 3, m) % m

print(f"{m = }")
print(f"{a = }")
print(f"{s7 = }")


choices = bin(s7)[2:].zfill(64)

bets = [2**k for k in range(2, 30)]

for bet, choice in zip(bets, choices):
    resp = conn.recvline().strip().decode("utf-8")
    print(resp)

    conn.sendline(str(bet).encode("utf-8"))
    conn.recvline()

    conn.sendline(COIN[int(choice)])
    resp = conn.recvline().strip().decode("utf-8")
    print(resp)

conn.interactive()

Flag: ENO{1nfin1t3_r1che5_3re_y0ur5_1t_s33m5}

Kleinvieh

alt text

Writeup author: Grzechu

Step 1:

from math import isqrt

# Given values
n = 123478096241280364670962652250405187135677205589718111459493149962577739081187795982860395854714430939628907753414209475535232237859888263943995193440085650470423977781096613357495769010922395819095023507620908240797541546863744965624796522452543464875196533943396427785995290939050936636955447563027745679377
c = 77628487658893896220661847757290784292662262378387512724956478473883885554341297046249919230536341773341256727418777179462763043017367869438255024390966651705078565690271228162236626313519640870358976726577499711921457546321449494612008358074930154972571393221926233201707908214569445622263631145131680881658
strange = 11519395324733889428998199861620021305356608571856051121451410451257032517261285528888324473164306329355782680120640320262135517302025844260832350017955127625053351256653287330703220294568460211384842833586028123185201232184080106340230097212868897257794101622865852490355812546172336607114197297201223620901

# Step 1: Find s such that (s - 1)^2 ≡ strange mod n
# We solve (s - 1)^2 = strange + k * n for some integer k
# We test small values of k until (s - 1)^2 is a perfect square

found = False
for k in range(1000):  # Adjust the range if necessary
    target = strange + k * n
    if target < 0:
        continue  # Skip negative targets
    s_minus_1 = isqrt(target)
    if s_minus_1 * s_minus_1 == target:
        s = s_minus_1 + 1
        found = True
        break

if not found:
    raise ValueError("Could not find s. Try increasing the range for k.")

Step 2:

# Step 2: Solve for p and q using the quadratic equation
# x^2 - s x + n = 0
discriminant = s * s - 4 * n
if discriminant < 0:
    raise ValueError("Discriminant is negative. No real roots.")

sqrt_discriminant = isqrt(discriminant)
if sqrt_discriminant * sqrt_discriminant != discriminant:
    raise ValueError("Discriminant is not a perfect square.")

p = (s + sqrt_discriminant) // 2
q = (s - sqrt_discriminant) // 2

# Step 3: Verify p and q
if p * q != n:
    raise ValueError("p and q do not multiply to n.")

# Step 4: Compute phi and the private key d
phi = (p - 1) * (q - 1)
e = 65537  # Standard RSA exponent
d = pow(e, -1, phi)

# Step 5: Decrypt the ciphertext
flag = pow(c, d, n)

# Convert the flag to bytes
flag_bytes = flag.to_bytes((flag.bit_length() + 7) // 8, byteorder='big')
print(flag_bytes.decode())

Flag: ENO{4_b1t_0f_ph1_4nd_a_bi1_0f_bru13_f0rc3_br3ak5_1t}

Kleinvieh 2

alt text

Writeup author: zBlxst

This challenge implements 5 RSA encryptions by dividing the flag in 5 parts (which are really small). Note that the public exponent e is 3.

1st part:

def encrypt1(message : bytes, key):
	return pow(bytes_to_long(message), key.e, key.n)

Here, message < cbrt(n), so pow(message, e, n) == pow(message, e).

clears[0] = integer_nthroot(encrypted[0], 3)[0]

2nd part:

def encrypt2(message : bytes, key):
	r = #large number
	return pow(bytes_to_long(message) * r, key.e, key.n)

Here, (message * r) ^ e == message^e * r^e. So by dividing by r^e, when recover the small message.

clears[1] = integer_nthroot((encrypted[1] * pow(r, -e, n)) % n, 3)[0]

3rd part:

def encrypt3(message : bytes, key):
	bytelength = int(math.floor(math.log2(key.n))) // 8
	msg = message + b'\x00' * (bytelength - len(message))
	return pow(bytes_to_long(msg), key.e, key.n)

Adding one zero at the end of the message is the same as multiplying it by 256. So we do the same as before.

clears[2] = integer_nthroot((encrypted[2] * pow(pow(2, 8*(bytelength-lengths)),
                             -e, n)) % n, 3)[0]

4th part:

def encrypt4(message : bytes, key):
	bytelength = int(math.floor(math.log2(key.n))) // 8
	msg = message * (bytelength // len(message))
	return pow(bytes_to_long(msg), key.e, key.n)

Again, repeating the message (say of size S) is like multiplying by 10^S + 1 (just like 4545 = 45*101 = 45 * (10^2 + 1) for example).

multiplier = 0
for i in range(bytelength // lengths):
    multiplier += (2**lengths)**(8*i)

clears[3] = integer_nthroot((encrypted[3] * pow(multiplier, -e, n)) % n, 3)[0]

5th part:

def encrypt5(message : bytes, key):
	bytelength = int(math.floor(math.log2(key.n))) // 8
	msg = b'\x42' * (bytelength - len(message)) + message
	return pow(bytes_to_long(msg), key.e, key.n)

This time it's harder, but the attack from https://github.com/jvdsn/crypto-attacks/blob/master/attacks/rsa/stereotyped_message.py works really well. But basically, it's just finding small roots of (0x4242..420000..00 + m)^e - c in Z/nZ.

Flag: ENO{s0m3_0f_th35e_me1hod5_4ctua1ly_h4d_th3_sam3_1de3_th4t_i5_why_1t_i5_ju5t_1_ch4ll3nge}