Cryptography

Challenge
Link

Snab? Yes, Snab (397 pts)

Secure Channel (479 pts)

You AES Me Up (482 pts)

Snab? Yes, Snab (397 pts)

Description

-

Solution

Diberikan source code sebagai berikut

from Cryptodome.Util.number import*

e = 0x10001
s = pow(p + q, 2)
n = p*q
a = pow(s, 3, r)
b = (s - q*(2*p + q))*r

m_list = [findme]

c_list = []
for i in range(len(m_list)):
	m = bytes_to_long(m_list[i])
	c = pow(m*r, e, n)

	c_list.append(c)

output = open("output.txt", "w")
output.writelines([str(i) + "\n" for i in [e, s, n, a, b, c_list]])
output.close()

Terlihat terdapat beberapa parameter tambahan hasil operasi dari parameter lain yang dituliskan pada output.txt , jadi disini kami menjabarkan parameter yang ada tersebut.

s = p2 + 2pq + q2

b = (p2 + 2pq + q2 - q(2p + q))*r
b = (p2 + 2pq + q2 - 2pq - q2)*r
b = p2r + 2pqr + q2r - 2pqr - q2r
b = p2r

n = p*q

Karena b dan n sama sama memiliki faktor p , maka lakukan gcd didapatkan p , selanjutnya bagi n dengan p dapat q. Untuk r tinggal bagi b dengan p2 . Sisanya tinggal decrypt rsa kemudian bagi dengan r.

from Crypto.Util.number import *

import gmpy2
b = 1443061772954701335732136128869248910288995712185482317126411260349775148932784597588115548780067761993841192961205698418501468762734686695975550561598140253475710348439640002745286347562
n = 121789376487960809489253386587170686658768726657045553214623415992384832614485249137256874454267032401365173859563210814953487893574413409932117585950570225259024509903129746392143101
p = gmpy2.gcd(b,n)
q = n/p
r = b/p**2
e = 0x10001
enc = [95844532553991737600355244654272099305361975575150371319709729091243030203575898742071987199800250922501746626433985253038713853151746857514762678605619742310839669559545627531098676, 42098262117872607180245376226279234844537189667792611290978137770131205295202393318329675438677406769928295941768074280915365884838027414974072838410934952571392616562898636004189303, 8604504123043858588289398284978073629384165878986588408956445422750740896636700840713408309772547146776823067482307495576552057400894861616123713400577813256614795674220942022738198, 66896916235028791010554130879834163456721897024453929564151545727202320039792487273512943832159287883050106923587075192390665897004465138382234040927275478139131450371794658563343368, 88176130128782413821390318550151008388570132120182664342566671328546119423517817326934034720909238554168653863093116429325532932401977519369212892117707167802400008407395125896733332, 42250039274640778630603717605163827961176577828564055370588929192401015587247485151024369147022833032549004175634147831360114651662490704138925606397505368573040950634048151235675964, 106267843822546752528780879737401351948170741446817769684516569656816005147897267321452764634553751488085440938706773625287154372645991244141121226180609731226228509942129690482744498, 7344462713592491879813960159075800353984094813742489003735150623847056840460595091048879286634691169764793649426176975158414555454778075430233699780146900520609629142406422725693811, 68155732896092345896827379516624133280166986984023541993085330906321960888421556683672078055376548346464764100036149614632795220030187229733989823788323988946361921828069707823065198, 2456638129741631242062051214133833843357605035108383884677777076160879939756985403557604264648903511528401478876871578775440101482814072714355366084122429853207060638683606389504551, 99671982271645788903414016384550975165361965345980177928115018027271173062935625698434769263846972984813377601618481025600240081090732166957299336765744471217496851539810214590361856]
phi = (p-1)*(q-1)
d = inverse(e,phi)
flag = ""
for i in enc:
    flag += long_to_bytes(pow(i,d,n)/r)
print flag

Ternyata didapatkan source code lain

#Snab says good job! But you're not done yet
flag = findme
halfa = ''.join([flag[i] for i in range (0, len(flag), 2)])
halfb = ''.join([flag[i] for i in range (1, len(flag), 2)]
p = bytes_to_long(bytes(halfa, encoding = 'utf-8'))
q = bytes_to_long(bytes(halfb, encoding = 'utf-8'))
r = 0
while (not(isPrime(p) and isPrime(q))):
	p += 1
	q += 1
	r += 1

Karena nilai p dan q diketahui jadi kurangi saja dengan nilai r dan didapatkan flagnya . Flag dibagi berdasarkan ganjil genpa jadi tinggal reverse saja.

from Crypto.Util.number import *
import gmpy2

b = 1443061772954701335732136128869248910288995712185482317126411260349775148932784597588115548780067761993841192961205698418501468762734686695975550561598140253475710348439640002745286347562
n = 121789376487960809489253386587170686658768726657045553214623415992384832614485249137256874454267032401365173859563210814953487893574413409932117585950570225259024509903129746392143101
p = gmpy2.gcd(b,n)
q = n/p
r = b/p**2
e = 0x10001
enc = [95844532553991737600355244654272099305361975575150371319709729091243030203575898742071987199800250922501746626433985253038713853151746857514762678605619742310839669559545627531098676, 42098262117872607180245376226279234844537189667792611290978137770131205295202393318329675438677406769928295941768074280915365884838027414974072838410934952571392616562898636004189303, 8604504123043858588289398284978073629384165878986588408956445422750740896636700840713408309772547146776823067482307495576552057400894861616123713400577813256614795674220942022738198, 66896916235028791010554130879834163456721897024453929564151545727202320039792487273512943832159287883050106923587075192390665897004465138382234040927275478139131450371794658563343368, 88176130128782413821390318550151008388570132120182664342566671328546119423517817326934034720909238554168653863093116429325532932401977519369212892117707167802400008407395125896733332, 42250039274640778630603717605163827961176577828564055370588929192401015587247485151024369147022833032549004175634147831360114651662490704138925606397505368573040950634048151235675964, 106267843822546752528780879737401351948170741446817769684516569656816005147897267321452764634553751488085440938706773625287154372645991244141121226180609731226228509942129690482744498, 7344462713592491879813960159075800353984094813742489003735150623847056840460595091048879286634691169764793649426176975158414555454778075430233699780146900520609629142406422725693811, 68155732896092345896827379516624133280166986984023541993085330906321960888421556683672078055376548346464764100036149614632795220030187229733989823788323988946361921828069707823065198, 2456638129741631242062051214133833843357605035108383884677777076160879939756985403557604264648903511528401478876871578775440101482814072714355366084122429853207060638683606389504551, 99671982271645788903414016384550975165361965345980177928115018027271173062935625698434769263846972984813377601618481025600240081090732166957299336765744471217496851539810214590361856]
phi = (p-1)*(q-1)
d = inverse(e,phi)
flag = ""
for i in enc:
    flag += long_to_bytes(pow(i,d,n)/r)
a = long_to_bytes(p-r)
b = long_to_bytes(q-r)
res = ""
for i in range(len(a)):
    res += a[i]
    res += b[i]
print res

Flag : COMPFEST13{y0U_d1DnT_3xpEcT_t0_FinD_pQ_4s_a_fl4g_DiD_y0u_7e1877a801}

Secure Channel (479 pts)

Description

-

Solution

Diberikan 4 source code , disini kami melakukan analisis pada file inti yaitu dimana percakapan antara bob dan alice terjadi.

#!/usr/bin/env python3
import sys
from base64 import b64encode, b64decode
from Crypto.Util.number import getPrime, bytes_to_long  as bl, long_to_bytes as lb
from secrets import Alice, Bob
from chats import alice_dialogue, bob_dialogue
import time

class Unbuffered(object):
	def __init__(self, stream):
    	self.stream = stream
	def write(self, data):
    	self.stream.write(data)
    	self.stream.flush()
	def writelines(self, datas):
    	self.stream.writelines(datas)
    	self.stream.flush()
	def __getattr__(self, attr):
    	return getattr(self.stream, attr)

sys.stdout = Unbuffered(sys.stdout)

try:
	g = bl(b64decode(input('g: ')))
	assert g > 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
	p = getPrime(512)

	alice = Alice()
	bob = Bob()

	alice_public_part = alice.make_public_part(g, p)
	bob_public_part = bob.make_public_part(g, p)

	alice.make_private_part(bob_public_part, p)
	bob.make_private_part(alice_public_part, p)

	print('p:', p)
	print('Alice\'s public part:', b64encode(lb(alice_public_part)).decode())
	#print('Bob\'s public part:', b64encode(lb(bob_public_part)).decode()) # Bob doesn't want to share it to you :(
	print()

	assert len(alice_dialogue) == len(bob_dialogue)
	while True:   	 
    	for i in range(len(alice_dialogue)):
        	print('Messages from Alice:')
        	msg = alice.send_message(alice_dialogue[i])
        	print(b64encode(msg).decode())
        	print(bob.receive_message(msg))
        	print()
        	time.sleep(0.5)

        	print('Messages from Bob:')    
        	msg = bob.send_message(bob_dialogue[i])
        	print(b64encode(msg).decode())
        	print(alice.receive_message(msg))
        	print()
        	time.sleep(0.5)
except:
	print('Something is wrong.')
	exit(0)

Sepertinya tidak ada yang aneh , kemudian cek file secrets.py

#!/usr/bin/env python3
from Crypto.Util.number import bytes_to_long as bl, long_to_bytes as lb
from Crypto.Cipher import AES
import os
import random
import string

sp = list(map(ord, list(string.printable)))

def pad(msg):
	pad_length = random.randint(20, 100)
	for i in range(pad_length):
    	c = random.randint(0, 255)
    	while c in sp:
        	c = random.randint(0, 255)
    	pos = random.randint(0, len(msg) - 1)
    	msg = msg[:pos] + chr(c).encode() + msg[pos:]
	while True:
    	c = random.randint(0, 255)
    	while c in sp:
        	c = random.randint(0, 255)
    	pos = random.randint(0, len(msg) - 1)
    	msg = msg[:pos] + chr(c).encode() + msg[pos:]
    	if (len(msg) % 16 == 0):
        	break
	return msg

class Person:
	def make_public_part(self, g, p):
    	return pow(g, self.secret, p)

	def make_private_part(self, gx, p):
    	self.key = pow(gx, self.secret, p) % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    	self.key = lb(self.key)
    	while (len(self.key) != 16):
        	self.key += b'\x01'
    	return self.key

	def send_message(self, msg):
    	iv = os.urandom(16)
    	cipher = AES.new(self.key, AES.MODE_CBC, iv)
    	enc = iv + cipher.encrypt(pad(msg))
    	return enc
    
	def receive_message(self, enc_message):
    	try:
        	iv = enc_message[:16]
        	enc = enc_message[16:]
        	cipher = AES.new(self.key, AES.MODE_CBC, iv)
        	msg = cipher.decrypt(enc)
        	return 'Message received!'
    	except:
        	return 'Message not received!'

class Alice(Person):
	def __init__(self):
    	self.secret = 0 # REDACTED

class Bob(Person):
	def __init__(self):
    	self.secret = 0 # REDACTED
    	assert 2 < self.secret < 100

class You(Person):
	def __init__(self, secret):
    	self.secret = secret

Terlihat pada constraint pada constructor kelas bob bahwa nilai secret > 2 dan < 100 . Jadi bisa dibruteforce . Yang jadi maslaah kita tidak tahu plaintextnya apa untuk pembandingnya, tapi bisa diatasi dengan melakukan >= 2 kali request ke server dan melakukan decrypt keduanya dengan key 3-100 , jika ada nilai yang sama dikeduanya berarti itulah plaintextnya. Berikut contohnya

a = ['u\'!IIC5uB)sv>#$`NcR`j g8|LBE#"CoI2\x0bvSrE>]1Qmd2G.)T>oL', "i5\x0b_of~\x0cFR0d9[}>A6GW?twwM_\tM\x0caQ7\n2:x`(;<YX5_<?K'[Q", ':d90)=;j\nOOhSakTs/$\x0cxE& SU/#^aE;tdRw\r\\TRc2_J#R8UjyFXk<83[ ej', 'q{?wd3?Rw?5\x0b{Q:}2IUQ<6k3`f4G^QOAQSjn:Is|K(DR!\x0c5Jmjt\x0c5qY7SB~', 'Fm!DH9YyyK$M2il\n=*F;nT3ya)YF*,8Q"KJ~05+n}ZQ(0F[6.|F\r`})H4J', '`;G,x2]d\\(ec QSFeB*a\x0bM\\W$wB1W2sd}|l*Tx1mOj39 5q%#Ei\x0b\x0cf%]2UZ,E', "T}>)G;Qhw5.X3<\x0b&A5c9OT-bW=\r$D'%U8aQeY~rl?FL/f\x0b5B+pBFZ{k&qVyGZ", '|D6/DTcA!`dG1>/{}DZj&n,A0lW<&vesAXm.\t5.O)y,1\x0c7DktZ', 'e$W[,"R:(eUIxI_\tVnD_(6\rYgQ+v};fhk|R9z6;p#j|84;#!\tbi\'ZJh\x0cD.zHW,', 'B1Z\njP\tyM,YI@>GW6_g}B=a[!"<d}z\n/W`e4C8\x0cij5r&C*?E}$Hl"', '\x0b&,Csm!_p OX!`4lQ4WHLHC\rMi;NFVu<<nJ})>)M*rqFe"\rzj]TA4G$oot]+', 'KPKjGz#_s2\t6s:U9R2K6B\x0cjId\tbWVUr?(5rJ\\i<j#@7];[Tb]6$z;`* _i', 'FYT3QeGtN>k759?ik$OxAyd0Tq^g6m+1 *B)dSVjQA?xGzRJ-n', 'gdM`xdyh;i7s+!8kCQ7\x0b9WN!y@L;\r\txTIy1&A3\x0b!1wyX|^p2Fu4Zek@zpJu>', 'QWOcQf})(>U)8j7r*O\'&pt1I\\qImo*Y\nS knwa9DBPp6-+c[fQL6\x0c"=V}?1%usP7sh', 'VmK}R@Le4W\nVuylN+a.IWNG\'~\nDv{Q"^M5SD?XdB<RD\nu5X\rof0O!=e%hz(U$GBi', '9u0lfBbln_8^*f\t84yqt7S%X=}foRq3uA]_>M|A\t&A\x0b"HzKh(Nx=~', 'q5uK#uh-Pn\nLGgKj\n[,`\thT3T!D"\nZj6<!_9qqLNs+P[[WqQe+Q:%j6x9;\r/ I4W\x0c', '\r\x0cgrF]?]L%6%=T9\nc<@fN/o^vvSj<3awRw5W\x0b660e|:bh902gZNl[', '\n\x0c2}o\x0bV8|PpEQg~D~gl<Bc5%O.{)VK7\r4xTm"A9ag\rEh]~PgCJn[=,_=3', 'sVB=~hl_Xt\r>aCiW\t}jR{V2i?S*p5X;\nuOW<q[2\x0bCt}h', '%=K)T?>4w(\x0b^n|%=7r$JjwVAR/2F^RxCM(gQ"\tECz.2WJyLDsn|\'Rrb', "M6%dk\tggHahExLpbZ\\leq\x0b+8KIP xai1l!D6+yA%'kh/)^AU}", "J@qEEQw=tzkWT\x0b$9.[\rd9\nc^2Q1mODKwDpb&PN>yFE1>\rX3{(#QF0{'r!pCA~\r", "c'CC6DIl7L2\\`G\x0bh9gSR>$/k&`]%[!PIOMw[<'[k\\hS*EQxE&#\nv]D", "+047Q}yk\x0b\r-:$\\\nb;9!\\5mhSk)M\nb\nE'|)A##e|sI:.iu%bJ]", '0f\\I0EC.Vr^!<%4[!\\[[WqoG*=&gD+4/Rv `?:6Qy[EmHsVh\rElUl&>iO,E', 'vw*3j%V_:g~(V\x0bKJ\x0c:!E,fqM^J.{*@QGQ$_R"M\x0c42\tY\x0cA^ZSYi{A#XS;1\x0b', 'F0Z@2 o29/Bk9U@,a%Zf+@O0op$9+(K=24\x0bJB&0"4nmc$B<c', "''kEo?\x0cZdl2m@]5\r;mll\x0bq4>Ms#wSKLF\x0cETcf\t\rM>W%+pl^E$;~v\\BW\\n<", 'JA\\Rd(e\\3bN\t[L}4E`}xtGx;bN_3yW?<J}Zn}jKOX?_^\x0b"', "(9-JF)H5+T#&>DtaQ,'MeT/;EAK,s%VtD*n(\x0cuH3%Af|!WOZj\x0bK", 'Zk2:+%R!WN~\'\'<|^yOO\'B1EO%V*f8]$zDsk5ecs"oQ]zd?a`&B"r8CWh8%Zv]jJ}UV', 'mJ\rD\tw5];K\ruvW*\\\\!JVx%30u*1r\n>V(k<83W^HJ5;\n]_OkyKO#JT\tx{(Zg5FJp', 'q)Vk)R]^ctLIy\tY?NS015COM2($(X{Xcm0SzHKx->5pUwq_HlX;+[', "' m[~\\<]3T!qhhmudnr\x0b|}6DAC{x|%$8\nL\x0b':E(a$<IBt\x0b\\R3dQdL wN", '.lxQQ58C2:/RMo uSxkk;`>GSDp/9WSI#X2l}4%FWYlXQ', 'W6PRO<Z#h/-Mw\r|bNNvUCvJQ.(uu~Mqx:UFK\\;8Fh `e-Zd>o=F>4&KGXw:', 'J Dhx)JI77-O_ukY5duBW3obe [$3JP{U@,>PZMZYSiQtFd&6P422.@~Ekq', '-5|okNIX+\\q\t1&U\tD-\t$k(?Od{,Q94l!(!K?\x0c>7~;mg\x0clclV5dYrWQF[dg', 'S|~W?r" Oft]rHW%6(Fg2|.O\x0bdBSM\nKU(vtQ-/t7H@"y/oqNp;}Ag5X\x0b', "XhZ&x(\t%Wc#\\ s`k%vE\x0c51>'\nh.bgb|:R6k(R@Y{JB-/5z!%MAmb!", 'l\'\\k[\t\x0cFTkr!~M4(^l]JhN!%?Y\'vZS^,U#!j#5F1|\\{"4(a,<N=d\x0bK\x0b{', "Ve~>unB\\oPFAy%n\nCwP;\x0c`KW:@af5tlQDUnMrNNd&1W:\\T*Mm.QtN/G)X7uvob'", '(~`@la{Xo&rDh 5X:O:\x0cTv7s# 92:b\n7r7M)"9{E4xe.1JT]Zl<d9VaXgma', "+2.#\\ym~N^:v[~>49=\x0c\nm8\r'X\tv~o4vOj8$oO*|y 6\\}G(\tel=\rX:", 'B,J-,IIR"H&vfDloX;O_1GyF3Awh|8cN8+TquT\\u5!Qx`ZTl*U+h', '9W6aq=OrK?N@R;}Rq\'"N\\K2cTf@tM9k&{7opKgO/Ac\t$mt`:rT2%6id', 'lP}7qo%#\x0bePtd.LTueAkj6\t]K6#}w%cJ4^wLb,0[+[cCz\rdl.l,,R6EZSM', '# u#Xb+,7R~mk-L7@Y5<a@U7!\x0bSZXTU{Y;8jPf\x0cS#k>0Hz4xhEW>Z;', "-L>e\x0be;_E{ U8fgqC:a6'n\\2~oR53_%\ns<L^yv;3qLB.|2F;%M", '\t>\x0c^!XErP3F7i<)DN 7GBzTw\t\\]mm-Y|rBnqd{-_R}|~&SpEa i', '}[q}DCn*jO&:l<I6"NiP<Cr& (_\nCZ~|R"@:iyh>h}xs$/wjENxnmiU:I>?O', '1iEEr\\Q F\'OT}Y"czek\x0bKVVw\x0ciP:yV\t\x0bYg>ldQAibya/P4J}I:D\'kb.O0', 'B/5bP]+PJ9hYwOp6AW=[W ?w|m&d>r\n<bn\\DeG`SkFF#@N_', 'u\\=\r,r"BDKmB\n47xYe|n(L5@tOwEH::{F[%]V\x0cK\te!OMq\n\x0c ,*gMr@,75]tPYf', 'h:!r\rdLdEBRL]SO(;"\x0c\x0c:&d(H6#>Nk`s~ur58=\n-L<P8p%)pgmm(.', '\'DfiKGH2"AwFPj^rX.J0T]\x0b\x0bC!\x0c\x0c1\\ecYv+!9=\t;ld,T+\rzB\nn,~; ?)\t[', ':zH(l@/+Z+F 2R+xKjT)-b_P6Wm)LlEWgc,LN$}M"!DLBu.>5sp#^TPs92', '4p(sM&E(&J*t/(Fz7\roIe$|\n;N\x0cg@1-AEq7PEb0 TM&QuB"GyT\n', "e+Q{>1Z2Zl){>:g7jpQSs,)b{P_T'I>ZKFRIxayPLGjt[d\rajZ1X:A({Wou&O9\t", 'dUVY/q,[qd%#<,Kl*75[6-\\k9J3G(v;aH;W3[;JOw)Ts92\n>Ce#}jz}', 'PY;o{?\\xXxb>[ASZ`>S3hC])?(c?.g$ByAGm~+zwSDG8)y5p;:/KT8*8^56\\t)b)', 'X:Ce8=5C[/67HX`1re~9\n&uR%D]qe1\x0c-<.C 813F6I\\sm9\x0b$5Ksbl\tM.x_dLRM%', '\tGmy#vDpUG\x0br7\\!g!j77rdr*Y`\\X\\/Jo\x0bccfEo<m3yau&\x0c38cul>*CkyO>', '?\rk$#JDxy&cF`;qoQ^P8\rk-(34DoS.\r<*:w~*/1[eE\t5XZoJgr@=YD', ':+.?UCx,7Y7bJCi;-}<QZ2v9 6vPTX8\x0cO7KV,n?kFh~^E7&:Nw[ahI\x0bm]rfr x^', 'Fc>?sHoxPk:Rp$`<dAPf~xAjq\x0cx"0j?m"@_(:\t]G4~8()arYuN~\rWX', ' ;iy$\\=-FC_T?%GJY%[8DjQ=*h \x0cG~gfPNP?4n}3k322A+/=A"Ksmb', 'y\'sEx37\x0cL,!0j]BToIi8\nOU4r6#>s<KZ1n@\nq#T1ggz)?keSoTbyLU"vn', '87d&', 'Hf{5!UUvHa8%>O6!2V$1dz{:3oC]_\tjz4P1BB/e!37u>/"NS5;(%!oz3', 'K~.\t>VWc\r%{\tMEZ_g>\t5{gfr\\4EIO3;ZXmG/S-sFb([|zH', "I*=[CLY0_m'uLWQ.ZeW\x0cW5Y60#{H\nhcK\t^bI!!k:HKj)w<[r<)cCL!%+c:\t9N`B", 'QqW\t!LU*pO$No\tRGZ#U5NzW};$p\x0b>\r`}IOKD?&M+.\tC-CL7\t*+CNk7j=YG,X\x0b]:', '1$2Q,W)/hS]TjT6ybpw,*Mie1Gl%;0F*5i!7"n6?\r)!Yc!CWa', 'zA/:ZZjpFU/^i3f@.zdWFX0\rWz7Pf|\\\tQNB$Eu@R1]u%F?O}h]e+g)2D\n[z', 'Tf7307+xA4{&*>\nm8|\x0b7,#\'Gj{X$w3LcS*Mhx_)S`,|\\-F]S"4t]p@0|SW(oyW:', 'QD*T.1-:_-/l)y|a"i=)X\x0b!J8pP|,x}.9\\!+zju]`EV)k\\\\cRIS\x0cql', 'oX^{?p|a\\\nbZq>3PR|m8}"m0(N=1lI\x0bzR[<P5B\'#,#\rE@0a%L', 'p(ZYyrHYU<bPcmsB8]\x0br-$>p2 eNz~2goGcOqK5&&7o \\T.T=(f%;N-SI\x0b`V5\t,', 'Rhg:|$S;mOqm s%9W\rT6J)\tvJ9\\PP1=X8D!T0$7\t_TN#8\n&q1', 'j(C&`{ofr5@Y\'"lBfe5<pU S_A1-WJC0ThCP86"X[?iQ<CClqdPUA<(PnBllLE\'6@4p3\r', " 1-vmf>eMl1sHPJ9)0nUW~B?'@TZ^LK<smZH\rDRFg(M\tO?9<<;[1\t", 'm6+BB\\Wgr^lcVI#OJ%71PQz&cAx>Q<\\Y#~yR[5wCCF nx"[\nUu0A6$[G', 'e\x0c/Y8\t.swwc-Y"$Ip0IuQ2brp|b{{/\x0c}Y*9hn5{uV!eu\'JdM%*Ts.:%5cs\'dnk$', 'ks3+rcgyzx?[^^^\x0b9\r5H}7DH`8Nu1JXt<rzcH2!a(Re52Q?\r#%kZD6\x0c4D', ',$YFPHA*\nE)"N\x0c-J%QY\\RY(j\r}8s<\tj\nn:HDJ@PRA??!z`oMucUUJ', '(mlr~qT)RX|SML>^&sTwk0?di@@V|%#8B r]^_ZUE0YJK|&s', ';-cm!*J.LXvqCy#\x0baO#f^-]\r@ -Z7zb=2=d`JAp"\rLK s`<-H>~<G/NWKsi', 'Jr%!uH\rlG_Y+$qd8~{+MnVH&7+M>nZROMPi5DE@xFmtN{pa{Lk', '/ \\J^(#+aMp3c{fe>TVtdo?RxrPl*w4wcE`WWf$ZS7fh ({k2 1!6p\tLM~FZ CJs', "Cn#;_2i.ztS&81IJ9DJ1Ph b<'`# nA\r<N0[t:()\t9Hu 9m@`,\tv_W!#UTg)", "\rimH'Icq:C<t&Wd?VV9}fkp8d~b\x0c%}--|@\n<\x0bu ^lcaz6&cF`tV", 'i0AnV-JxT+:WSe{l)Xz{J\x0cpF@\\K0>\x0b\tbWFFjEKd\x0b+w$gO3bx_,z<~C~v^y}gz', "%{v*b8-c]`vR*\n#FXX'657w!+^NeG>SpX9;V:\n\r", 'd^.ENb\nBYuvlZl\x0buFPH.NOaa\\+\x0b1#YmFfPH_2Aedjq4gJx^>8}\rY9l]rA-', '1\tM-,dyLj^)4z=%?z+j.utOh*1m\'Z=]\nlv@Nl@"/\x0b$*JnO!v^P?4`29g4S']
b = ['Q:`o[jzpR;sG{)-1\x0c`Qm{\nWV(3B0T0fEQ&#R`g\n', 'F}AY{fh|o\x0bn\t6cfmJol>e;>If/\tg98 ,!` {XS', 'fDNnjcfLRNVf$pT{J8\t4-fh8GI3NBj u\nLdNoiRs],#DE\np:3`', "#yHB5|qjBoR)\x0cTJ$3-\r\x0c-g=e;3!\t+fKaouqtZ3\x0b K9@@\x0bR%?^\x0c'h--6", 'DFP]\\uoPjkk@X(jz"\x0bGJ9/\x0bhx.\r>\x0bzab96IK/\r', '0&sB?hC8V7p0|C>a\t*yI\r=W~L*:})aso|>/Iy$2zUN\nN', "SH\n:1G5\nqH@`!B|bd->r'1kNF? t]XzPnqKlq\r_iC", '\tjQf-bdlkPR+!i)xSbLCl,AYNCu759!s2YzYs*  k7J}', '`({;e\'Z086Z8dtOqE6%Qjk7A2oc "G1jR\\N\'i)\no+|', '[\nhYoaD*3H;kXd^v%`an6t51Pi6zbqY",3ZiRq', 'FD8sm)+W`g|R/$D=wnO&>g#)uL)+h}lqY`m', '1^G/.a9HzCLX:sd\tkO_^;u /Qq\r@f`02r6n94', '}0,TuyS\njqk)GS:EAXm8$-8JI:fC1\rl" jpxb`_i|kAd<VKaK', ',xax]HnB6,ceDv#\nhy";MW\'6{?>k+hvl?`=\x0b{_V^3\tW', "{+Pm'Fkbj!w-@9Ly5!%PQ;\r1FlX~r5@/I`'3u@7*,", '/U$\n OBc:.:/Nbv>so1MkHT(f~[g,A.M_I%*70e#', 'u/zP}u%o7`x;\'@77XA7\x0b}\rbAA|"Cm;%$Xn)-z{jfHU%tbOEs', 'J]4\x0b*^O#h56h<k]hG,A<7zt{H 3pJ5V,pv?g\rE', 'm\x0bW^[/Sun11D#O.+0:HeZdT[v6m{!5c?.aKlNQ1;`ENY', 'z#X9+cX|jEf!kqKx\nT{Q-L]\rS1Ya=ZYNV.Y8%sU\x0b', 'jR,}\nx_;H\\\x0c"x"nt\nCkk9Vc{X2H\x0bpK;Z<jzLsuzRuZ)', 'iD.$^o[p:7Wh2\t8eWlA1n5!bl(\nD\\JCGA::ig\x0bx7nq-!', "b5}[*;.\x0cU8+26\x0c+=mNDFWkZAHCM{>lfY|#f@['u/b-K:2F\x0bB\n", "Gx7=_i=.,g4G'78iiNr=+_}!;_Y`Ps?RWQkI{4> h]n", '1y3%bO>D"\\sZ{dQ/+n?A\x0b[)1&dwu%n\x0b\\G|\tv=\x0bWO', ';Q"q3iPrS?&\x0ck\t8uK<Wv&v^!%l=r2\x0bT]k A%yW$a/sEK\'D:gS', 'bH?,~U^NR\\C8n;g>^\t\x0c2RInFn)#r/(7YfYB&UZ]', '5 +/&<pWbXI~P(2eIm\r\x0b@FMx.[G6O6L_t1ZWz_?Kbjj', "1v-^!p-JZ+I%xGdX'!G(f4<7#=9cTQh}O\x0ch@gmS\n$\\x%(D+", '!\x0cE#s=*~NP#.w:X71q!J49`cIA)\x0bICv]ql1\x0b/3/x>n9 \t*f`', '?**+{K]95KV}4EJj*cIuJWhjR\r0rW*vV&JxI]\x0b]=b_t|V=]>\\x', "\tZz@>aB!b3u`|44W4P:qP}hG>'jU_%GoZfvDum;)", 'X\x0boh4}~X\x0ccN:+@\n\t;Hngb\thN*:Y', "-&2'StT&)$;8@ZWf`OL#>_psavu\r1>7WjNnM#rfal>n5*'A\x0b%\n", 'HEi".#}Cu`Q]n;;EYLL(1:h|?DI^7vxDtS/dMOz5"', "<H='X,P%11pbh\r{ p]mFgt^oXLEHY~\nnIU>OXx\t", '-48{&,\\l\r4C6J~xz[NMxsl`YC<!I\\L*v.fq', 'l\r:\x0bdeM_/O`gS?i\x0cE6CKLq4r{.`4Y<ab5x7YRYb', '`Ao0*m4e;x"/\tA9Fy\tWND\nY+@^?=IRhQ#7j\'SN', 'bWwh"_E["S~&Ya%1ptV.4g+^~F(1#Rf;{4NWou~r#(!YahH', "VR')g\x0c,[C*Ppp{?\rn;ez34z\\@pA p\\Ib@iU@~_1&\t1dfD{4M<", 'k|&i}{n\x0b7`cdv+)z1()D.mPYqMCLb~xgo(\\Jm&\x0cA,No', 'rwnE6Y4pIJ8|8-|=#a)4+c\trHI\n|~}SRVi<\x0b.^CZ', '\x0cPK\n{x|ocx!i$`&Dx"pR>GjS 2At{ft8Kt#Kr2I\'', '4iWN>q&\\} mn!q,vZoL\x0czJm#Y=eWlzC`hZ4z>,vQ/$tk\tJ/w', '\rAYtpk%N\nQ*d zgp|y Km/Nq\tR+"G9{\x0bqL3cit', '#5){?AR]mh1\tHO35pB\\~-b&GfYINP+"i= eCzJG6L\n\x0bDGB', ']ah$xG\\2<CO5t J#f8F#(oRMxk{Y%P[18 K\nes1^+uD', '2R8^"k\'Bg\\b{\':W\tvt\t\x0c%X}HHQfv} J,i7)ix1E)$"g', ']XX;@iqQj1yVEwP{z23s\t\t,\tB`vcx 7Vj:VQ|OHRh;\x0c', 'atT\neM)%BtWaNp\\e=W>)8!ni9GM(ous19c6p,HGta2&Ogp', 'zoyE\x0b;l :<\x0ce^V:sg7Kf1W]I;m8"EzKSKI3i< 86', '&L) \rT[wMuO-li.|uz7}@\newQ?vmR^.RIs3&ZVU}62fjF', 'Y}T<~V2d\x0c$IsR[\ndZlOiW_gh|.A|*[zox+\\_d63:', 'u%Z\\e\x0byF3GmomB\r\tiO1\x0b}$K&@Y\\}!Egbg@veo {\n+,$X', '\nh75;\n34KI}x/sLjI7x;ywBX0{m;i"sAk?+.kf rR2*&[/L', 'q\r;P+,ou!>$j@;Fea?\\L%uQ\rMTq~_H*.%zND:!F5Gx6jNMM', 'tqsJ7LC6Wc{~JVgK-t,^z0>!\twpM=!U;\nVU,#lk=4n~&_? {y', "*]vTn%\r2v^%gOlI\\isO:g>{\t%|mafXJu-xV'E[(;~!bFF\n", "[PMSUhxY7H<3*X)Bc(y7p]j0U~*YwEoG*v)m\x0cpj'P", '[Zo: *z>~z>f6\t:-<\\={r\rqh#\x0b4\x0ce|3QSshn9Gd;o$y', '[A(Q5b1m\\$@wl5K`s;(s{4=<c`y/A k K10p&,M:M{W7', "*4_lQN.*Iv`}3hrJ@o1S+C=RV{]g*aII\thD'x", 'G9>_ge-c8ewI#$xL\n\x0ct.a^l#NzK/eO@RO"1H"<9\x0b,6dQO', 'g!)h.k`,|mdn;J%k=[ \x0cxg)E%X$%=\x0c~Q|u\ncH^<', '_2:g$El_B&-\tn]-Xx}\t?"<Z@UZ63vk;nv)%Qa*f', '-FXLR -H&DW&iXt)t0[F@`8Afp%;NIp[vMUp>7<{wkf9\x0cYt(7M#', "fGJMmgw~qf-kJ$I0:e:5j;s]yae('P6O*+y\x0b\x0b@7fD%,(*", '\r%\x0c\rQ>GwvU-F(t<HaJQ{kskg#IJ/H``pcJ4+&<?_-.*%zJfL', '}V$c6CihZItc*GM\x0bt8I9+ H&_\rx$zz^JM', '87d&', '@^L!U*_d?@0Ux1F.F5WjRG"yv#~ sE6SZ9wG>LB=', 'Sg%)tG~\x0c=-{sfSG1?[\x0bGD\nUQG\\gd)9:Sx/"F8I', 'D?\n43L(N+"eZ:-mkl\x0c9 oWW\t.HVGV8M.Wt', '%caqVLfic"=jrnq>NUK0Jkr!nVYs *POUbN*-J(b?Z\x0c6', '" (37p>`d\x0c!$ZGd}@R\t_$ME\t0k$cfg} Q=VYS;-9k', 'KQLch*49+*Po0cWk/A><w"wxK5pc0W6K\'W', '^f$23JA!_;)S]_i<"*RM}XI[.@yh,b_gcS&Uv\x0b3% IhR', 'bZ~GE{r-,\'\x0b^f:t2\x0bW.er-ol",_-xrN/A`gKncE)\'k{B', "A)o'$,}:,SxaQN+G|j<7ksp+nKpwRj(T\\hgCgH\x0b", '=F<E\rfNKV*d|S-,_U4"RmMtLUZ"|$,1%\tV5SRU@8>jWg`9\r\t~', ',6#\\Zp:D%S.;h;C*>=yN\x0bJ]{Bam1Vgb\rY.c9x+{VPOED# G\x0b^', ',-N%*Ml<-GAmX"!B4I8QXAI2,vXJK:L/qmNQf;;% ', 'Q_tO=2eN*tq&qttq9?$SJM1.|CHGW:&B\\V!.MA_', "s5vPeDB<$3z\ngt.\r X/L(tGho>\t\x0b3p7%'hv\rjTnmd\x0c]", 'qMXnB1{xD"!gl\t\x0c:\t9PI2H(ht#\tj8s`Q\x0ciVTHG~"K$o', 'jYX0U8I\rImnHU=?H:(/z=8oJK\'IJR0%C)"cAo_=~.S&', 'a"t8UU)P~X|34VaS.\\\'v2[ji78!RXC//k:-$!` (8hIuz^w.\tiuJ2/', "e0O3.*3Kz\x0b'dPoVw_9JW|9+#j*T#H)rC\tBR60/ohcVZ~]X`B", 'YlGE=seEh>A7;5#\t0KW\\\x0cEkTj~F~&3}F;[e,B~/Q:NJ]D<&eS2H', 'Yz\x0cz{4T/mhIgarO"g>#X4HH3|D4FCsXNPy;', "ln\x0bxckk/k m'dDbPHw5Y;p]\x0b!|sifLckq3%2q\x0bQF(", 'i]axX+_Vbp/WM.9N|[`Ck$N9Kh"dW8Y;-llzjWH~64RB', 'Jua@boN"GD)6bV\'\'2;9wv1\r8k0Bw"h;510cXs bL0-`8Br_', "boPY/?w8'6i(Z^:a`1h`i*/0I?^?2W^bj<y'IAL)QQP;GV", '\rDfA\\_Psy%^BC*(?v*HL%o_7NJ:~M#EDFNM;4\rfTVhP', 'T1!OOc~Mv)dQY@2SGEd \'\nV"QArFvfLeQ\x0cP', ' zemcYx=#&Mg}j~+2.JJNF*f]}V.O,m(J0qB/v\t( E']
c = ['v\x0b(Bw>\ttM/]cVtT=\'e]\r\\2joDEr"GO;cbo\x0c1!|(', '7:+W0q=f$DqL,Xe)^Od*_\nGTm1\x0c?$ lVHu{QI;(+B%,1cgX', "'<;2`WM:T)Ub%F\x0bo8zWWt(Mg75'l)VstQkh3q#\\>-8Ca`1;=S)", "Xb!=50b*o\x0c`/I)|f_[)efvM:`m+[clI+Y07fqs=guXFZ<j;m'", "(U*TdU':D[ql'&%lLs4?BexH}E[/9U\tEXAJFu", "3;70vYuKpd\x0cpcU,+Zms`QRu\n'zqM4~,ZV:lv]8I5.5F(@r:M", '!0l*,_kq}zI^Z\x0c@|\x0b.\n4KC?[|SA5hWC5\rZ/<3YVP]\x0b\r@', 'D[Uc,WL{1c!RQE\\PVCqOIY"n\x0bfJ2T', '<hK%:OmzE\\1O$Tb=\x0b5p"OJX3xl\\\\"vY4\'WA,7];CVWo/qA', 'r?\n<st%}r_U<?&oZk57x/V:9G@0"t~Z%><nlwJmxus4T', 'Naa/5di|VS0-=VII7JDND~5Y.@-jQ,x12+dU\\;HeM', 'IzQv"Q.WoH+\r6YS|4\'al\x0c"k\x0c*_\n;6\tU,AhLTpVGU\tm', '^!hN7p4vbh |<i{YHRxeAem_@|\n@k$\t\x0c@0<6"Z2Vs.&b', "^@k7/<C#e?b(bTw-\\ :g1G#*\t9X9@D f;t;z'E,1rd>", 'S*A$|744;gLOL5BPjBJ\x0c\x0beV/}ShCg\n{2jAM5J*Cm$\tWt2KG74_U5O', '9hxA.I}\x0b^)gfn7SV\x0c[_[pw|fJ+#~03-upai3^H?o', "GPg^fc:'@j %|?W)!CS(+]#\n\\_M?#ORD@%*zdGf/o_*fAk", '6Jj<lb!^i6N.I\\(EN.Kg*\t4q1<8ce/^<bKlZk`i{<Dnm', "`_;=Z\x0c\x0cdFYPW.!/WKm'd6\\fCnxz<.1X!~0.8e|Qv,", '.]KK\x0cucD7"I/0? D`V,\rx<)[dw@%pR f', '"\t/dQ&([tv~H`c@vx9?`bScGY\nb2Zs&O6)\r0~,>z\tdpX^;Vf\'dY/*)_', 'DBbA1\x0bUX4Clp)}/W"^`l?\\nMqU$sZz\x0b$6Bm~\nG:J\rLK%dqT', 'T\x0bd}}b_}(H2x|kuW?6+J\tZ&4@AUUe\rLf\r*%Djhb#0syR&g5', "EztDC,$O?zJn:\\M5v';7V`- V\r?!}nDn\r^(!P%fi255{", 'yTpJ~>)}KM+s&LF O,&yPZe:)\x0b"K"L\\@y\rcGo(2\\|o3K', "Q\t\x0c:VR>~'S 9+6y#xAx)\x0b}\\i:mX0\x0c{=K]MxC+b\x0b.5\x0cC[_", '.A$S]M]\nl\x0bZ;rm4"($x\'#K#C-"?B/g;\\\'(l.QQ#;Zgg~.P', 'ir[ "\t\n0|XG^p#?CVeHr+-sdI\x0b&x\n ?:w[HY1WUgJ|A;Z(-', "brAt7]\tA01}(ea=H)\x0clGU{ jAUc'.`R$yb15%_((%y'", '|%?,2uM0%>GU`a+n\\:HOSKdtB8GE\\P]dm{`xL', '$eXW*f/\r3t3C/!&\t\\j;2$3HNjRM)k#\tp?%xpGB>', "Q_iKoJ)N.\\s7.'%+,q3?Ad\nilU9y)@\x0b'\x0cK!jJ", "@PBkcu7GB'CJ(\n\ntCUbv#[s(gk*]\x0czI5'OBRmQU", '78H*na.\\@<tv0$"]thwe/#8\\\n(\t#WEQu}gQ/h0w', 'IH/n:&P1%8A(;\n\\j!#e e7kr{@)0*dy6Y4%:"bRR#', 'LYe;g2D\x0b}Id`L"P4`\tsoAtJ$Qi0XS5WikdU[Jk~1<VVMQOm', '\\(}&Y\tnnY#,\r2XE43^d\x0b&P3r$|nw*2S[tH8N+>,-g6dXc?PI', '\x0b0dP;$bwl"& WG\rlx"\'Uh3)"3<`i;;vty2>uiGS', '%F?XbIVvD0$\x0b9R\x0c\x0bxjhvV-Ixd\x0cqComoMn7(~g7l\rb rYB@RM)', 'JiT929gN]<f 8hFyB$E1x\n-T80~l}IOMrh`b]', '-b)RY7 u\x0cr/ByMlO9VSULDghI#4jT0=r*ZYZ0b\x0c`hcd6L', "6'od}6b|8p}+z7^DMx6iN.BwZ~kb=S>\nw[W65vr?&r", "F~\x0b}zXxX7?eo9;13\nk}^!7)_5\t%+\tIs'JEKNu_", 'K(\x0c,n^|2[i\x0c&Dp*;HQ\'AZ!b\t\\S";(H\\&%s-X~f< &q', '_G]xPm\x0cG|IT%dBMaUT%loVg3\x0bn,qnRuu%\x0b%ca?,[)}Xr22-(X', "hXQr@474w' gRx0SG=~l@#[DWh|eD{tii!", 'L49_3WPWMnsf\ru/mt,9l|\riYpbfPvgoevN;\raA.m', 'q^I 03vZA5Gxk0j|Q5)YR*7R`3%XX/Wh Y?]\x0c6WNafJ', 'Ok;W6>E!vgzEK&_!~\\"\nJcpZH__*KAYw&~A1LG[\txoInxG\x0bx?=:w\t{:', ">9U&DWgV/ I4J_eD~8C'>d((%B=!#O,18'}\rM{ g<\rP", "(Fk?S'v!D7K CW7:b$xYb?o^t$\x0csmI1\rV4/![mj;c2Kh", 'rkD8"YBiUg.7(k\x0cd\\^M/~D.\x0c;K=1VmI(2fTukv"GH-ROz5', 'M7%M_\r/UJcMMi5fh\\FSRb1q\x0c2aS,<9\\*ZT~%$Kd:p', ',qu9$W0\x0cpNo 4wW1Yt&js\n+)W3}RFR3A\\r.,uX\\&L<', "D0?e+NGpM\\\x0cXVe,HEO's!uG:q*FsC\rqF[$RZB2Y0RB", '7#dSF!V2^4]*As\x0b^6S>3"E]Hw#gh#]:FHkJpfDq[y}U', 'Ab4]1Qyc:<)^N/UWh6KFUm]^*dq~u6uiyX"o"Q,2zxIC', 'U@kTj+b+was6=zm48@u]kWOL|({^<>E=\\Sdx{Lo0x"`/(X{b', 'F1fLyxx]5S(wm.hy76Qws>[}!n\x0cn^.I\\A<\\T3]h/`,^', '!{f@n#dJU}\x0cJIR7#k(*Sbs`_QYxC3NX\nQ^w}:hkx', '&*<;F\\OYm5\rkHMqfe7CJ2Y)H$\tGZn\\V:Vj6lwQ', "\x0cmD\ry[ .5_gp!Im<'q?wa{^+TonvJ?rLC%", '* ?|4C"H.@{?\\\n{J5Tq!}\x0c]t\'!5^E^s^DuXMtH-.S3_H`', '~fr3\\jSkIclSO>y@%(C5WxoMEb8sW?\t,(^iTOKd~,IO', 'Kz^O{v2);TIOb\th@paf7\\jk /5U\x0c5]XH<bv(N8s0|,ovl+z\x0bl!~B', '8C>d\rL~5qXoK<@B@I4A%E@5 l_]:B/@MQ~$u"+Q03P6Y3r?EM^4s.', 'vgxu(+Y%pdoG~X~sL}hCxv\rne\t#kbv<Z(L91CgxL5', 'J\n(+\\jJK\r~k$\n7~XIi^~t7.>?h\x0bT{>1I#uQ+#\n8?\tD', 'f`vv+cO\\`C<.sa 9\t4BfDx}A4z?oZV}#9A\x0bP_[77SH@U j', 't0:F2IXb?e9X^6f[7S}_@H{iNI0V&\x0c/`9(Y:SZ4yR?HEb', '87d&', "5xu\t_$vn=#}q%J+i{E[.bW?T.^0mdLPlm3_IO 5V'X-e", '=/.q<K)l:zByMT9[-8Pa-jA|+EQ/1&j+A o%;?mbR"IC?', '80\rCZ/\x0b|63M\n!t67uA6PXh:,oxb`[^[^E"@>\x0btN|f&8pd', '\ru/+<0N%`qVz:>o.4}VrdKx\x0b?)T@0A|o\tZd`+i\n) ', 'e apY~GiK6}zR:Z{;QjURoo"cfF+)$a# d\x0b`\\s\r7/fH~f', '8ZqlKj(QL*Yr-\'m)tNp(){l>\t!&CU6{aAed<+NG\nmHv)"_{', 'Q2)zs<:\x0cG\nI56|0Re{F6VK&.~h\'\tB ; Ig"W;^_{=', '8%aPe\rmzgF<thSSE/I\x0b]NzX!mp@E$;PK.~O1f*r t\n3+#s\n', '#QBk=En~Bju5P^!`<HX/&$\rE[JDx !j}D5f:@sikT\x0bPcK^', '\nhp}D2}Q:7H4U#)<\r70j?9,WqVaDFflrbQuxJ[V%', 'F_ESpfg*Gsuu[J5F=(j]K){\tcPP_e_ #4Sri7Ov{{=;\\|$Z%p,', 'XjS,aqF:\n:9\t,b7PUes8:XSE_|!1U97IM 8?d3y', '-B,(d))bWIY#[J~,)9F@WqnI\x0bB*.?QrV~)"Q&/y', 'i.W;f"U];iyVS^E36]-T68@b?tMlcA`F,qz8yM9M.*4', 'nJ$B)l1&\x0b?kyyrR-{3on_((iZbMdw>RU+%ax!la\\0kIG\nH\tV', '\rzjxHS}:-2HeosqQyp=z,w3HsP|pa}{}pK{R>W^yAf.Cd42', "g.ScqIFwI!TeB@*-|q1>4p\r9'%w]<&@M.'Z=<z>xyn&q+4*", "5g}\x0b'+O\n_Z7+([(\x0c=_dSQ_X/ZI]MxZ3+Q3%4%W", 'F&>?W]ly/>~.^n[~v\tv?\\%*t!%h\x0bloiwaf@:', '(NW_I<}p#/_S`&$Eo`%$\rAOR583_{rU4Mb~hnX\r7\t\x0b!~', 'yd>[NDOAMb5\':vCW>h\re9*\\d%9)Q="vb(t', "4HbdVBAn142/efMo6'&#colt[D{oTe!` .?@(jP", '\x0c,=5q95.K{B+QJM2a+t@`}h)f@0<oesAQRR%>m~Sj~p,+]82$RKXZH!', ',g8\t\\H>^)74~+eyX_w)I5x#K\x0cIz25#Xt396|nd8;<B', 'G^]{tTimFv[Eka-~`i!--,&W@MR*"\x0c7;oVgb5zi*!:lGwPzZd', '+C!\n\x0b4E33T_a\rjb3[G=4_(t<\r=9\x0b{%\t^*do)9sb;J|CG)KTv_Ij-(', ')n+;/N]]Q{S$<FU$&`&c8<\x0brC:l}$62`"AuionfkT;F"#']
for i in range(len(a)):
    if(a[i] in b and a[i] in c):
   	 print(a[i])

Didapatkan outputnya “87d&”. Jadi bisa digunakan sebagai pembanding di script utama.

from Crypto.Util.number import *
import base64
from Crypto.Cipher import AES
import string
from pwn import *

def decrypt(enc,key,iv):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    msg = cipher.decrypt(enc)
    return msg

sp = list(string.printable)
g = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+1
r = remote("103.152.242.242",1457)
# r = process("./alice-bob.py")
r.recvuntil("g: ")
r.sendline(base64.b64encode(long_to_bytes(g)))
r.recvuntil("p: ")
p = int(r.recvline().strip())
r.recvuntil("public part: ")
alice_pub = r.recvline().strip()
a_pub = bytes_to_long(base64.b64decode(alice_pub))
list_msg = []
for i in range(40):
    # print(i)
    r.recvuntil("Alice:\n")
    list_msg.append(base64.b64decode(r.recvline().strip()))
    r.recvuntil("Bob:\n")
    list_msg.append(base64.b64decode(r.recvline().strip()))
r.close()
# print(list_msg)
hmm = []
for msg in list_msg[:1]:
    for k in range(3,101):
   	 ss = pow(a_pub, k, p) % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
   	 key = long_to_bytes(ss)
   	 while (len(key) != 16):
   		 key += '\x01'
   	 tmp = decrypt(msg[16:],key,msg[:16])
   	 flag = ""
   	 for i in tmp:
   		 if(i in sp):
   			 flag += i
   	 if(flag=='87d&'):
   		 break
flag = []
for msg in list_msg:
    tmp = decrypt(msg[16:],key,msg[:16])
    tmp2 = ""
    for i in tmp:
   	 if(i in sp):
   		 tmp2 += i
    flag.append(tmp2)
print(flag)
# print(list_msg)

Output

['87d&', "87d'2", '6>p<c/c', '=(l#a', '6uO2nDfm1=Bkq9&@3BW&@rc.&56', '=(lLpA8c%#DC9NKCh[Zr56', ":2+3L/g*_.BOQ'q+EV:2F!,(2@:q1", '=(l#a56', '6VgEQ7R^6T0f+/5An3YW1c@1!', "88W2r+A-ctF<G[=AKYT!EcZ=F1,'h\\BOPpi@ru:&F$B", "8LJ?tE,oN3FEo!MF`M%9H#IgJBOQ'q+EV:.+ED%7F8", '=_2#T/0JP@@:re"0KM*G>l', '8K_\\TG%De<BOr;uCggs\\2D@0t+EVO?/hSa', ':MVL(8K`4kCht58ASu$$BlkJ+AoqU)+F.mJ/g*Z&+E)-M', '1GE8q0f:XC2DR7%@:h?\'2`!:"1HAu\'1H9d', '1,r\\s@P^#%2*#8*An*\\P0Ocjn@l.XL2e?JY@:V;R2)-jB3Ab/(', ':2+3L/0K.J+D>2,AKZ).AKYT$@:p^#Dg*?', '8K_\\bE+L/*@:O(aEcW@5@;]t$F<GX9AKZ).Blbm', '<+oue+Cf(nDJj$%+DGm>F(Jj(Eb-A6BkM+$56', '=_2#T/c', '8K_\\bE+L/;DfmFJAKZ#-B4uB>', '8K_\\bE+L/5D_;', ':MV(pBOu&', '6@!,p', '6@!,', '@X2M', '87d&', "87d'2", '6>p<c/c', '=(l#a', '6uO2nDfm1=Bkq9&@3BW&@rc.&56', '=(lLpA8c%#DC9NKCh[Zr56', ":2+3L/g*_.BOQ'q+EV:2F!,(2@:q1", '=(l#a56', '6VgEQ7R^6T0f+/5An3YW1c@1!', "88W2r+A-ctF<G[=AKYT!EcZ=F1,'h\\BOPpi@ru:&F$B", "8LJ?tE,oN3FEo!MF`M%9H#IgJBOQ'q+EV:.+ED%7F8", '=_2#T/0JP@@:re"0KM*G>l', '8K_\\TG%De<BOr;uCggs\\2D@0t+EVO?/hSa', ':MVL(8K`4kCht58ASu$$BlkJ+AoqU)+F.mJ/g*Z&+E)-M', '1GE8q0f:XC2DR7%@:h?\'2`!:"1HAu\'1H9d', '1,r\\s@P^#%2*#8*An*\\P0Ocjn@l.XL2e?JY@:V;R2)-jB3Ab/(', ':2+3L/0K.J+D>2,AKZ).AKYT$@:p^#Dg*?', '8K_\\bE+L/*@:O(aEcW@5@;]t$F<GX9AKZ).Blbm', '<+oue+Cf(nDJj$%+DGm>F(Jj(Eb-A6BkM+$56', '=_2#T/c', '8K_\\bE+L/;DfmFJAKZ#-B4uB>', '8K_\\bE+L/5D_;', ':MV(pBOu&', '6@!,p', '6@!,', '@X2M', '87d&', "87d'2", '6>p<c/c', '=(l#a', '6uO2nDfm1=Bkq9&@3BW&@rc.&56', '=(lLpA8c%#DC9NKCh[Zr56', ":2+3L/g*_.BOQ'q+EV:2F!,(2@:q1", '=(l#a56', '6VgEQ7R^6T0f+/5An3YW1c@1!', "88W2r+A-ctF<G[=AKYT!EcZ=F1,'h\\BOPpi@ru:&F$B", "8LJ?tE,oN3FEo!MF`M%9H#IgJBOQ'q+EV:.+ED%7F8", '=_2#T/0JP@@:re"0KM*G>l', '8K_\\TG%De<BOr;uCggs\\2D@0t+EVO?/hSa', ':MVL(8K`4kCht58ASu$$BlkJ+AoqU)+F.mJ/g*Z&+E)-M', '1GE8q0f:XC2DR7%@:h?\'2`!:"1HAu\'1H9d', '1,r\\s@P^#%2*#8*An*\\P0Ocjn@l.XL2e?JY@:V;R2)-jB3Ab/(', ':2+3L/0K.J+D>2,AKZ).AKYT$@:p^#Dg*?', '8K_\\bE+L/*@:O(aEcW@5@;]t$F<GX9AKZ).Blbm', '<+oue+Cf(nDJj$%+DGm>F(Jj(Eb-A6BkM+$56', '=_2#T/c', '8K_\\bE+L/;DfmFJAKZ#-B4uB>', '8K_\\bE+L/5D_;', ':MV(pBOu&', '6@!,p', '6@!,', '@X2M', '87d&', "87d'2"]

Didapatkan output aneh , cukup lama stuck disini karena kami tidak memisahkan setiap percakapannya dan langsung melakukan decode secara keseluruhan. Namun untungnya kami sempat mencoba di cyberchef dan terlihat bahwa ada beberapa string yang terbaca ketika menggunakan base85 decode , kemudian kami sadar ternyata harusnya di decode per percakapan ( tidak berkelanjutan ) . Jadi decode saja sampai ketemu flagnya

Flag : COMPFEST13{4fd29464a28a1b39559f4fc500b41c4b17ec8ad74512394a830b51506628caf4_734b39d538}

You AES Me Up (482 pts)

Description

-

Solution

Diberikan source code sebagai berikut

#!/usr/bin/env python3
import sys
import os
import random
import binascii
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, bytes_to_long
from secret import FLAG

IV = os.urandom(AES.block_size)
KEY = os.urandom(AES.block_size)

class Unbuffered(object):
	def __init__(self, stream):
    	self.stream = stream
	def write(self, data):
    	self.stream.write(data)
    	self.stream.flush()
	def writelines(self, datas):
    	self.stream.writelines(datas)
    	self.stream.flush()
	def __getattr__(self, attr):
    	return getattr(self.stream, attr)

sys.stdout = Unbuffered(sys.stdout)

def pad(msg):
	return msg + (chr(16 - len(msg) % 16) * (16 - len(msg) % 16)).encode()

def get_flag():
	flag = pad(FLAG)
	cipher = AES.new(IV, AES.MODE_ECB)
	flag = cipher.encrypt(flag)

	enc = b''
	flag = pad(flag)
	iv = IV
	for i in range(0, len(flag), 16):
    	cipher = AES.new(KEY, AES.MODE_CBC, iv)
    	enc += cipher.encrypt(flag[i:i+16])
    	iv = long_to_bytes(bytes_to_long(enc[i:i+16]) ^ bytes_to_long(flag[i:i+16]))
	print('flag (in hex) =', binascii.hexlify(enc).decode())

def encrypt():
	msg = input('msg (in hex) = ')
	if (len(msg) % 2 != 0):
    	print('Invalid input!')
    	return
	msg = binascii.unhexlify(msg.encode())
	cipher = AES.new(KEY, AES.MODE_CBC, IV)
	enc = cipher.encrypt(pad(msg))
	print('enc (in hex) =', binascii.hexlify(enc).decode())

def decrypt():
	enc = input('enc (in hex) = ')
	if (len(enc) % 32 != 0):
    	print('Invalid input!')
    	return
	enc = binascii.unhexlify(enc.encode())
	cipher = AES.new(KEY, AES.MODE_CBC, IV)
	msg = cipher.decrypt(enc)
	print('msg (in hex) =', binascii.hexlify(msg).decode())

def menu():
	print('1. Get encrypted flag')
	print('2. Encrypt a message')
	print('3. Decrypt a message')
	print('4. Exit')

if __name__ == '__main__':
	while True:
    	try:
        	menu()
        	choice = input('> ')
        	if choice == '1':
            	get_flag()
        	elif (choice == '2'):
            	encrypt()
        	elif (choice == '3'):
            	decrypt()
        	elif (choice == '4'):
            	print('Bye.')
            	break
        	else:
            	print('Invalid input!')
    	except:
        	print('Something went wrong.')
        	break

Setelah kami lakukan analisis kami menemukan 2 bug , yang pertama kita bisa leak iv , iv nya digunakan berulang kali. Untuk mudahnya encrypt satu block null byte lalu decrypt null byte + enc(null byte) -> otomatis bakal melakukan xor null byte dengan hasil decrypt null byte ( harusnya di xor dengan iv biar null byte hasilnya , tapi malah di xor dengan null byte jadinya dapat iv ). Yang kedua ada ini baru sadar setelah coba coba running filenya + debug nilainya , ternyata hasil enkripsi flag di padding , jadi kita tau nilai akhir dari flag hasil padding , yaitu “\x10”*16 . Jadi tinggal balik aja prosesnya

Block = block[::1] Urutan block dibalik

dec(enc(block[i]),iv)^iv -> biar dapet nilai asli dari dec (tanpa xor iv)
dec(enc(block[i]),iv)^iv^known_plain -> dapet nilai iv yang digunain buat encrypt di looping ke-i
dec(enc(block[i]),iv)^iv^known_plain^block[i+1] -> dapet plaintext ( value flag )

berikut solver yang kami gunakan

from Crypto.Util.strxor import strxor
from pwn import *
from Crypto.Cipher import AES

r = remote("103.152.242.242",5592)
r.recvuntil(">")
r.sendline("2")
r.recvuntil(" = ")
r.sendline("00"*16)
r.recvuntil(" = ")
leak_enc = r.recvline().strip()
r.recvuntil(">")
r.sendline("3")
r.recvuntil(" = ")
r.sendline("00"*16+leak_enc)
r.recvuntil(" = ")
tmp = r.recvline().strip()
iv = tmp[32:64].decode('hex')
# print(iv)
r.sendline("1")
r.recvuntil(" = ")
enc_flag = r.recvline().strip()
list_enc = []
for i in range(0,len(enc_flag),32):
    list_enc.append(enc_flag[i:i+32])
known_plain = "10101010101010101010101010101010".decode('hex')
list_enc = list_enc[::-1]
list_flag = []
for i in range(len(list_enc)-1):
    r.recvuntil(">")
    r.sendline("3")
    r.recvuntil(" = ")
    r.sendline(list_enc[i])
    r.recvuntil(" = ")
    tmp_dec = r.recvline().strip()
    tmp_dec = tmp_dec.decode('hex')
    tmp_iv = strxor(strxor(known_plain,tmp_dec),iv)
    known_plain = strxor(tmp_iv,list_enc[i+1].decode('hex'))
    list_flag.append(known_plain)
cipher = AES.new(iv, AES.MODE_ECB)
res = ""
for i in list_flag[::-1]:
    res += cipher.decrypt(i)
print res

Flag : COMPFEST13{Y0u_aes_me_Uppppppp_____t0_c0dE_on_st0rmy_Seaaaas____e0212d1a34}

Last updated