Try   HackMD

CryptoHack Writeup

INTRODUCTION

1. Finding Flags

Each challenge is designed to help introduce you to a new piece of cryptography. Solving a challenge will require you to find a "flag".

These flags will usually be in the format crypto{y0ur_f1rst_fl4g}. The flag format helps you verify that you found the correct solution.

Try submitting this into the form below to solve your first challenge.

Writeup:
Flag: crypto{y0ur_f1rst_fl4g}

MATHEMATICS

> Qadratic Residues

Quadratic residual
An integer is called quadratic residual if..

a^2=x % p ——> (a^2 - x)=k*p ——> [1]

p-integer modulo
where Zp={0..p-1} and Z*p={set of integers where GCD(p, Zp)==1}

Now solve Eq-1
Here, Z*p are "a" values


p=29
ints = [14, 6, 11]
from Crypto.Util.number import *
a_val=[i for i in range(0,p-1) if GCD(p,i)==1]

for i in a_val:
    for j in ints:
        for k in range(0,100):
            if (pow(i,2)-j)==k*p:
               print(i)

#We later go for 8 as it is the smallest among all.

> Legendre Symbol

Legendre Symbol obeys these following rules,

(a/p)=1 if a is a quadratic residue and a ≢ 0 mod p
(a/p)=1
if a is a quadratic non-residue mod p
(a/p)=0
if a ≡ 0 mod p

Where (a / p) == (a^(p-1)/2) % p, this will give you all the Legendre's Symbols

The prime supplied obeys p = 3 mod 4, which allows us easily compute the square root.
So from this, we can take (p+1/4).
(a/p) = a**(p+1/4) mod p —→ for p = 3(mod 4)

According to Tonelli–Shanks algorithm, p = 3%4 this makes the computation much easier. Tonelli factorization doesn’t work on composite( Non-Prime ) number.

p = 101524035174539890485408575671085261788758965189060164484385690801466167356667036677932998889725476582421738788500738738503134356158197247473850273565349249573867251280253564698939768700489401960767007716413932851838937641880157263936985954881657889497583485535527613578457628399173971810541670838543309159139

ints = [25081841204695904475894082974192007718642931811040324543182130088804239047149283334700530600468528298920930150221871666297194395061462592781551275161695411167049544771049769000895119729307495913024360169904315078028798025169985966732789207320203861858234048872508633514498384390497048416012928086480326832803, 45471765180330439060504647480621449634904192839383897212809808339619841633826534856109999027962620381874878086991125854247108359699799913776917227058286090426484548349388138935504299609200377899052716663351188664096302672712078508601311725863678223874157861163196340391008634419348573975841578359355931590555, 17364140182001694956465593533200623738590196990236340894554145562517924989208719245429557645254953527658049246737589538280332010533027062477684237933221198639948938784244510469138826808187365678322547992099715229218615475923754896960363138890331502811292427146595752813297603265829581292183917027983351121325, 14388109104985808487337749876058284426747816961971581447380608277949200244660381570568531129775053684256071819837294436069133592772543582735985855506250660938574234958754211349215293281645205354069970790155237033436065434572020652955666855773232074749487007626050323967496732359278657193580493324467258802863, 4379499308310772821004090447650785095356643590411706358119239166662089428685562719233435615196994728767593223519226235062647670077854687031681041462632566890129595506430188602238753450337691441293042716909901692570971955078924699306873191983953501093343423248482960643055943413031768521782634679536276233318, 85256449776780591202928235662805033201684571648990042997557084658000067050672130152734911919581661523957075992761662315262685030115255938352540032297113615687815976039390537716707854569980516690246592112936796917504034711418465442893323439490171095447109457355598873230115172636184525449905022174536414781771, 50576597458517451578431293746926099486388286246142012476814190030935689430726042810458344828563913001012415702876199708216875020997112089693759638454900092580746638631062117961876611545851157613835724635005253792316142379239047654392970415343694657580353333217547079551304961116837545648785312490665576832987, 96868738830341112368094632337476840272563704408573054404213766500407517251810212494515862176356916912627172280446141202661640191237336568731069327906100896178776245311689857997012187599140875912026589672629935267844696976980890380730867520071059572350667913710344648377601017758188404474812654737363275994871, 4881261656846638800623549662943393234361061827128610120046315649707078244180313661063004390750821317096754282796876479695558644108492317407662131441224257537276274962372021273583478509416358764706098471849536036184924640593888902859441388472856822541452041181244337124767666161645827145408781917658423571721, 18237936726367556664171427575475596460727369368246286138804284742124256700367133250078608537129877968287885457417957868580553371999414227484737603688992620953200143688061024092623556471053006464123205133894607923801371986027458274343737860395496260538663183193877539815179246700525865152165600985105257601565]

from Crypto.Util.number import *

for a in ints:
   if pow(a,(p-1)//2,p)==1:
     non_res=a
     print(non_res)

#Now Finding roots of the non residuel value
new_val=pow(non_res,(p+1)//4,p)
print(new_val)

> Chinese Remainder Theorem

#import  gmpy2

'''
Given the following set of linear congruences:

x ≡ 2 mod 5
x ≡ 3 mod 11
x ≡ 5 mod 17

Find the integer a such that x ≡ a mod 935
'''

from Crypto.Util.number import *

def Chinese(n, N, a):
    result = 0

    for i in range(len(n)):
        ai = a[i]
        ni = n[i]
        bi = N // ni

        result += ai * bi * inverse(bi, ni)

    return result % N

N=935
n=[5,11,17]
a=[2,3,5]
result=Chinese(n,N,a)
print(result)

Brainteaser Part 1

> Successive Powers

from Crypto.Util.number import *
import gmpy2

t=[588, 665, 216, 113, 642, 4, 836, 114, 851, 492, 819, 237]

pval=max(t)+1

for p in range(pval,1000000):
   try:
      x=[ (t[i] * inverse(t[i-1]) ,p)%p for i in range(1000) ]
      if len(set(x))==1:
        print(p,x)
   except:
      pass

> Broken RSA

import Crypto.Util.number as cun
from pprint import pprint

def roots_of_unity(e, phi, n, rounds=500):
    # Divide common factors of `phi` and `e` until they're coprime.
    phi_coprime = phi
    while cun.GCD(phi_coprime, e) != 1:
        phi_coprime //= cun.GCD(phi_coprime, e)

    # Don't know how many roots of unity there are, so just try and collect a bunch
    roots = set(pow(i, phi_coprime, n) for i in range(1, rounds))

    assert all(pow(root, e, n) == 1 for root in roots)
    return roots, phi_coprime

e = 16
n=27772857409875257529415990911214211975844307184430241451899407838750503024323367895540981606586709985980003435082116995888017731426634845808624796292507989171497629109450825818587383112280639037484593490692935998202437639626747133650990603333094513531505209954273004473567193235535061942991750932725808679249964667090723480397916715320876867803719301313440005075056481203859010490836599717523664197112053206745235908610484907715210436413015546671034478367679465233737115549451849810421017181842615880836253875862101545582922437858358265964489786463923280312860843031914516061327752183283528015684588796400861331354873
c = 11303174761894431146735697569489134747234975144162172162401674567273034831391936916397234068346115459134602443963604063679379285919302225719050193590179240191429612072131629779948379821039610415099784351073443218911356328815458050694493726951231241096695626477586428880220528001269746547018741237131741255022371957489462380305100634600499204435763201371188769446054925748151987175656677342779043435047048130599123081581036362712208692748034620245590448762406543804069935873123161582756799517226666835316588896306926659321054276507714414876684738121421124177324568084533020088172040422767194971217814466953837590498718

# Problem: e and phi are not coprime - d does not exist
phi = n-1 # there were no prime factors of n

# Find e'th roots of unity modulo n
roots, phi_coprime = roots_of_unity(e, phi, n)

# Use our `phi_coprime` to get one possible plaintext
d = pow(e, -1, phi_coprime)
m = pow(c, d, n)
assert pow(m, e, n) == c

# Use the roots of unity to get all other possible plaintexts
ms = [(m * root) % n for root in roots]
ms = [cun.long_to_bytes(m) for m in ms]
pprint(ms)

SYMMETRIC CIPHERS

> ECB CBC WTF

In the given challenge the given mode encryption was in CBC but decryption with ECB.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

ciphertext = iv.hex() + encrypted.hex() The generated ciphertext in my case was "cb30b6a41ea0302796321785baa60db2af7989205ddcbac9a207e202970885d8a275d6e53e3effcf527bf9bbf1cf87a1"

We see the length of the ciphertext to be 96 in hexadecimal, 48 in bytes where Initialization vector (iv) length is of 16 and the remaining is the actual ciphertext.

iv=ciphertext[:16]

ct=ciphetext[16:]

Now decrypt the first 16 bytes ct block with ECB mode and then xor with iv, this is the first part of the plaintext and the second 16 bytes block will be decrypted with ECB mode and that text is now xored with the first ct block resulting in the 2nd plaintext.

plaintext=p1+p2


Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Ciphertext

{"cookie":"29ba24b9e53b8f4852ce8daa7fbad6de8cf8813d64f394306815baa49b7c3fc323c646c6455fbf3b920455962deb81d9"}

Source Code

from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta

KEY = ?
FLAG = ?

@chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv):
    cookie = bytes.fromhex(cookie)
    iv = bytes.fromhex(iv)

    try:
        cipher = AES.new(KEY, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(cookie)
        unpadded = unpad(decrypted, 16)
    except ValueError as e:
        return {"error": str(e)}

    if b"admin=True" in unpadded.split(b";"):
        return {"flag": FLAG}
    else:
        return {"error": "Only admin can read the flag"}

@chal.route('/flipping_cookie/get_cookie/')
def get_cookie():
    expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
    cookie = f"admin=False;expiry={expires_at}".encode()

    iv = os.urandom(16)
    padded = pad(cookie, 16)
    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(padded)
    ciphertext = iv.hex() + encrypted.hex()

    return {"cookie": ciphertext}

> Lazy CBC

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Ciphertext

ct="339451173da40bb3f2599e0adcb474759c89a29d7eb5ece460037fd4158c3cd8af8f76d0164ebe3b676b28e1eff93508”

Source Code

from Crypto.Cipher import AES

KEY = ?
FLAG = ?

@chal.route('/lazy_cbc/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)
    if len(plaintext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    encrypted = cipher.encrypt(plaintext)

    return {"ciphertext": encrypted.hex()}

@chal.route('/lazy_cbc/get_flag/<key>/')
def get_flag(key):
    key = bytes.fromhex(key)

    if key == KEY:
        return {"plaintext": FLAG.encode().hex()}
    else:
        return {"error": "invalid key"}

@chal.route('/lazy_cbc/receive/<ciphertext>/')
def receive(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)
    if len(ciphertext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    decrypted = cipher.decrypt(ciphertext)

    try:
        decrypted.decode() # ensure plaintext is valid ascii
    except UnicodeDecodeError:
        return {"error": "Invalid plaintext: " + decrypted.hex()}

    return {"success": "Your message has been received"}

Attack

Given the conditions above we can exploit this in the following way:

  1. Make a plaintext with a length of *at least* 3 blocks
  2. Encrypt the plaintext and get the resulting ciphertext
  3. Modify the second block of the ciphertext to *contain only zeros*
  4. Modify the third block of the ciphertext to be *the same as the first block*
  5. Decrypt the ciphertext and get the *invalid plaintext* result
  6. XOR the first and third blocks of the *invalid plaintext*
  7. That’s our key!

As IV = Key let’s say, Pt1 = Key ^ Decrypt(Ct1)

Pt2 = Ct1 ^ Decrypt(Ct2)

Pt3 = Ct2 ^ Decrypt(Ct3)

If we make Ct2 = 0 [Block] and C3 = C1 then,

Pt1 = Key ^ Decrypt(Ct1)

Pt2 = Ct1 ^ Decrypt(0)

Pt3 = 0 ^ Decrypt(Ct1)

Therefore, Key = XOR(Pt1,Pt3)

ct="339451173da40bb3f2599e0adcb474759c89a29d7eb5ece460037fd4158c3cd8af8f76d0164ebe3b676b28e1eff93508"
ct=ct.encode()
b1=ct[:32]
b2='0'*32.encode()

new_ct=b1+b2+b1
print("New fake Cipher: ", new_ct)
#after decryption of new_ct we get a plaintext
pt="61616161616161616161616161616161ff2f3668967735aa1d493ff1f1c4ff39c5153f87bbf70ac3705e5c4ffd5e7a14"

pt1=pt[:32]
pt3=pt[64:]

key=xor(bytes.fromhex(pt1),bytes.fromhex(p3)).hex()
print("Key:", key)

#use this key to get the FLAG

> Triple DES

Source Code

from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad

IV = os.urandom(8)
FLAG = ?

def xor(a, b):
    # xor 2 bytestrings, repeating the 2nd one if necessary
    return bytes(x ^ y for x,y in zip(a, b * (1 + len(a) // len(b))))

@chal.route('/triple_des/encrypt/<key>/<plaintext>/')
def encrypt(key, plaintext):
    try:
        key = bytes.fromhex(key)
        plaintext = bytes.fromhex(plaintext)
        plaintext = xor(plaintext, IV)

        cipher = DES3.new(key, DES3.MODE_ECB)
        ciphertext = cipher.encrypt(plaintext)
        ciphertext = xor(ciphertext, IV)

        return {"ciphertext": ciphertext.hex()}

    except ValueError as e:
        return {"error": str(e)}

@chal.route('/triple_des/encrypt_flag/<key>/')
def encrypt_flag(key):
    return encrypt(key, pad(FLAG.encode(), 8).hex())

There is a serious Vulnerability with DES, it is vulnerable to Weak Keys .

This vulnerability was discovered when the key was brute forced within a day or two in 1990s.

Let me demonstrate by solving this challenge.

There are various weak keys that can be used for attack, not necessary to use my key alone.

Attack

The key that I used to encrypt was “0101010101010101FEFEFEFEFEFEFEFE".

Now we obtained the cipher text {"ciphertext":"63727970746f7b6e30745f346c6c5f6b3379735f3472335f673030645f6b3379737d060606060606"}

Use the same ciphertext to and key to encrypt again, this gives us the FLAG.

> Symmetry

Source Code

from Crypto.Cipher import AES

KEY = ?
FLAG = ?

@chal.route('/symmetry/encrypt/<plaintext>/<iv>/')
def encrypt(plaintext, iv):
    plaintext = bytes.fromhex(plaintext)
    iv = bytes.fromhex(iv)
    if len(iv) != 16:
        return {"error": "IV length must be 16"}

    cipher = AES.new(KEY, AES.MODE_OFB, iv)
    encrypted = cipher.encrypt(plaintext)
    ciphertext = encrypted.hex()

    return {"ciphertext": ciphertext}

@chal.route('/symmetry/encrypt_flag/')
def encrypt_flag():
    iv = os.urandom(16)

    cipher = AES.new(KEY, AES.MODE_OFB, iv)
    encrypted = cipher.encrypt(FLAG.encode())
    ciphertext = iv.hex() + encrypted.hex()

    return {"ciphertext": ciphertext}

AES OFB mode is symmetrical cipher, so just by using the same IV and Ciphertext we can get the Flag.

> Bean Counter

Source Code

from Crypto.Cipher import AES

KEY = ?

class StepUpCounter(object):
    def __init__(self, value=os.urandom(16), step_up=False):
        self.value = value.hex()
        self.step = 1
        self.stup = step_up

    def increment(self):
        if self.stup:
            self.newIV = hex(int(self.value, 16) + self.step)
        else:
            self.newIV = hex(int(self.value, 16) - self.stup)
        self.value = self.newIV[2:len(self.newIV)]
        return bytes.fromhex(self.value.zfill(32))

    def __repr__(self):
        self.increment()
        return self.value

@chal.route('/bean_counter/encrypt/')
def encrypt():
    cipher = AES.new(KEY, AES.MODE_ECB)
    ctr = StepUpCounter()

    out = []
    with open("challenge_files/bean_flag.png", 'rb') as f:
        block = f.read(16)
        while block:
            keystream = cipher.encrypt(ctr.increment())
            xored = [a^b for a, b in zip(block, keystream)]
            out.append(bytes(xored).hex())
            block = f.read(16)

    return {"encrypted": ''.join(out)}

def __init__(self, value=os.urandom(16), step_up=False): . Here steup_up = False means the value is not getting updated.

Let’s understand the working of the code, the first block header of the png file is XORed with the key and so on.


ct='649b00e86c44d40680028e6a09bc6d9cedcb4f83614ece7b88048e67404d6ab318cb4ebe02349a54f450ef1060845ba18ba222ca413ab77ce522eb1f299229ce95118b360adcea3b0e8671a0690221b26cebc1ec73ee17d0e08d3178e24673a4a4e89de702f853910f880483438491f5b544b140dea03d337c693bc2e77da7f09684b2c4d2837cf4b0d059d8b54acf1a331cb9f058d181f567e9018da7bfac85783cb45a82a651e74ef74c9aa587d4b2502c8bda989b8edfbfbd5658bf9654c8c7287d6e86b43948b57dc3f77ef71625de9b6330f89519c37b69d5ce8f5596102f3292d05ef1b1cf40f107f016415d27b4d0e102459bf0f5bc48e42d9cd83fb4a252bf92b8d14f7273f17d1a2b95640b8665c778706562534fc73dd9efb8e821ea5405c2dda834d5b2dba428622c50b02a3e81509c9704936fa7f135bea763b191310305dab2f0df4ff905881b1e46fc39b4f25080bbe2b3dc7df0fa92c3467173723191f4d1618311ecc11cbe3a73f2027e3f001ea3a0b94e947500132b72b2d2ba912109f311e6bc9629c35135a70909fa662662c0c29ced1ca9a81cbcca03c090105607311143be2e3f726f3e52e74a1f32b14f405c3d4bab1fbb567b55453b92c3fbf6344e4f656877d6d60867f5322200b12d22b7302025bd3fa6dfd6261c37db348fc34cc6b9605782cde1c2e4bca2e28d53d93d9fb2b51ff63ccb685bda0b9120beb045b0d630a608d8f33292840c460b1e053009dcf7c313a5b2c833464bfd75064e8cc88a864b830af27b01b8b981e62a37fa6b823bc6b7221d3d9f346f6b24752d5fa955d8c7691534f5b2aa3835c4f0bcbb15a2269f35bba7bc12217106f90a4cb0b88d323ac30ae7b8e0c788e8033f359a546fd80d36d7a244e4379e3b47b5bb42365a925fe0cec5cf965e07444cabead0eb187120a2e47fb8d93295921fb76075e2366779ca3b189019c404e7087c7733e499651002855e78093b862377d5ab178d12359988eeca5534578ba379bae01272758cb5f8fb8a4d237e1d91929fcd62e9cfd444fce94c78b18946e50021f28de8201107fbd145fbfce41c86efcf01e79d3336fc0c65c63ef7a6781eba33166a48f22ec9a18894da92092d51169a6bbdfe54fc51ae9bb70b0492e83de0c5a0e1686ef5f79ce358f0b46865f0b6eb7c42ac26487b1693f6c3b477b267ccfef4f5683138cfd3339a688f21589a0ff074b37cf3f586089e89f770814638d43c646909df57d56049cfe578d06a80bc3c5532dc58f6ace1b7dde7200843dc011fbd56b19abbe7152a6d9e5b5f0c973f85a7a523ef66e31252520e5953ad20f97b879c297d67decf908878a051c0e7fed468ea658c341a0ddfa1467988b8a23e956cab1a111cc992185fb7972c48d9f60f15c6bcb1629cd095e8f378b12d578ebbbb40ed1d8f9e80a58e6c83502434dc13847bd6c4bc1115ee82b0e0087fa52109d522573dae6f9c3ab9520ecaa7339ead80fcb550b58335197772ce7c44a45c90a5185eb697c23facc6d2f080b1707cac33050ea97b5811a6e63008e30c00d2552c84fd80915090128fa948d81f2dec4b436229f658bf64fba2f143c5b36dc503edb5c5fbc2f98eccb949b5ec6a5d3852d202b98766975225a3eee83b023934bc74b765445897c9d0ecdb204738ce236ad2171bc59b47084f34bfa4ab31630b4a67489e5fb2b151a06f5d9f7063eeaa4501e88b172a0fad8cbae96c385333b3b303ebd0741e2bffee38c0b95a70534b13a97aa43f4b7cf225939fb465c81c3755acaed576a3c87decf3e4e95335c27433973d0a6894dd830b484071d0c1f823e8f912a82ec1b794eaed42e6ee490aeab1c8702d259161c9010a9b8c394c5b05aaedeecb420382ab19561db6842be93f66d227b3fd7d709346fe35f609f24e3d19fc8736a8e7ff3280f8a01d78416a801a710eaf0f478b4c5fa0f35644f0a2ba5f7c0aa15fd4f842a4f91959e0316b53a26ca8ec60565af772a9915fa4361ff36b06a374703af731928b5849e791388aeff34ddc9ccae3257dfd407bb827468c832a5c4fa34292f87374213028b86a4671a32b6b2d97a42c1e39df2eefa55bc5fc53642dd130019751bf1ad2b6f82f253306939ece37c71f6d6abc5c6ce28083b598489fed29385e6acf18aba71d0d5f0fc456ed34f6cf74f803f5730c7a5df4f91ed4cc502bdf91a74e2195cd364e1b25f26c15e2e3031a80c3f4997a7dd187a5a326cd39e43978b87785933d5367aadf050776894557e4d808e5601e15a7d23b2d71b777b70e566bde53f4d7203901a77e2510ac3f2d211205a514c53c483831474906c16ce66fb6e16115b56cd9c1d180ad61738f90fa8be69b1b44180cfd2ebac158929a07eff4cf4bfdddfe421b01a220fccece09612545d306ab69527ff6831b9c91776c22911c6453d4b2baf45dfe8ca4b0a68c7966a8a780774bd2ebeb38b0549db21675c9d1049fa462cc08b00ffbb8c358583a33b35fbeb047ac6a674c8ed966b78d4fb32290d025ed1fad03c566229c3f5285539ed884c1b8fc37646edbbe8998d6a3014fb9b6954054e8266fe95a790ad427f46e76e38aa6e61a2c56dc1a725ad5bddedb302ab6429f7d4087e5dc04a96792bdb5d064ba57b1b73c0ed2d94fb60361802de1621a8bbbc3e014cb9e9896a0c31082d5ceddaf7e46da0cdd6a59cd08d88be22c7e64398ff741ed5d2ac301be114fd629953bcb668a7a9a982edee2363f38dc13c7a56371eeb2f6dbb89d47482b6c954d30b6432abf7d2241f6c8ce11d736182ec3daff4a4415495d24f14d780706ae2e8f0d709ca1e0b9bb445940d4ae62f69cbeb80fe585630ef19807849664cc280df4843234d0df7132a1f7a25d129dfebb515cabeeb63b5ba0f5ace8f725bf35a71f15ab41af88ea45db666d90aeabcd7290bb65422cc28c74e82785febee1bce89673d3673bef9b2d1c116e96864264804c30a07efa7553ec4e8aa018323cd9155d3389c2cfa44f1816cb9af5d19d54faa80c36c0f20920c03a221bdfb2e8a566af919354eddaa6742bae11e1930c1622cf14c1fcea5c483082e9c0a769c547d87a37fd20fe9648d841c1f6aa8847db2cd9bb6b515c9add7f15783a866bdb2e8facffa75509348c7ed149c705184d088a759dfda47d78f7ad83e06b7e616de595294e919e3732425fd592b813e640ee1d2e898c6d06c0f2d6317a5925575bdeb721318539f8a32f29ab684c809e2852df9868f57cc1a6106b93a18d8f16d21d3b3dd780054e7b21361e881f5f240254d5e902416328aabf1017f9c5e2abce170bd21d86140b57f0b5f0e0491f722f889e3e8fbbd8f2ed5b527e6b7b64624b3e59884bdce8a6a12192ae8e84692e16ee842f6946654204edee253eb82c8f7ab690f2119c6c52714c7b0f8374f5de4a951ff2fe755cd566f3db72f70245a5c89526fe2e9f68e1b8dd37569c91b59b81c9efbffa4e93ab8299ee73eec266c400a4b46f1b63ae0942cfbb80926156b7c16889d7a1f81220b064349c4f1c6ab76f90a7997200fe432b927fc83b6772367f25c20cbad04d1b3acf15e1247273405f822a37a2c8094f60f073142a3a00e7640c7804c7de4a8122a44e31f65163c7a8eacebed52d66f3e8cdfa5ddca5661f18fbfc8fa0297ebc84441b97b304c0012c5621bb1fcab9939069a328cc339ea314ed506626a2ef2d1ea06dd64ff3fe01f5147bc533ea828f449c177918262a760b4397e5ffbd6274ecf846ae2c7abbdab4c6d00f0a4e0530e5e380a6c3c3c8ef2240700f3da3e34c471ef8bd392834e6356eb403d6dde16978c39ecc7ebee011f113b78fdf2beb91cefb1deb44119c317a6ba33eeea7f269c1bf5263433e6b92bd808dc8f1e671ff2bd796bd8e307e755844f9e4076dbba67c1d28bc6b1b627b72329d7e03d809001796fe7ac8eac5ebc835b984ba66fe9c4bf46dd6a9fcdd5d8e7cbe4ee4bec0ba225aa77d517e670675c49a7e2e0b092b9276d51fe1765bfa28b3169ece0232982212793dafc69e683a25b7e7f08a480fe268dded0dd2de3108e4a9820c8db027b8fbc6714d5abb04489ac7c3378b09a5d114968a8389ed9ee86d30288526b84b11b4facd489b9cf7f27cd80f41ce991671524431161490df2f646051db8950b91ff0f634aaaf03bddfbd4223ccaa6ab26d058903a905927af2b3b8a6e02f9e6c83b1bcecfb60b49756cc22fcd799085f08345aae2d3191c3897724a7d5db05d520d4a60a0a3fa69a60cf12e2e9b60bfe83d242c64654198f587483eed1c4944d0119758a0a8e3e8656ca251dbb48b71d4514daf9e8873e39b8d846c6688e3573905da633b5eb70476e896c5536aa5343e842328b201b1d5a0a4ea787b8d38d7f260ec839c08df984dcbc316c392af2a92eac16a45218097518be33e13bdc70bb228022ec0abaa4b743fc99b4480894c9d4d1e5788e08550b851649adf3f1b7fc6fd857a29fe109fff211bdae36ba61bbed655584838e0d4721bf936f85d8794294ec9974f543d4225fce5a6d6ae2d9397401c6cda12b5ffca3f338b0401b42fa57412cd504cc18e1b928fdd23ab64d8659f88b937a3a980168b95a65f552c500e4e5a988bcc5df6f7937fab81dd2b8a242459284e179d5f0150e640491bf5de07742f9ea8bd12737977595b5f1e3d3a491dfb15a095348487676f10ce3992eedfa9b1f3bbc1e6ce62493a3f8e321259b31bab89f2ac6315c542c3b70fc05ec12c30b135e092cca03d5b5f00c601fac9a5c0ff89906e43f260464c374a423968f6c940c63e0926e1eba6db2a82abcb9f944153602c3ba58bac2e1ccd89d532e0959e26d5d84e6ff47e764526db1cf015b7f108b6010655242ce72711f935aae4d79cf66162599c5c843f56efdcf863d6b968755054d2ec3693cb64307ccc9dff250912a921172fb43306370ca51e91b4b7f7f0ea5c2fc623629ec33301b6957afa96484774e1b3faf1c3c57b42c43896c5dfeadea73128e0bfd483fa32788a5af54affb78e152365414493af9b01ec96e85e6fe90800d7d5a18216b6a5c8b4e250388ab6cc6ea5a79fb1b7647a5befa0c4892124dc3d210bd825e08a061c5fec639238cf2f4380ccd2013ca9ab710eff51c00940418f90ea178ae901a9e446c94052649cae1f2170025b1b1918065ce39540a592ca7701e5da54ccfcd52d20ca08aadc522f6cac9daefb8a953d0784aa820d8ab0ac9fad53e7841090da398a3ded9cf7fafc442ff04414c72f2b31f2931cf7a40fc22856856add4a346da7137cd1b9a2a7cebb6f5ef9269c12357c46b2303892f19b69be8eca66407d80ea26f9d0533d36682f645c1e1f9cec62e5d04320c9abe507ab2bffea35d81d6bbd631af080a4d4edb458d638099ee0ffee0b8bdcf73002b15cc12cde58c80be9b867e9ea93f3eb41f137c64a6823508a8b21c737946fc174729a46f397b3acdcf4b78214d3fba80852026548795029748edbba685a86ecad3241f6cd4b0e84a7c8d03f9b99c4424c31e89c836f65dade5c90d1996c6b8360ed1828cf71751cdf9692cdb180e54d2a301338f038e51ddec377a2879392507be3cf5daa7d661f37847bd6cb46b36c448d225a991ff4f86f5c44392017e6d735af6bde208db085bdad6b8ba0630dbc7cfa88745b5e4ee2a13a7e4808da544cf816ad9ae170c6e9c6ebf5ffafdddde04ea59e0ccd8e143a3febf5628cf54c1dc619db3f9300779bb0cc03dc2a2eb1f83d1578be8ccb7f6bfdd3a94e4675bb7ddfa5c83b09bee77ee21c5d5b16e73e5eef97307cf7aa292bb2771aed05cb0f24d484880bccb6d896d06dce45ab726c1bd977e9bbe92e4c47df2a3269aeebd1e0f32f1800acca6f4ad887359f4b921d29a2e6ae141f5fe73e7a3d67be1a88db518413122e0ae838ae75d1de22abeafd852397c5bea3656eb302ec36eb089a4962f1e3fbf68e5f6549277f3e476866daf37c3b3ce4dddd2d7d43286a34d756ea01688dae3dae774a0bec3297d04f0762e5902279e3db6b29995d4d020281abf0e6dd4073613e3a322f1dee53ce7bc9d51cd1273ca28e545cca60198b226ea4cfc85a39fa93e4434cb6afc8cc38c920eb1f87d731630c43aaa1065bd6f12c65e566ce1488e77fedcf3ef73aab451389131fb8e129aa1c002182959d0ba2861c408377e8ceee3b3cde42762c875736a23aa38ca6e234f38e0930a320a3ffafa4c8ac9c8149724bff852c7f77b11f775c207cc30019e01c9923a0a2ad43a0a5337d22821baca74ef0863f45bbaf2e8e5dd2001cb23a57df0eaeeb99fa36f5f63a989c374f551f83fcee931268b3400aca9310caba15df8c83cd7d6edcb4ea90305894880028e6740f4d08d56b44eaf6147be44d9718e676ed729cec3e84fd7c471b80c8002891309b96cc909c84abd7943a4c70d4b8e6740ed5d8bb5bf0dc00c23ab62f402cd1525955dab89eb39c61526ee4bc94fde30c1fa3eceedeb4ee6250f9a745aef131e10a07e341a74f37e55f37ebb32c824af92461c8c39436c9ee78aea1427d6077407933b836152c2062ddcfcc7b8ab3f41d330b3aa27016a2147dd967d2cba056de8d481765cc24fca660f186a69a415093acdc6d11e20b52d156d85bfe8cc29cdeb60523533b8d011863501754e7110a46270399ec5df84ec61d5548cc2a3dea6e2a401dac7935aa5e444d889c2a3c214f8202d5a5cab7c36017ad47d7f9e83ec85f6ae0dec500370e45ae454146bae33fd4994a687a10ccc026d9a26958ec84433a801daafe5d96cac83c320968994eb02f1c29f843333d21c7ed481900a8a87b18cfa70214f5edb153a2eee458946af88b450b7d4df44aa6d48cd7f4ec00b6c718fdfa25572f77e7ed78c096b532fc0e7f330d7f49b66bb19bc072b61e56950ad7378a458d1f716d8b62897ef52df36c170adcad136ba7c113cbc6e4a2c1f2f60e6a026e287ef2d94e6c75ade5c90734af13fe3b73f1e5b69d55597119f13eac2762b6a4b6d2c6ce743c6028e8433da7dc7d5ad5c93cd512eef9ecdfa0608e47dc914a683a548389a3e3208c6a19f4a6dd981644749c5246a50b5e0b18609e003d83f9d9c6902b5c2544d1cc284f20bcd5152824a1e0d1949d50f6c00402f2707b3ee6a78baf29945ca6ebb3715d33a1d64636b75fa2da99932f019abc87a8d216d65e8053e345b97b86cbb3715b4fa4ed2bac7b1e5595997b8de874572fe174063e2b30d082511f787aa0e1bcf88e8879755d1ab685fb5ec4884a992180361065738a2dbd01281766c5f044ecc40eb236e39cc6e0a8e971a77d202f83e50ef5b3be9bc5d93625f1972be341c430c0570ec69c68c6543af3be3d831bd3fdecc9524c23b8d7897df113e4eaf2321927b5cf2740dbf489944947ed48168e965a4fd8fdebe02157317d8f78fab53a321c88f5626a5d9be7d362d9ea4b9d0abda37eb21c12102cc4e5f1972f634b842a27103d439c2b605af650e7c576cb01aae734a954ea9aa0ec49ddc4a65b33f1d43aa8ddc02e5aeb237960b24a4dc52d08cfa0ef3a08aefe46180654bcb2dac2e8214a638a87d5e38fe2220e398ef66cf150f2a9eab5576d0aadd5812925020d167371bafa23211b7da39f7ec70b29e281b6d36825fbe9cba8f2d64453ee033cd4ca28571387947616473db3bc5763d1c8f66417ca3446f8b6eb7308d25e468c2037c7724fc1be88c09e8a589093f53f2a8f44979a443250481282fe98d6ce022ea5559ce12f57c54d120c3ecec58d5572fccebff2b4fedb530d1f187aa840d8f0bd8ffcbd1b2530eb5c5d414da1a96a028c146ae84d894b23707cee14948a243c821463ee3046bc34e24a4cc71b3bfa02cc5a014c9fe8156faf54c54d416d050dce5b2065b1a381eef0cc24d4a1c38b834a587332ebd0b070feb254acc0e82229a6d87d1119a41e39de5d1fa9421fefc7700dcce5cc4b492174789a64eab2725d23b1c9446a16275f09185a828ee448b9d6f4bffea5da2f5c788ff36ccd25edcd822cbe4ab6447e1a275d445451baf8401e84f706befa78005de91ae8f68e6642cc0e7f355f0f2a778b4e55e7ed5ecfa769f4c6e13f9f2703e4e630051320db7e5b0822ff2d5d3f570a00d0688af91d75bb2984346d16740c51e0c95f83076daede48a2cfc1b6834b058de82bf4bc74512583feac02704abfd7671c50b1c0bc7e60a2d31a4d5dabbb84338e296bd826958cb018c517628db4b123fc17cbeb27e7782d71cc225aeac29889bb4345cdc5e02b196a2a2ac262cbd1e0485d8880785813c67417343f6fc204fa5c7ddb4f4eb9c67803be6c1bf625aafc11c679c199b0704877a08f0d3d565f8cf8a0c898be3668f0871ae3c1dc0d86e010eab69a57c7686476813bbe7ea6f8e39fba419a220ac87ae1a27d062ccfbf5ecf4743664e46885ac2d9aa685a3cee1c68dc5233c82975426ac45c691f551418fa0de85c46ae20b04bf62c5d5d740a17e7042ff67cdcd41d3b50f5dace1db2daea90d64b44029b48131ecab7dc3fe760d36b476a228a46d57db770a182099fe3c134395b3654ca98e9af1c486ff543bf6d96db4381acf26017ca146f52976999ef56daf8337bb52a08411e6c3e588dcae7349fc7072b65c7b3741fa0f259314a98707cd1121c917a82408f6d5f38e62ac4310d25e4164a8a58638d2f14799f11a43a4b99a55203b81650a89fddd5829f5dada74fa78d48b2139b7f396135682cc18458e020062bcf7fb10f7d48306e721048406e430973b1da9e8ac5c1b9b7ae4d1b1f2996be70e7889d3faf9c14bd75d50d0737cf39f920addbda7c693cabf495ac468e466ae4a6df9a84f6c13f6de4258e0508865e980018abf5e7ddb4aaa64cf0ded60c20f64788edde66f49cc1f0587dc87c321b8fc4d934ea9215777bc8ca323a42aa8cc2205f14713574efbc4b66ce72589bc30d9507d6d2e9f72c260a52afc01991007a222b06c8bccef6aab98b737b4f9f1a8b40873da3f597cf94c878355e46defcb1334c02bcdeecc47adea8bac32bd3fb3af8e3a6f99badc18023bcbcb079642a27173c9954e6dcbb6559b8caa3ee1ed50da883ee30c1132bdb6a05346b13d3f6c85a2e434d36025a0c189bbb490bc7b9c6641f56187a48a06e7e90bd19a5955e6ca7eb96483a580056ce445d91c9b19056a5bc209efccaadea71a36b66c56aed7173387aa884e8a00e16f32b27060ed61e8e0540974614dbc7fa1f81ab72aaa26a7b907ce6f3e22fed3983ce2543002e0092e145b778d789763266ede2cb83a96acd6d9ea6ca8406ebde81e95578bb7d40ab992d6f971b93adb252dad2f4e9cb3ec8c3865eacbed2c5a8b9b5ec5e472f91027d6963791f5caed408aacfd1063a8e083b5698eed29ac4d017c575cd0520ee1c6f2b182cb60db7a8abb66a42842cf26418855b2fdde5baa1639b987bd197d97115579eecfe94cddd85240820ee2fe5f204571ae5e12fe1cd2cd51939f94a03aca24f99eabb735d41a631851a012bea4090725557056534c71f93a1d8806d8e02dceb4c088ae2b1713abc4f4fc3c101fa2fded082b896c3ee183a83f42c9e8d461e6a0def4d2c7caaad88f209621a1484f11cc2b3d223a8c1f16822a9331df26e1b8220f0720a5ec2807c7625fbc0387a0946d8ba80a9776b8a48dc667f19f7255711e1095c8a1119a605820d9603b20809698eefdc36ebf699e17d509de523bf84c91ae1439ab5f90bfd7f972160723c20dda29cef376b7042bab24ab290d21e3455370bf620ad11f70d4179b139079ce04d9be46853c1ca9d653e6566e4e84b114ce296a7bfe9f495ac4d855d72fdcfb8feb8ddcc36fffca9be623af45f71e79b66b8cdda49093ca30e71a0b070723c204d9be694fca0fc3eccc199517668aade978972080c7d51a350fedf044ba4c0c5565668dda0c7fb29d4db63a38a6734a8c50b72c0085c4b9d7f11a0b07f9103730ee09cee560a112a802bdbaa1eb6961449a7deeea4a814f60b6f071c59f7750c48fcea7f07594654bcb2dbe3c90caed5901e6c58b35d4da8bc864b418df381f7bcb2c8f3bb7d3a58909cf4bc9e5bfa1647042d1f77203f7c1036a43d35f765eea377e5e780df43a55b476fa84638e99be9902525e1c06e5e161e330e7ba686f93e556ec8401e009fb9458d755d58b4dae9359bf620ae22c43f437bbe3648d4ab36e89d75feae31c4185a5d4a037ded9e50b5fa53f8ea8b272223c8f41c8156e7fec4c725451ed220d09e078673f3f30875a8d81a762d31c3357bc81bfd058a4a89a7d103da3746941c173e0aa0e3ef028f413cc5a06b149d200c24e72f811c6b6887ed7eff89adcf37364ec6eeafbbdd6b8a6b530fdf74d464b4e38b935a48135283b183e9ec75518b554667a39a98eba757397de17f638de84a9088905e4c773ee560f72663b907e2a792cd7e4b695212cd062cc09e826d5e111b135a651db79e90e2deac053b06cde04901894ed883c6152d0b058ea240b563bef1cbd24269a51c468c90bea24a627e5c17ef25b3c8954d1352b76b36f0aecdd83d548febda72b4fed81ebbd3be3b85e0686d4ca56d904e39dbb3e2f256cd7dd1098498471ca1d5ba7aeff03e55ea6389ab63bd2e15984c3280e8be72a058d6be725d01fa1f10de9caf87fde75845a05163b94bc5c8114feff16981f3962f637bb597b10cd4ca2068b0885646a7fff3d48cf43a2b51ffe5f795f7a87e91b61db23a428618531ecc6e5c30ecb056a52901c445a7ca48690c1f97dcf4808fb144d79a3a835f0786fed599163173da0462f4fcf314cc737985469445e2dfbd4d4707fff79df331327e4c7e15eb37dc2e35736625fc5056c1ca53b88fda9f3bcc60389cf4d524e73eae22853e63163c50b0e1fcd39aef0b60ddccced68a912a90049d95b972d0abe891b87cc43322b04845cd3510daf88bce550d0b016c89ab32b701012823bebc37017db06f1baee89d5d9dac30f0c4b39f08fc01ee110de51b9e35e47e30a6da003db29a97f33fcd3ef1e09eadef772895596d1d541edc05ec88f817ff31a531e157838e75d28a2cf51939f109c759027fa4d1137321bd4fbffae5bde52eb5bc3478ec9d96f955b94a46c2b3791a9c38e654afd3fb958c2f17113a293e6b5d205d8b6d1f37796c0f17193c90476478c491b4d2ccb70300effa9cae2b36e8cab7f84955a2993d8be8b14b9eb53d930c75b9630f78cc1b1242dadef044da43768d73be25ec81900b60d6f5c0cda9154956d3ceaffc22ccc027fa9c29c6433f81e5b4eac780184657154d486f9f168ea983d46fc746b9fc7c19d278a2d955f0fc4be8c4ca76ecc1cd2bd7ebdd3b49a1cc1b429c1e2c4e6fb4b3ab87abeb212a7bf969e7457719e78b6498143cf89705eb390ef321438bf43d281acff66cfd6d83ec93dc4e875b5415a12767c813fb35af40179689383a64b854a8143cf6404d9f3475a165a6237d5dce66604c547cbcfec58b57ab479b5d47a0f8f4d0889052423970a2d8d9c66bb4962e2681cefb0597ed7371c193cb90cdcf3d3462570569db701dc8f4c9bee08c649a062ee92c643d3d671dc644fc71b0e23a349d75559d7c2911b573d169322f7d8d8140d448a6344ace4c0873d859659760ec34f0d45f06fb37b9cffe1658410a71c29b435b9b41ee24c2496932c1e798f0fcd70f27e272d9904644660d79b4498d44dc5478bd9bc06622231725d082fd3d45deccdca5320a37e99290d886dac83c349c54726ccebb75cbbd841c4257152d28068eae6ef851638913e27e21016a110ac45d579b01f55796e537550d35f519052de21f88fdb4b16d8b26a171fe3cc4cec6be9058daa9efd193ac4b5f4883af4d857d52dc9eefaf71033c340a11f2ee688f9caa3f20ae53fd8eb420a2d87a8a878f674c0ada4f6acfc41abbf561d0a8a48a030bc754df924769f2e44b57b54e2321ce44261779bf70a386019fe341b0bc8ee4f8b91b602b1f59ae56c8c421f985a06be325b1c4d5bcb183ebb3faaab6d02d0270ac7f6423f4b684e0205f7b527e76b61929a9e2af7c6e4c237cf6e8572a007cd69c4fb4f32f3d0f618b49e3e0d6087058901d8af74936077f2935579f9573c94b704a8414219bd9613622d1c4278f8ce6b15616561d6729231d09efa84a0c8fb0b6dd62da8738f0e44ab644bef3fb311a929dcb46c8be89f64ba1574d3ef6437699f7e89d3fa79816b3df3dcee9b4ba5a264c0cda24db1e5c96e018e3fef477362bf717cecaca98c09a0eb83d405669ccb0e29dd5bbd9e1a94756bd3800b03c38eda1e7bb750f3df58b94363e3018d0f83efca7e6bb695334d44aead3fec66e488587fcb7d9ab92b941a0a26a66400482be50657a8884e8a21c04e7db267ef6d395fbc1659f6e5c540e9331cdc30bc3e1ef6d175dd3a9917f3d4f63f62e77578e6535ac01dfa0d27971c2901818b0f8d81f16ea9a30e2d0b1fa6d937870295284433210540db660e161b095f14d75b7366b76108cef1128a1bfa742cad6f61e76b625e7bae6043e5da3bf5ae1ca9cbbaf5906e6a57348467141a6bfda8c11ad0ab7c0255b9070f5bd8396740498f3569594564d04d0a4cc728ab634fdc1e92129f76c1c74e004d61e4a553d7c222ae2c6688afb35cbbd8e51217e1a222604c99b96869dd00821b41af9f4e6744820ec45fc40705ca41d269ff1e703d29c2055aa2e18194a994408d09e8661ae41992119df6dc6815ea98459748f9da5a8876f4f89bb427661652b471b57b54ee9b4b231b2d21c6b01e3f19cc345621ec27ab090a77ecaa94d4a65d622ec2eded6181960938f038e8564462e737583de8c21d3909a582b675759b46c7745054433c194e7e96b135026151496be4f7c2a6958d894be508d06ba281e74aade0830b8143e34eb097e3da29228c00e16f52d3119119956c0fbbe69f32408d4e01884215d8bef6565ce934bf173f27cb052a8ea3574121b003597f4d28a92f4ea3cb96b5f227184b19c68b2fdd4c8dee012eee5a2f5c8371cb310d11d6b8a8a372e205edf0c26f17a011ccfb016574497fbf5439eb32fd810c904fa8cff673b27857d7406fcbee57c0314baf235777e72f0008aec5896e8cabbffeccbfd5436e5379ca63ef6db29294a7a144678923c24ca424e650dc5069fdaa267fb5c751b07e4ac80a42d55910086614a58f2ea041efd991729919b80bc0a401527dd27ba8664986a9d20a0b25c470adeedff95cedc03e02cfcc75f9107c05eddde5c2476efc75b95cdf5d01f9deea21caef33b455437afa187efc702b1a63b8eeff42564ed93d29026ae864ff10f5889ee59c19f8add6ff0fb36540e3f743de7eaefc5020e6ce4e0c38b834456edaa7bf2dbc39d8e283a8cf45c78bd426396bae8debf213dd6268e72152900d81c61e981e0dd6519fb002ca8de45aa2a6f93b62a88015b4d38350311b13be3ccb6110024f7df8b927eddf7df17316522b2291d44512400ec0efc6028e64b8cf40340545f4028cc02a73039c10128a6146fa93282dde49497d2aed954971f811c6578aed0b07d79c893428c1bc443cabc8b650a6e8a02e2cd4fd7d77e0f86ff67620a042bc19a55ad93d29022e5c33ed8034690a280ec387ad2e6a4fc459b91f1baafa7239ad6ceb082efccc013998366867e0ed080f7eb76de661a3c764e841ea5f827f9d2a0c63fc75192ac2f9c5a05cad065fa51b9776e88000420b6bf24f54de710de1343a957cfd91ec1d39f917ac2c186a486e6f834d42859bae87da4de56d18a339d8d29e024832f07c7261b97de03dda4d7df839b8e51c101b67f87c10dc7af8c957e0c7555492b7f60fd33a1d1cc522a9aa2f8e133cbcec6ae804b394a0e1aadf11f51db4d523f3e2de984a1a70fb1cbf5770d27b685d0573e4437cd4b777eefdeb7c9d7360e0227447cfc6c2c1b93013b5283450fb9401ae9585fe0c72bd5af9e7131515127027b699b38df8596ed3648fcc8a2ad88ea1d6e142fe733ce1062d723d4867a3821195178b587fcbb32e62894b441fa222e090119da474fa5eb99acf49a8427efe7cd423889063e2a25f795fca77b916ca0b87d1ea1243b0f50526c06113914cec2ea252dd34c35a8760acc536dcd744c469a2621fe03869acf7a07bf76e1176d2d459db033c200053f37e9dc96c8002868c6bc1025e0fac56b1125bcb74872fef19f9089b4b4a71db05032e1dbc3166da26d81684e202f1a28c4c9b8f0d3166da26d81684e202f1a28c4c9b8f357dc0d1338e1405e2ad2f9e31cfc621019e7521b0c39dcc0d3ffec4afbc8d843e42090e6e31a4ea1c76feacc4fbae1a539dbd01d88816a43fd41d26efe922a746b55eda26d896672817a33ee0b27502a22037e69472172481054624c3e0f3375d45fc7cbed05bd7ee45ff3f2e74acd06ed32b058a5dde2cc8effb3ee1b38f18f977e091b38ea7dceed86c31e2b3775666a8875e46254130b49622af2a09af92151445b775f98a01e8f78cc45c7fd986cb53c60b6e9414984570c4f92818fce9cac8e767cd0911a9f7d367aa3417b162f1dd5601327b2e98491ccc012b5c022a6dce3133679c3ef677c7fa340c071ee528dc308f4d8160ec056061639fb4924726254cce34f7ec92f5d3672e5386a28930ee7034049f7c76c60d19165e032520f3d93ecf1baf5c4a92ab2348faf7fa31511d8a0ac8a03bb77e1eaa77dd1737c3799ff86073e567aba9dfcba6057ae4ad5594a5be7df143cc2542620793676e0538a38f0fcd62f580797c7c9ca53a298e2e621679629d18957c311efde789ae2b0ed5fada05890bd6de32d1bfe1b2f52235c5e778f3d8848a3482454a09e5cbfcdc86009c5cd0422a356965e22b8938a862457afa988062007ccdb2a21985a1248d4867e70880f37891669b9633db32bd309e02dc1f930b8e45d5bee01b469c63d196bc2bc784021faca5509a7d9e03bf71bf1028d6478d91f8f00ec6165c4812101fd346c9e21091780f424517afdd63f5a59a31dfcf3d9335bf78a5c2ef4b6a88465d7352bfeef311ad2e6cce7de4f0570306333beedec631edaeec128636b45a05ad0881033cd11ab561fca5620cb02b711c995bc7e6fc5eb3cf95370b502a68ef532af5679de9ad341af577e4b6b069033195d97fea5fc48c89cb2d0e0298023a493312ebd422469d29f4d852b0b65f004c4ee05db9197355e138a182a4c5428693b9d3a5040d069a2d5662a1722e129ba46548ae951dab8cb88f3ff4c74849664f8e860a88081b1f57f8fe6b36aa3c2ec69ca5e48c67923a86c31ac4c9ce27a42c9c53df6d3f6bfb83c6a8a91a2a9d6c74786b4f1c8ce7fc07e7f5bbb43ef4b5b16b9bde20fcdedc904ea9e26131d51b749df37b1df4c212bd957350c24edd8a98e3a777ea608966969856edebf9c0bef8b642d5579b14df2af710fc5c91b997917a659303ce695dce6dbad7d6bdd8f2878a625db8cb6542f621ce3f1f9a9bcca2a1877ad558b172236685a6d0ba4dd8353922f3efb31cb6f802b89237adb02832b2f030efa9587f8bf317126a5b8361aa26dd2341007ccdb2db2c66e45caf05917071c38c29c2e5e529eac8eecb5b94bbc327c89df295c931ca735076822cf0d353c17cbf26cfc88dc2e7f94147009ecfcd0bac9c7dae676b19c10836325ab419ca0c79f951982e01372922542971b598b405e31722969a4b8a6d6e72e1f9cafb36f19f7c76e62c2e9d47b17038c2bb97b1b075cd40a9661ffe61ca2098027abc7c28049212fb5cf6a90273f695cd4e34fae1e7ed50da933ad7c158abe7fb4bc5297073cfb963400dd90de24c52d488eb590480707493295fde769030785cf79da63896cd27cfe8d4298685a366c60e21e9261588223ff17629eedaa49033f5b411b7f7cd6c5eb90dd098286fe809e7e755b9e1c125c8febe295e193f3a048456656c0dd5704a6dd9e5b3d6f1740995b7d5877d6f7b9b1937d535ef7bb67d7d213595eaf901f2696b0c125daacd7a8c939351dd038edafd49aa30439de55ebc39110d80be33daedaaf222dd9fe7fb0927a7bdf84cfe1770fa36f06179fc9d1db037eb58bdf1588e68104d4274b0b5507fff1fdefd5eb71038b5f73e297fa06e4128d39d1f93e4ce0dd2350e1e19f0e4046bce0d19b9e13c438067e4b0b843e01ae4c97cfb7d0a1a927216321e9b3a10b471f176d9a64d6a767f65703fa9c04db879e9736503e6ce06c8386dd70554f8003873686487e0dbb11869c87571868b8ed14ca691038f7edebc04077adb8436ac033b5fc6203afbf429253a64895594a5e9a97e099949f44e78082dcc1f0cb042f7b2eedfb48b99472ef113336d394c15f8e458d7253041f4c6731ad552b27c23946adbda11aca596de20308303e2a4f0b3f7306d5159388f522d9a74b5b2735cdc1c9e1c80280f3b40a7929073d01bf3d0f579fb3616a3f26384a79b19f866cb8c8d5edcd05377c36696165200591ea6ca67acb9546b9c345a3d0b066dc5f18d2b146770371faa5d8e7e963c20afa1dd81328979f59c095dd6be88d476320f271dde189a96afa586c5155ac475d98dce71b3bf024e0f7496d63010cbce4aaad915b04cb43c1818ac31e0b275c263ad02d234f002e70e290cc16d6253ad74d62109baedd96e8cabcf84228240b4d81738acf776ef76919b9b28ce469ce169838b45ed64e68a79cff3bc84687c90d50a0c14c3f4b071245722d969d236a72e869221f8ec6fe302862b7c9bb82860e43a15d59d4966429374f47b97b4f2675abef8a39755711a2d65a7e63c1fb9c5487d76be8c80225119bbd9da791f39bca85b9509ef43b03b9a8dc77ab902156b48afd3ed737ef111fe9939a94eed9915f45ae322630bf831909e8f65a6676e259b547bfffbe86fe3d2290fb0c01bb426d2392d0f107c6af034cdf3aa0f6d74b3420ef6bbe7960c905b880c0f10942c5324a27e955fdd5fb3c5e16379e659b3803d4405ce0cdf7e7f6234ce34cecdf07534902182c8ee3d38d3bf34b74c5a7c793df3dc3ecd871d2eddfaee7278818a03e26cf031f27b6beb98b122766fbd2be1d09c65ceecaf7d77ec85744d85936cc7b188024bf1c7c9cd4e1802df3835713b8461ee76b4f85e595d39060df016277d139dbc0544cd4fcb0522d617321d8852911d5631f277bda9db6e9d40a7bde1dbb9ee43bad83abb373e35f8963113306687490e874589f8749df08fdea0650e2c0470b21dc57ed4fe103733426150cd4ebcdb3aa620761de19afb4f9257929055c1f4da5a40e919154bbb0b0bcae6cbd0d09f37cecc2e5f35e18c54b0e7c2e489b40ef24f3fe7ccd61afb528f68afe1db4d9cb9110b0fc58dfc8d329fa336ebd964d023153a467bb3f654e23f9411b000e16f4eaecb0e1f4d090e9e9f8b97b174e52a059f5dd183135c377af379df51ba9d82fb31a9cb957e81db9b1b73ac80ba58964dce0e820028fabd337a31ea12f202cc43f57b6e667008726d45900a222a50269049498823b4face98f17e6ccb6e345723d4180d69be7f089548ef5e0f8cfdcdc288afa1a9d9c8f68ed50478e04aaff087aaa82daf4381b5c36ee44a6fea3f99b6362d11ea68c203443b27d2dbf3f12e186254c1c6ec563909dc19e4cb1cf937786c7e2856a4817eab5737c8d8ffc25156465e2926bb5275e2ca4e2ba0b17db31ccc8ca424a8f5aa1ec3ef2a249314a68af4019745b832b6eb66335891634c52b336ef0f9b4e2ad33d6d84a680879cae728d3eb331179c1baee4fe333d7d7a9731a60fc7c56d532c9b46e9061de3cc1f9611f37ef56a8af72b4f189fca6e701cb953c9c8e3f199b36b269297f87e7d980fa5fac6c91f44076c9ca40bae5280dcbadcd5397169c77a09c291127956a61c7109839f6530d43a01c3c9d710491d1eb1b0ea3b01a9798c6bc2194210925147dd47f078e101ab395f7f63cfd4325f603731f17705d394b33b1679542623ca9ebf1af36cdb3dab7925f81f093e99f046055922a5024699bd0f20ef6937afa01e6ecec4b3cdb224fe0bd7b53f199ccdd2b8fdd9761da568b58acba083a596180e51f8615a867d808f17c31b4623a0412712925bb2a7236740f18187a98a1ab3b7e1918344c6caec9d2d32f983aba579ccde076858bcf3dc9521d16151394461ff7345c3bffd926c56d869a4876183650f385e0705402ac3e4548d6ae9ec01312520a022da59d5d63d0f5f6a4b6d6c8e402f8a5f5789984ac2ac0548a76e8c69ec5589e9ec7d7fa25715ca2d6ed869e18192777f30b539a0a37b16403478298c436ccc72fe7cb8d2ff2de0758b06542be4cbef2ba70559c91d3766f110cfe3f9ead656147ce1919800964b2c6528d209a3b02da57ff06bacd163be4972b5b4a6f8d757a42aa8cc0522d6173319980aea244fced2fae9a39738c8b5f192d557b678167274314f138b139dd6ff7a505e1534b36d8cd2abeac5eeef33d40d2faaebec0cb7e860509d5b5a10cf28af6e1c4ed281f0afe247f703e1ed6e89d4472e1e39ad34d857d555db1ca97493153f9de0604e0131771e3b98642fb68c96939dbd7fc149961a98ff3a2332a5d7dbe61df43a55f0f27861ea002797de20301825a59b59ae734ae6cc1a1867ad1f850f26e2ac2c7fa0ce0a62bef3f350b79433af287e4e326341f4105a3b5887124a87522b715643cad17d01a4a17842e3cd262ffe47c22ffe028d8be48174c522816fea0b01835439f8ec3780e8ac641bb6548bb5173ff38976b2a6738adb3924996c1e8735fee6188ac7583f5d8c3a8c6be7f49b5a46e92ee50b0b25e178607ea1ed5d41bff3457777ec83fd99165b777d836a3f2bebbe78f4b63744d9b6889eabe616e9c25feb29a5da331c7767614228a90f87752c18a0d992458208b8658a8dab6e438da26619a91644b349e2a20b6f8f6c6e02c1f30a03c38e2548f82fcdb790153efed1416757ac2fc2e5f1963720a4ae408e0149849408eb35f0ecff8ee54a140227119113b326ba7f4c1cc126ad9d18b997a01307be9d018a9b434a2b275e187c961ce6184aa67ad704d6f86ead8f0ca7c51ce7b4dc2ce0bafa719d7ad95fd938427fff9d8a140b25d9e06ff27bf861bc103dbd4fe062ae137ae9f314371b997826e386add0a41907d9b1a20508098f6e60685d05ba9fe02b2b5c01dae7ae8caaaa6294cf6d2ac5c610dcfa885de35aa5e444d889c2a3dea6e2a4019a5de35aa504f4c59ab9be0fa222b58619ae6499fc86754b4e199c0ca2a305c92605c359b8763aba7820af236a3a49b7947f89efe35e8f2cff790a846dc2a8078988ae6b013df74caa262454b4e2a401dde32ced205fb4a6b8c3402f3748dabe454b6d68bb745b0f6cb0824984710570f2f3446878c61ee63ae74eb7ba0dd23adae7cee0f1dcc43af8f4e66b82812ff2d5ccea6f27efc008fe65c96701dee023ab074cca6f9e7c4036b93baf4b6cf838dffcdb442ad93a271ece9fb4e28a3e63243e691e87728c56faa6d06592e85e830c0418e93ef8d7c71a0b875199b64321c7e40aa276d26fde042867a7738a2022d8cccf8362ac76b8e0980c925fc0ad9aa88c38a862f971b1325cde52a7a70740d8bfc1cb9d5c4a65b33f1d6b82a5bc6681eacb72509dbd8642ce8405ec4b3915844c9b683b30658273ff3d947c5ba57790636bee8fa92afc34b93bdfd6e2a4011da27b42a9627ad4013458a34352fdf4f36a89efe33e8fccf2ff1ca251dbf91f55a0a8e08775843a19ac080b25e1766c6fbde31a1f495ac419ac080b25d93aababdf522e9f79d6032428c11325e1b8620fcf4f0e08b2ef65a78b1e806062652192c35c143e23ef82065ce3f43543e710562861f819776beb29908bca97b105307ad6ffcbedc002746c194ea85377c38a7b585e5f82dc1041aa2624d2df30f52ccbe85b87cbe9c4448ec4202f31e4a4019dca93080c708b0bc978f865c8d959c143abe834d51bee9a2691179be21f09c3996547c2abee896d5a2452a6347bd09d795a8c0eef61b435f781fea1b4e8b4094e624441cb53570ccf43e3628b0c69c7dae7ae9c1ff93f5a1c8297c462f5ae1406620eafdf0129a90b2152c6b460a7015a04ea16bb6bcbfc1121832d734a7123d86889efffa0ec9e24d4b2a6734a7123d86889ef77e9b1c9e36e3369f8141b8aceedcb4ee624008aa2c2620c'

from pwn import xor
# .png files all have headers like this. This is the first block of plaintext
png_header = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR'

#this is the first block of the ciphertext
b1=bytes.fromhex(ct[:32])

key=xor(png_header,b1)

#now we use the key to xor with the whole png header data
val=xor(key,ct)
with open('bean.png','wb') as f:
    f.write(bytes(val))

RSA

PUBLIC EXPONENT

> Crossed Wires

'''
Here we are provided with multiple public key exponent i.e "e".
Therefore, we will have have mutiple d with respective to different e values 
'''

import binascii
import gmpy2

n=21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771

p=134460556242811604004061671529264401215233974442536870999694816691450423689575549530215841622090861571494882591368883283016107051686642467260643894947947473532769025695530343815260424314855023688439603651834585971233941772580950216838838690315383700689885536546289584980534945897919914730948196240662991266027
q=161469718942256895682124261315253003309512855995894840701317251772156087404025170146631429756064534716206164807382734456438092732743677793224010769460318383691408352089793973150914149255603969984103815563896440419666191368964699279209687091969164697704779792586727943470780308857107052647197945528236341228473
print("n==p*q?",n==p*q)

n_e_values=[(21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 106979), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 108533), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 69557), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 97117), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 103231)]

phi=(p-1)*(q-1)

#writing a loop to find all the d values
c = 20304610279578186738172766224224793119885071262464464448863461184092225736054747976985179673905441502689126216282897704508745403799054734121583968853999791604281615154100736259131453424385364324630229671185343778172807262640709301838274824603101692485662726226902121105591137437331463201881264245562214012160875177167442010952439360623396658974413900469093836794752270399520074596329058725874834082188697377597949405779039139194196065364426213208345461407030771089787529200057105746584493554722790592530472869581310117300343461207750821737840042745530876391793484035024644475535353227851321505537398888106855012746117

for e_values in n_e_values:
   d=gmpy2.invert(e_values[1],phi)
   c=pow(c,d,n)
# Note: c has to be updated every iteration
print(binascii.unhexlify(hex(c)[2:]))

> Infinite Descent

import binascii
import gmpy2

n = 383347712330877040452238619329524841763392526146840572232926924642094891453979246383798913394114305368360426867021623649667024217266529000859703542590316063318592391925062014229671423777796679798747131250552455356061834719512365575593221216339005132464338847195248627639623487124025890693416305788160905762011825079336880567461033322240015771102929696350161937950387427696385850443727777996483584464610046380722736790790188061964311222153985614287276995741553706506834906746892708903948496564047090014307484054609862129530262108669567834726352078060081889712109412073731026030466300060341737504223822014714056413752165841749368159510588178604096191956750941078391415634472219765129561622344109769892244712668402761549412177892054051266761597330660545704317210567759828757156904778495608968785747998059857467440128156068391746919684258227682866083662345263659558066864109212457286114506228470930775092735385388316268663664139056183180238043386636254075940621543717531670995823417070666005930452836389812129462051771646048498397195157405386923446893886593048680984896989809135802276892911038588008701926729269812453226891776546037663583893625479252643042517196958990266376741676514631089466493864064316127648074609662749196545969926051
e = 65537
c = 98280456757136766244944891987028935843441533415613592591358482906016439563076150526116369842213103333480506705993633901994107281890187248495507270868621384652207697607019899166492132408348789252555196428608661320671877412710489782358282011364127799563335562917707783563681920786994453004763755404510541574502176243896756839917991848428091594919111448023948527766368304503100650379914153058191140072528095898576018893829830104362124927140555107994114143042266758709328068902664037870075742542194318059191313468675939426810988239079424823495317464035252325521917592045198152643533223015952702649249494753395100973534541766285551891859649320371178562200252228779395393974169736998523394598517174182142007480526603025578004665936854657294541338697513521007818552254811797566860763442604365744596444735991732790926343720102293453429936734206246109968817158815749927063561835274636195149702317415680401987150336994583752062565237605953153790371155918439941193401473271753038180560129784192800351649724465553733201451581525173536731674524145027931923204961274369826379325051601238308635192540223484055096203293400419816024111797903442864181965959247745006822690967920957905188441550106930799896292835287867403979631824085790047851383294389
p = 19579267410474709598749314750954211170621862561006233612440352022286786882372619130071639824109783540564512429081674132336811972404563957025465034025781206466631730784516337210291334356396471732168742739790464109881039219452504456611589154349427303832789968502204300316585544080003423669120186095188478480761108168299370326928127888786819392372477069515318179751702985809024210164243409544692708684215042226932081052831028570060308963093217622183111643335692361019897449265402290540025790581589980867847884281862216603571536255382298035337865885153328169634178323279004749915197270120323340416965014136429743252761521
q = 19579267410474709598749314750954211170621862561006233612440352022286786882372619130071639824109783540564512429081674132336811972404563957025465034025781206466631730784516337210291334356396471732168742739790464109881039219452504456611589154349427303832789968502204300316585544080003423669120186095188478480761108168299370326928127888786819392372477069515318179751702985809024210164243409544692708684215042226932081052831028570060308963093217622183111643335692362635203582868526178838018946986792656819885261069890315500550802303622551029821058459163702751893798676443415681144429096989664473705850619792495553724950931

phi=(p-1)*(q-1)

d=gmpy2.invert(e,phi)

m = pow(c,d,n)

print("plain text: \n",binascii.unhexlify(hex(m)[2:]))

'''
plain text: 
b'crypto{f3rm47_w45_4_g3n1u5}'
'''

> Marin's Secret

'''
We will Directly Decompose N
'''

from Crypto.Util.number import inverse
import binascii

n = 658416274830184544125027519921443515789888264156074733099244040126213682497714032798116399288176502462829255784525977722903018714434309698108208388664768262754316426220651576623731617882923164117579624827261244506084274371250277849351631679441171018418018498039996472549893150577189302871520311715179730714312181456245097848491669795997289830612988058523968384808822828370900198489249243399165125219244753790779764466236965135793576516193213175061401667388622228362042717054014679032953441034021506856017081062617572351195418505899388715709795992029559042119783423597324707100694064675909238717573058764118893225111602703838080618565401139902143069901117174204252871948846864436771808616432457102844534843857198735242005309073939051433790946726672234643259349535186268571629077937597838801337973092285608744209951533199868228040004432132597073390363357892379997655878857696334892216345070227646749851381208554044940444182864026513709449823489593439017366358869648168238735087593808344484365136284219725233811605331815007424582890821887260682886632543613109252862114326372077785369292570900594814481097443781269562647303671428895764224084402259605109600363098950091998891375812839523613295667253813978434879172781217285652895469194181218343078754501694746598738215243769747956572555989594598180639098344891175879455994652382137038240166358066403475457 
e = 65537
c = 400280463088930432319280359115194977582517363610532464295210669530407870753439127455401384569705425621445943992963380983084917385428631223046908837804126399345875252917090184158440305503817193246288672986488987883177380307377025079266030262650932575205141853413302558460364242355531272967481409414783634558791175827816540767545944534238189079030192843288596934979693517964655661507346729751987928147021620165009965051933278913952899114253301044747587310830419190623282578931589587504555005361571572561916866063458812965314474160499067525067495140150092119620928363007467390920130717521169105167963364154636472055084012592138570354390246779276003156184676298710746583104700516466091034510765027167956117869051938116457370384737440965109619578227422049806566060571831017610877072484262724789571076529586427405780121096546942812322324807145137017942266863534989082115189065560011841150908380937354301243153206428896320576609904361937035263985348984794208198892615898907005955403529470847124269512316191753950203794578656029324506688293446571598506042198219080325747328636232040936761788558421528960279832802127562115852304946867628316502959562274485483867481731149338209009753229463924855930103271197831370982488703456463385914801246828662212622006947380115549529820197355738525329885232170215757585685484402344437894981555179129287164971002033759724456

p = 1475979915214180235084898622737381736312066145333169775147771216478570297878078949377407337049389289382748507531496480477281264838760259191814463365330269540496961201113430156902396093989090226259326935025281409614983499388222831448598601834318536230923772641390209490231836446899608210795482963763094236630945410832793769905399982457186322944729636418890623372171723742105636440368218459649632948538696905872650486914434637457507280441823676813517852099348660847172579408422316678097670224011990280170474894487426924742108823536808485072502240519452587542875349976558572670229633962575212637477897785501552646522609988869914013540483809865681250419497686697771007
q = 446087557183758429571151706402101809886208632412859901111991219963404685792820473369112545269003989026153245931124316702395758705693679364790903497461147071065254193353938124978226307947312410798874869040070279328428810311754844108094878252494866760969586998128982645877596028979171536962503068429617331702184750324583009171832104916050157628886606372145501702225925125224076829605427173573964812995250569412480720738476855293681666712844831190877620606786663862190240118570736831901886479225810414714078935386562497968178729127629594924411960961386713946279899275006954917139758796061223803393537381034666494402951052059047968693255388647930440925104186817009640171764133172418132836351

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

pt=pow(c,d,n)

print("plain text: \n",binascii.unhexlify(hex(pt)[2:]))

'''
plain text: 
b'crypto{Th3se_Pr1m3s_4r3_t00_r4r3}'
'''

> Fast Primes

PKCS#1 OAEP (RSA) - PyCryptodome 3.15.0 documentation

To view the key.pem file

from Crypto.PublicKey import RSA
f = open('key.pem','r')
key = RSA.import_key(f.read())
#Get the n and e value
n=key.n
e=key.e

Final Solution

import math
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
import gmpy2
import binascii

e = 0x10001

p=51894141255108267693828471848483688186015845988173648228318286999011443419469
q=77342270837753916396402614215980760127245056504361515489809293852222206596161
n = p * q
phi = (p - 1) * (q - 1)

d=gmpy2.invert(e,phi)
#Note: don't forget to change the datatype of d from mpz to int
d=int(d)
key = RSA.construct((n, e, d))
cipher = PKCS1_OAEP.new(key)
ct="249d72cd1d287b1a15a3881f2bff5788bc4bf62c789f2df44d88aae805b54c9a94b8944c0ba798f70062b66160fee312b98879f1dd5d17b33095feb3c5830d28"
ct=binascii.unhexlify(ct)
pt = cipher.decrypt(ct)
print("Plaintext: \n",pt)

'''
Plaintext: 
b'crypto{p00R_3570n14}'
'''

> Signing Server

Source code

#!/usr/bin/env python3

from Crypto.Util.number import bytes_to_long, long_to_bytes
from utils import listener

class Challenge():
    def __init__(self):
        self.before_input = "Welcome to my signing server. You can get_pubkey, get_secret, or sign.\n"

    def challenge(self, your_input):
        if not 'option' in your_input:
            return {"error": "You must send an option to this server"}

        elif your_input['option'] == 'get_pubkey':
            return {"N": hex(N), "e": hex(E) }

        elif your_input['option'] == 'get_secret':
            secret = bytes_to_long(SECRET_MESSAGE)
            return {"secret": hex(pow(secret, E, N)) }

        elif your_input['option'] == 'sign':
            msg = int(your_input['msg'], 16)
            return {"signature": hex(pow(msg, D, N)) }

        else:
            return {"error": "Invalid option"}

listener.start_server(port=13374)

Solution

from pwn import *
import json
s=remote('socket.cryptohack.org','13374')

e_n_value=json.dumps({"option": "get_pubkey",}).encode()
ct=json.dumps({"option": "get_secret",}).encode()

 
print(s.recvline().decode())
s.sendline(ct)

secret=eval(s.recvline().decode())
ciphertext=secret['secret']
flag=json.dumps({"option":"sign","msg":str(ciphertext)}).encode()

	s.sendline(flag)
print(s.recvline().decode())

'''
Output: 
TODO: audit signing server to make sure that meddling hacker doesn't 
get hold of my secret 
flag: crypto{d0n7_516n_ju57_4ny7h1n6}
'''

DIFFIE-HELLMAN

Group Theory

> Additive

from pwn import *
from Crypto.Cipher import AES
import hashlib
import json

s=remote('socket.cryptohack.org', '13373')

#decryption from DH starter 5 challenge

def decrypt_flag(shared_secret: int, iv: str, ciphertext: str):
    # Derive AES key from shared secret
    sha1 = hashlib.sha1()
    sha1.update(str(shared_secret).encode('ascii'))
    key = sha1.digest()[:16]
    # Decrypt flag
    ciphertext = bytes.fromhex(ciphertext)
    iv = bytes.fromhex(iv)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)
    return plaintext

alice=s.recvline().decode()
alice=eval(alice[24:])

p=int(alice["p"],16)
g=int(alice["g"],16)
A=int(alice["A"],16)

# Finding a value
a = A * pow(g, -1, p)

bob=s.recvline().decode()
bob=eval(bob[22:])
B=int(bob["B"],16)
shared_secret = (a * B) % p
print(f"shared_secret:{shared_secret}")

aes_value=s.recvline().decode()
aes_value=eval(aes_value[24:])
print(aes_value)
iv=aes_value["iv"]
ciphertext=aes_value["encrypted"]

shared_secret = 781375771286523921682177536065612640494056443200091700967335945135067268298653748996108322420480852844610393628365669516985078401109539826459162980506903623821748901865082257455849415791691881709448350504513673033914283456716068601658189110168981077531264035904750408276794096099693262398008324260587814780721089721047526118391598923434827194939854735798577674170203256314333942485760352085036751905369610326318228197468392759214066509014997981777945415195218141
iv = "c396c71ca5eef7b2b03f359e30de5924"
ciphertext = "5fcf13b8d7e15aed2ac21bbe0ab732e9e75b221a4cba4c90cd63a25d0dda2461d24bc10e7dceffc0fe5a9b4ae00bfaa7"

print(decrypt_flag(shared_secret, iv, ciphertext))

Note: There was some issue with my script… manual decryption works fine.