0%

2024-N1CTF-junior-wp-crypto

记录一下题解,希望自己能快快进步。*代表赛中未做出的题目。

Junior RSA

题目描述:

1
My p and q are generated by an unbreakable algorithm!

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from Crypto.Util.number import *
from secret import flag

m = bytes_to_long(flag)

def gen(bits):
while True:
a = getPrime(bits)
b = getPrime(bits)
c = getPrime(bits)
p = (a << (2*bits)) + (b << bits) + c
q = (c << (2*bits)) + (a << bits) + b
if isPrime(p) and isPrime(q):
break
n = p * q
e = 65537 * a * b * c
return n, e

n, e = gen(256)
enc = pow(m, e, n)
print(f'n = {n}')
print(f'e = {e}')
print(f'enc = {enc}')

"""
n = 1224562468550864572988516321107388462006452125881847529675226398144888628055678744854491489016309262856785169494723943649344959507818155642772331582922466943539371681776924160647697558836379614689120727659593775446187326222964118917872973684996317614900715927751822277949264379149585370840318817143291878609357893969588131470982041272505875501444442064552286330626234504767040724907034678080283717062342383737341651784574675215207283219694413200065153603535550259
e = 47356701171507751941853094934330097161634963503549196148254287987823089762869775349307331223083118848869825102126184149696632299476124764277876323238594318983922914255635452587035212905468593961720866809724369270149104325019013500377581
enc = 307839781648837102719329833689146078918113606357673952357833605392673923316706392387378621203382529480917019155723632239435123748698548640308486267420983085309284306889248702165586731118889200017606360233948688879034822132551452439147516062116990766999765714755923073387252339782026780490661436777023426366620269445376047876173655782230659201893151372247389482285331969025687842851498151565880029377050013378302485301558801016888957357366922840214729734193614497
"""

是一个分解n的RSA题目,而分解的关键在于给出了:

其中a、b、c分别是256bit的素数。

首先需要注意到n的高256位和低256位,其实分别就等于ac乘积的高256位,以及bc乘积的低256位。而对于e来说就分别可以写作:

其中已知量是ach和bcl。

那么下面就分别叙述一下怎么得到b和a。为了得到b,把第一个式子展开:

其实这整个式子就可以看作是一个除数为2^256ach的带余除法,只是有一点细节需要注意:acl与b的乘积并不一定小于除数,但是数量级是相近的,因此最多会有一个很小的误差,所以直接整除过后,再爆破一下这个误差就能得到b。

而对于a来说,展开得到:

这个式子则更加简单,注意到等式两边在模2^256下有:

所以直接求:

就能得到a,然后就有n的完整分解了。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from Crypto.Util.number import *

n = 1224562468550864572988516321107388462006452125881847529675226398144888628055678744854491489016309262856785169494723943649344959507818155642772331582922466943539371681776924160647697558836379614689120727659593775446187326222964118917872973684996317614900715927751822277949264379149585370840318817143291878609357893969588131470982041272505875501444442064552286330626234504767040724907034678080283717062342383737341651784574675215207283219694413200065153603535550259
e = 47356701171507751941853094934330097161634963503549196148254287987823089762869775349307331223083118848869825102126184149696632299476124764277876323238594318983922914255635452587035212905468593961720866809724369270149104325019013500377581
enc = 307839781648837102719329833689146078918113606357673952357833605392673923316706392387378621203382529480917019155723632239435123748698548640308486267420983085309284306889248702165586731118889200017606360233948688879034822132551452439147516062116990766999765714755923073387252339782026780490661436777023426366620269445376047876173655782230659201893151372247389482285331969025687842851498151565880029377050013378302485301558801016888957357366922840214729734193614497

#part1 factor n
bcl = n % 2**256
ach = n // 2**(1024+256) * 2**256

for i in range(10):
b = e // ach // 65537 + i
if(GCD(b,e) != 1):
break

ac = e // 65537 // b
a = inverse(bcl,2**256)*(e//65537%2**256) % 2**256

c = e // a // b // 65537


#part2 get flag
bits = 256

p = (a << (2*bits)) + (b << bits) + c
q = (c << (2*bits)) + (a << bits) + b
phi = (p-1)*(q-1)

d = inverse(e,phi)
print(long_to_bytes(int(pow(enc,d,n))))

#ctfpunk{2ffab167-af57-49d2-ba91-81d9864a98ef}



Masquerade

题目描述:

1
Masquerade🎭! Admission by ticket!

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.PublicKey import DSA
from Crypto.Util.number import *
FLAG = "ctfpunk{XXXX_FAKE_FLAG_XXXX}"

Ticket_Office = DSA.generate(1024).public_key()
Auth_Code = b"ID: @hash_hash & NOTE: L@zy M@n :)"

print(Ticket_Office._key)
Ticket = list(map(int, input("Gimme your ticket: ").split(',')))
assert Auth_Code in long_to_bytes(Ticket[2]) \
and Ticket[2]%int(Ticket_Office._key['p'])

if Ticket_Office._verify(Ticket[2], Ticket[:2]):
print(f"Welcome to the masquerade, Enjoy it! {FLAG}")

题目基于DSA签名,流程也几句话就能说清楚。连上靶机后,给出DSA公钥(y,g,p,q),要求输入签名对(r,s)与消息m,当输入的值满足以下几个条件就可以获得flag:

  • Auth_Code包含在m转化成的字节流中
  • m模p不为0
  • 验签要通过

先找源码看看什么情况吧。源码里的签名与验签是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def _sign(self, m, k):
if not self.has_private():
raise TypeError("DSA public key cannot be used for signing")
if not (1 < k < self.q):
raise ValueError("k is not between 2 and q-1")

x, q, p, g = [self._key[comp] for comp in ['x', 'q', 'p', 'g']]

blind_factor = Integer.random_range(min_inclusive=1,
max_exclusive=q)
inv_blind_k = (blind_factor * k).inverse(q)
blind_x = x * blind_factor

r = pow(g, k, p) % q # r = (g**k mod p) mod q
s = (inv_blind_k * (blind_factor * m + blind_x * r)) % q
return map(int, (r, s))

def _verify(self, m, sig):
r, s = sig
y, q, p, g = [self._key[comp] for comp in ['y', 'q', 'p', 'g']]
if not (0 < r < q) or not (0 < s < q):
return False
w = Integer(s).inverse(q)
u1 = (w * m) % q
u2 = (w * r) % q
v = (pow(g, u1, p) * pow(y, u2, p) % p) % q
return v == r

应该注意到的一点是,他的签名与验签相对于标准的DSA来说,缺少了对m的哈希,那么他的验签过程就可以写成:

也就是说,我们现在要想办法找到一组r,s,m,使得他们满足题目其他条件的同时通过这个验签式。同时要注意_verify的实现里还规定了:

1
2
if not (0 < r < q) or not (0 < s < q):
return False

也就是r和s没有办法简单取0。

先想一下最主要的困难之处是什么,对于DSA的验签操作来说,我们没有他的私钥x,而私钥x满足:

也就是说,只要验签式中还含有y,就不可避免地会需要用到x,所以要想办法从验签式中消去y才对。而最直接的想法就是利用费马小定理:

那么如果把r取成p-1的话就可以消去y,同样的道理也可以利用到g上。但是问题在于r和s必须是0-q的数,所以这是不可能的。这也就同时断了把r取为0的念想。

然后就注意到如果r和s值是相同的话,rs^(-1)在模q下就等于1,而这是肯定可以办到的。与此同时,由于y的指数是1,并没有完全消掉,代回到刚才的验签式中:

那么就发现r取y的时候就刚好可以从等式两侧消去y,更准确地说,r和s应该取y%q,而y和g都是模p下的q阶元素,所以指数上的运算都是正确的。

那么验签式就变成:

y^(-1)已经不太好变了,那么要让这个式子成立,就必须想办法令:

而题目对m的要求是:

  • Auth_Code包含在m转化成的字节流中
  • m模p不为0

而元素g模p下的阶是q,因此我们只要能取到m=kq,就可以使g^m在模p下为1。那么现在的问题就是如何让m=kq的同时,其转化成的字节流还包含Authcode。而这就很简单了,我们记Authcode对应的整数为a,那么对于形如下面的t:

t也就是将a左移多个字节,所以t转化的字节流肯定包含Authcode,同时我们可以在他的低位加任意数字,因为这并不会影响他的高位的字节,所以其实我们只需要找一个满足下面条件的b:

而这直接用一个带余除法就可以做到。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Pwn4Sage.pwn import *
from Crypto.Util.number import *

sh = remote("101.34.72.206",1337)
dict = eval(sh.recvline().strip().decode())
y = dict['y']
g = dict['g']
p = dict['p']
q = dict['q']

Auth_Code = b"ID: @hash_hash & NOTE: L@zy M@n :)"
k = 256**30*bytes_to_long(Auth_Code) // q
r = 256**30*bytes_to_long(Auth_Code) % q
m = (256**30*bytes_to_long(Auth_Code)+(q-r))

sh.sendline((str(y%q)+","+str(y%q)+","+str(m)).encode())
print(sh.recvline())

#ctfpunk{Use_h@sh_to_r3si5t_EF@!!}



*CC@

题目描述:

1
Pursue the ultimate CCA security🔒.

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from Crypto.Util.number import *
from Crypto.PublicKey import ElGamal
from Crypto.Cipher import AES
import os
FLAG = "ctfpunk{XXXX_FAKE_FLAG_XXXX}"
FLAG = bytes_to_long(FLAG[8:-1].encode())

MENU = """
/----------------------------\\
| options |
| [O]racle |
| [G]et FLAG |
\\----------------------------/
"""

class Oracle:
def __init__(self):
self.Cipher0 = ElGamal.generate(512, os.urandom)
self.key = os.urandom(16)
self.T = 100

def Cipher1(self):
return AES.new(self.key, AES.MODE_GCM, nonce=os.urandom(16))

def query(self, data):
assert self.T; self.T -= 1
C = long_to_bytes(self.Cipher0._decrypt(data))
return self.Cipher1().decrypt(C)

sys = Oracle()
print(MENU)
while True:
op = input("> ").lower()
if op == 'o':
data = list(map(int, input("data: ").split(',')))
print(f"[+] {sys.query(data).hex()}")
if op == 'g':
r = getRandomNBitInteger(512)
print(f"[*]\nc = {sys.Cipher0._encrypt(FLAG, r)}\np = {sys.Cipher0.p}")

题目基于elgamal,连接上靶机后,靶机先生成好elgamal的密钥对后进入交互。交互有两个选项:

  • 输入g,会随机生成一个临时密钥r,并给出elgamal对flag的加密结果与模数p。加密结果为:
  • 输入o,可以输入密文,靶机会用elgamal将他解密后,返回AES-GCM模式解密的结果。

现在有最多100次的交互机会,要求还原出flag。

我的思路

对于elgamal这一层并没有多少好说的,显然我们只能先发送g得到flag的密文,然后构造如下的选择密文:

这样靶机elgamal解密后得到的就是km了。但是他会经过GCM解密,所以我们不知道他的具体值。但很有意思的一点是,我们可以获取km的字节长度。

比如我们就令k为1,返回的是21个字节,就说明flag共有21个字节长。同理我们就可以获得任意km的字节长度。但是这有一个前提,那就是km本身不大于p。所以要想获得正确的长度,我们的k是有大小限制的,由于p是64字节的量,flag是21字节,所以k最多就取到43,再往上取,密文就会模p了。

那么要想一想怎么能利用”能看见长度”这一点。我想到了一个类似于二分的思路。要说清楚这个思路要先从下面方式开始阐述:

  • 如果将k从1逐渐增大,那么得到的km也逐渐增大。

  • 当k为256时,得到的km一定是22字节(等价于左移一字节)

  • 这说明,k在1-256之间,一定存在一个值,使得:km 为21字节,(k+1)m为22字节

而对这样的k,又可以用如下等式对他进行描述:

需要注意到,两个不等式其实都只有一侧有用。这就可以划出一个m的范围:

而找到这个k的方法就是用二分。并且k是在每一个形如如下的区间都存在的:

这样就可以对每个区间都二分找到对应的k,然后划出对应的m的范围,然后取出其中范围最小的,就是能界定到的m的最精确的范围了。

然后可以发现m最终的范围是:

1
(152626665263510331007404672103917694625051871477736,152626665263510331007404672115803739317421271516525)

这其实已经可以转成bytes,看到flag的高字节:

1
hnp_m@kes_cc

这就提示预期是hnp,不过hnp的思路我直到比赛结束都没有想到,但是赛后请教了hashhash师傅明白了该怎么做,一会儿会写在后面。

先顺着现在这个思路继续说完。flag一共21个字节,而我们现在有12个字节,还缺少9个字节。不过flag显然是有一定语义的。比如很自然就会觉得接下来的两个字节是:

1
@_

因此其实本质上来说还差七个字节未知,按道理来说可以猜一猜。但是其实并不是非常好猜对(毕竟一直交错的flag本身也是个很奇怪的事情),所以还要想想办法。

我这里的想法是:从2开始遍历素数,当作k。此时我们发送C0,k^(-1)C1,这样我们会得到k^(-1)m模p下的值,而这个时候就会出现两种情况:

  • 如果得到的结果是一个较短的量(十几二十字节),这其实说明k是flag的因子
  • 如果得到的结果接近于乱码(六十多个字节),说明k不是flag的因子

而这对求flag有什么作用呢?我们将已知的14个字节”hnp_m@kes_cc@_”记作a,未知的7个字节记作b。那么flag可以写成:

而假如我们能通过遍历一些小素数的方式,得到flag的一些素因子,记这些素因子乘积为n,那么就有:

就可以直接求得b模n下的值:

而我们缺少7字节,并且由于b应该是可见字符打头,所以b应该在53bit往下,那么只要我们能找到乘积至少有个二三十bit的一些素因子,就可以通过爆破来最终确定最后七个字符了。

但是很遗憾的是,flag能找到的一些小素因子只有2、13、31,五十万以内都没有其他因子了,所以爆破不了。

不过还有最后一手就是硬猜,这也有一些小技巧,比如注意到最后两个bit一定是10,所以绝不会是用感叹号结束,因此大概率是一个长为7的、换了一点字符的单词。然后check的方式就是看满不满足下面的等式:

但是猜了半小时也没猜出任何一个满足条件的,确实还是有点背了哈哈哈。

exp:

binary_search:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from pwn import *
from tqdm import *
from Crypto.Util.number import *

current_num = 0

left = 256**20
right = 256**21

while(1):
current_num += 1
left1 = 256**(current_num-1)
right1 = 256**(current_num)
sh = remote("101.34.72.206",1338)

sh.recvuntil(b"> ")
sh.sendline(b"g")
sh.recvuntil(b"c = ")
c = eval(sh.recvline().strip().decode())
sh.recvuntil(b"p = ")
p = eval(sh.recvline().strip().decode())

for j in trange(100):
temp = (left1+right1)//2
sh.sendline(b"o")
sh.sendline(str(c[0]).encode()+b","+str(temp*c[1]).encode())
sh.recvuntil(b"> data: [+] ")

clen = len(sh.recvline().strip().decode())//2

if(clen == current_num+21):
right1 = temp
else:
left1 = temp
#print(left1,right1)
if(left1 >= right1-1):
templ = 256**(current_num+21-1) // (left1+1)
tempr = 256**(current_num+21-1) // left1
if(left < templ):
left = templ
print("l",left)
if(right > tempr):
right = tempr
print("r",right)
sh.close()
break
sh.close()

#hnp_m@kes_c

getprime:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *
from tqdm import *
from Crypto.Util.number import *
from math import prod
from gmpy2 import next_prime

primes = [2,13,31]

init = 1
while(1):
print(init)
sh = remote("101.34.72.206",1338)

sh.recvuntil(b"> ")
sh.sendline(b"g")
sh.recvuntil(b"c = ")
c = eval(sh.recvline().strip().decode())
sh.recvuntil(b"p = ")
p = eval(sh.recvline().strip().decode())

for j in trange(100):
init = next_prime(init)
sh.sendline(b"o")
sh.sendline(str(c[0]).encode()+b","+str(inverse(init,p)*c[1]).encode())
sh.recvuntil(b"> data: [+] ")

clen = len(sh.recvline().strip().decode())//2
if(clen < 30):
primes.append(init)
print(primes)
sh.close()

奇怪的一点是,我最后整理脚本,完全整理成我的这个二分思路后,发现得不到上面写的那个精确范围了,只能获得前11个字节,更加难猜。

那之前那个范围是怎么二分出来的?也许一个莫名其妙的bug其实指示了一个更合理的二分思路?有兴趣的师傅可以看一看,这是前一个范围的二分脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from pwn import *
from tqdm import *
from Crypto.Util.number import *

left = 256**20
right = 256**21

current_num = 3
left1 = 256**(current_num-1)
right1 = 256**(current_num)
while(1):
current_num += 1
left1 = 256**(current_num-1)
right1 = 256**(current_num)
sh = remote("101.34.72.206",1338)

sh.recvuntil(b"> ")
sh.sendline(b"g")
sh.recvuntil(b"c = ")
c = eval(sh.recvline().strip().decode())
sh.recvuntil(b"p = ")
p = eval(sh.recvline().strip().decode())

for j in trange(100):
temp = (left1+right1)//2
sh.sendline(b"o")
sh.sendline(str(c[0]).encode()+b","+str(temp*c[1]).encode())
sh.recvuntil(b"> data: [+] ")

clen = len(sh.recvline().strip().decode())//2
templ = 256**(clen-1)//temp
tempr = 256**clen//temp
if(clen == current_num+21):
right1 = temp
else:
left1 = temp
if(left < templ):
left = templ
print(left,right)
if(right > tempr):
right = tempr
print(left,right)
sh.close()

#152626665263510331007404672103917694625051871477736
#152626665263510331007404672115803739317421271516525

#hnp_m@kes_cc
#@_easier!!

预期思路(hnp)

构造密文的方式还是一样,发送C0,kC1,得到km模p下的AES-GCM解密结果。

只是有一点不同,这里对k没有任何限制,我们可以任选k发送,但是我们只会取用不足64字节的密文。这样做的意义在于:

中的ci就会是一个最多504bit的小量。而他又可以写成等式:

所以收集多组ki,我们就可以构造如下格:

这个格具有的线性关系是:

然后配一下系数就可以规约出含m的目标向量了。

至于要多少组数据,我用高斯启发式粗略算了一下怎么都要二十组往上才够:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *

p = getPrime(512)

for l in range(5,50):
T = 256^(63-21)
L = Matrix(ZZ,l+1,l+1)
L[0,0] = T
for i in range(l):
L[i+1,i+1] = p

from gmpy2 import *

def guass_Heuristic(L):
n = L.nrows()
efficient = (n/(2*pi*e))**(0.5)
print(l,len(bin(int(efficient*iroot(abs(L.det()),n)[0]))[2:]))

guass_Heuristic(L)

稳妥起见我取了四十组。但是这样做的一个小缺点是,ki随机取的话,那么返回的量也可以近似看作模p下的随机数,那么他小于504bit的机会也只有1/256左右,一般来说需要两三组才能取到一个这样的a。所以要获取几十组数据的话,运气不好可能会比较花时间。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from Pwn4Sage.pwn import *
from tqdm import *
from Crypto.Util.number import *
from random import *

a = []
while(1):
sh = remote("101.34.72.206",1338)

sh.recvuntil(b"> ")
sh.sendline(b"g")
sh.recvuntil(b"c = ")
c = eval(sh.recvline().strip().decode())
sh.recvuntil(b"p = ")
p = eval(sh.recvline().strip().decode())

for j in range(100):
k = randint((p-1)//2,p-1)
sh.sendline(b"o")
sh.sendline(str(c[0]).encode()+b","+str(k*c[1]).encode())
sh.recvuntil(b"> data: [+] ")

clen = len(sh.recvline().strip().decode())//2
if(clen == 63):
a.append((k,p))
sh.close()
print(len(a))
if(len(a) >= 40):
break

T = 256^(63-21)
L = matrix(ZZ,len(a)+1,len(a)+1)
L[0,0] = T
for i in range(len(a)):
L[0,i+1] = a[i][0]
L[i+1,i+1] = a[i][1]
res = L.BKZ()[0]
print(long_to_bytes(abs(int(res[0]//T))))

#ctfpunk{hnp_m@kes_cc@_m0re_e2}

然后求出了flag,看看他的分解:

1
2 · 13 · 31 · 189363108267382544674199345043360000978136712867

好吧,确实死的不冤。猜也是猜不到的,因为虽然意思一样,但我只考虑过easier,并且长度还对不上

(2.6更新)

今天回了趟老家,坐在车上的时候不知道为什么,突然想明白了自己赛中的二分为什么只能出前12个字节。

这是因为,我在每次使用二分的时候,都只用了100次交互机会。而再回顾我们之前的二分划的范围:

这是对第一次二分而言的,此时k的范围在1-256之间,k的含义是使(k+1)m为22字节,km为21字节的k。那么我们这时候用二分来找这样的k,由于256=2^8,最多需要八次。

而对于第二次二分,我们可以得到一个新k,其含义不变,只是这一次我们要找到的是使(k+1)m为23字节,km为22字节的k,因此这一次二分,k的范围在256-256^2,所以二分最多需要16次。

而这样划分范围其实很容易就能明白,其实越大的k其精度就越高,但是二分次数也就需要越多。比如我们如果找到使(k+1)m为43字节,km为42字节的k,得到的那个范围就很小了,也就基本上是m的准确值。但是注意,此时需要找的k在256^21-256^22之间,所以我们需要的二分次数也就最多需要到172次。这其实也就侧面说明,我们交互172次就能得到一个非常精确的flag范围了。

所以,其实我们根本不需要从1-256开始,慢慢的去找256-256^2,256^2-256^3….这每个阶段对应的k(因为越后面精度越高)。而可以直接去二分找到使(k+1)m为43字节,km为42字节的k。虽然一次只能交互100次而一共需要170+次,但问题不大,分两次交互就可以。

所以说,之所以只能出前十二个字节,是因为赛中每一次二分我都最多只用了一次交互。所以超过12个字节之后的范围其实不是精度不够,而是超过了100bit,根本就没二分完、根本就没找到k,所以才不出结果。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from pwn import *
from tqdm import *
from Crypto.Util.number import *

current_num = 23

left1 = 256**(current_num-1)
right1 = 256**(current_num)
while(1):
sh = remote("101.34.72.206",1338)

sh.recvuntil(b"> ")
sh.sendline(b"g")
sh.recvuntil(b"c = ")
c = eval(sh.recvline().strip().decode())
sh.recvuntil(b"p = ")
p = eval(sh.recvline().strip().decode())

for j in trange(100):
temp = (left1+right1)//2
sh.sendline(b"o")
sh.sendline(str(c[0]).encode()+b","+str(temp*c[1]).encode())
sh.recvuntil(b"> data: [+] ")

clen = len(sh.recvline().strip().decode())//2

if(clen == current_num+21):
right1 = temp
else:
left1 = temp

if(left1 >= right1-1):
tempr = 256**(current_num+21-1) // left1
print(long_to_bytes(tempr))
sh.close()
exit()

sh.close()

#hnp_m@kes_cc@_m0re_e2