بسم الله الرحمن الرحيم
Hi every body , here olimat .
In this write up i will explain all Reverse and Crypto challenges in this ctf.
let’s start with Cryptography Challenge
1-eventually
We have this file evl.7z after extract it we have these files
Challenge.py: Python script, ASCII text executable, with CRLF line terminators
file.txt: ASCII text, with very long lines (467), with CRLF line terminators
let’s analyze them
Challenge.py
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
import hashlib
import os
from secret import shared_secret
FLAG = b'Flag{XXXXXXXXXXXXXXXXXXXXXXXXXXXXX}'
def encrypt_flag(shared_secret: int):
sha1 = hashlib.sha1()
sha1.update(str(shared_secret).encode('ascii'))
key = sha1.digest()[:16]
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(pad(FLAG, 16))
data = {}
data['iv'] = iv.hex()
data['encrypted_flag'] = ciphertext.hex()
return data
print(encrypt_flag(shared_secret))
#{'iv': '96a7f5c23a3344689b465a821ea0cf39', 'encrypted_flag': '07ba868266c6c1f0a5600d8fafd984169222d32bdb786f03f06c327c02651be2a975cbaa20e8f89f17ce9ca2579a1a74'}
file.txt
g = 3
p = 2410312426357032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919
A= 112218741139542908880564359534373424013016249772931962692237907571990334483528877513809272625610512061159061737608547288558662879685086684299624481742865016924065000555267977830144740364467977206555914781236397216033805882207640219686011643468275165718132888489024688846101943642459655423609111976363316080620471928236879737944217503462265615774774318986375878440978819238346077908864116156831874695817477772477121232820827728424890845769152726027520772901423784
b = 19739342581490703698785772714920885908249341925650951555219049411298436217190605190824934787336279228785809783531814507661385111220639329358048196339626065676869119737979175531770768861808581110311903548567424039264485661330995221907803300824165469977099494284722831845653985392791480264712091293580274947132480402319812110462641143884577706335859190668240694680261160210609506891842793868297672619625924001403035676872189455767944077542198064499486164431451944
B= 1212972460522075344783337556660700537760331108332735677863862813666578639518899293226399921252049655031563612905395145236854443334774555982204857895716383215705498970395379526698761468932147200650513626028263449605755661189525521343142979265044068409405667549241125597387173006460145379759986272191990675988873894208956851773331039747840312455221354589910726982819203421992729738296452820365553759182547255998984882158393688119629609067647494762616719047466973581
the Challenge mention thing about Diffie-Hellman key exchange .
so we can determine that
g is primitive root for p.
p is prime modulus.
A is public for person 1 compute using g^a % p.
b is secret number for second person.
B is public for person 2 compute using g^b % p.
let’s analyze the code
the method encrypt_flag take shared secret as int number
first thing convert this secret to string then take first 16 bytes from sha1 hash result of shared secret as key,
after that generate random iv then encrypt the flag using aes cbc
solution:
compute the secret shared between them we need to calculate A^b % p
then take first 16 bytes from sha1 hash result of shared secret as key
after that decrypt it
script:
from Crypto.Cipher import AES
from hashlib import sha1
from Crypto.Util.Padding import unpad
def decrypt(shared_secret):
key = sha1(str(shared_secret).encode("ascii")).digest()[:16]
iv = bytes.fromhex("96a7f5c23a3344689b465a821ea0cf39")
ct = bytes.fromhex(
"07ba868266c6c1f0a5600d8fafd984169222d32bdb786f03f06c327c02651be2a975cbaa20e8f89f17ce9ca2579a1a74"
)
aes = AES.new(key, AES.MODE_CBC, iv)
return unpad(aes.decrypt(ct), 16).decode("utf-8")
def main():
p = 2410312426357032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919
A = 112218741139542908880564359534373424013016249772931962692237907571990334483528877513809272625610512061159061737608547288558662879685086684299624481742865016924065000555267977830144740364467977206555914781236397216033805882207640219686011643468275165718132888489024688846101943642459655423609111976363316080620471928236879737944217503462265615774774318986375878440978819238346077908864116156831874695817477772477121232820827728424890845769152726027520772901423784
b = 19739342581490703698785772714920885908249341925650951555219049411298436217190605190824934787336279228785809783531814507661385111220639329358048196339626065676869119737979175531770768861808581110311903548567424039264485661330995221907803300824165469977099494284722831845653985392791480264712091293580274947132480402319812110462641143884577706335859190668240694680261160210609506891842793868297672619625924001403035676872189455767944077542198064499486164431451944
print(decrypt(pow(A, b, p)))
if __name__ == "__main__":
main()
let’s play with reversing.
1-LittleHero
we have this file.
LittleHero.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
let’s analyze it
as we can see it’s GUI program written with c# it’s take password as input then validate it let’s see
string text = this.textBox1.Text;
int[] array = new int[]
{
0, 70, 0, 77, 0, 67, 0, 68, 0, 127,
0, 91, 0, 89, 0, 45, 0, 87, 0, 86,
0, 85, 0, 63, 0, 120, 0, 96, 0, 62,
0, 58, 0, 118, 0, 34, 0, 38, 0, 97,
0, 75, 0, 74, 0, 73, 0, 61, 0, 71,
0, 63, 0, 103
};
char[] array2 = text.ToCharArray();
byte[] array3 = new byte[text.Length * 2];
for (int i = 0; i < text.Length; i++)
{
int num = Convert.ToInt32(array2[i]);
array3[i * 2 + 1] = (byte)((num ^ i) & 255);
if (array[i * 2 + 1] != (int)array3[i * 2 + 1])
{
MessageBox.Show("Unvalid Password");
return;
}
}
MessageBox.Show(string.Format("Nice Work", Array.Empty<object>()));
it’s store in the text value of password we entered then check for this condition
if (password[i]^i)&0xff != array[i * 2 + 1] it’s print “Unvalid Password” else
print “Nice Work”
solution:
We have final result stored in array we need to reverse it
because the condition check for array[i*2+1] we need to xor the current value of array with floor(i/2) to validate it and avoid zero’s.
script:
def main():
arr = [0, 70, 0, 77, 0, 67, 0, 68, 0, 127, 0, 91, 0, 89, 0, 45, 0, 87, 0, 86, 0, 85, 0, 63, 0, 120, 0, 96, 0, 62, 0, 58, 0, 118, 0, 34, 0, 38, 0, 97, 0, 75, 0, 74, 0, 73, 0, 61, 0, 71, 0, 63, 0, 103]
flag = ""
for i in range(len(arr)):
if arr[i] > 0:
flag += chr((arr[i] ^ (i // 2)) & 0xFF)
print(flag)
if __name__ == "__main__":
main()
2-Death Note
We have these files.
DeathNote.exe: PE32 executable (console) Intel 80386, for MS Windows, 5 sections
Pcap.pcapng: pcapng capture file - version 1.0
Utility.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows, 5 sections
let’s analyze them
First thing i upload DeathNote.exe to virus total because we have these pcap file, And get this result.
let’s see it , Before we start let’s agree that Utility.dll used to handle processing,anti debug , etc .. so we don’t need to analyze it.
As we can see the malware start with create file “File.txt” after that connect to the ip “188.10.5.60” on port 54000 ,then encrypt Flag.png with AES CBC
with key hex for all non negative values less than 16 and the iv has same value.
let’s decode it.
We can find encoded value in pcap file so let’s extract it and get the flag
script:
from Crypto.Cipher import AES
def main():
ct = bytes.fromhex(
"273e99dc6e0311d9392409513c3ace0d34dfca2c11130e2f4c57fe8ce18ddde54cee64fb6a247a2447816ba21eab868a1b04a4566fb61cd3716b712d58f992501990efee8d27aa9b205d143f0867748e7c8468e7ba686f7cec95026399c51e3a15d9ceeb0f3eb527a9c800948d51f513153e821ee5ca85affe7b822df9853e7f96fa63897b2b55f52705a601c78ab3f45b9ebbea373e240cef5b411055a353ef7dc60b15d43245d94a00abf44862dcc1bd6f53af2a32ddfc5f08889fd30e1c36a75ccc60086d765fb1caab68ca6d08cbf6315682d2733f4786e3d7703a66f9216c081218cf294a073359eef04a06d9a305662479020dd0096abd7dd8dcddc26e0ca03e51d945ef84b74c77f67653a8c89026938c80e23739f64939de04ff0e371a50a6cdbf5559a40c2a55196f0235a2b5a6966e007563dc03cf877633bef812e15433e20bedb10779a17379fb7d03eb6054a20d5f8f3a84da405d6374cd03d19acda96b60bcc3229fddfd4a5a3bacb3ad83c197f4bad0aaf85f6ee84e9998cd0786883cb6b8b30c5ba0148743e7cc91bd51ea023de16d4f1c5442418f330847ad7eedf672c1dec926626af579e785b127b3111f7b5d97641c4db25d5899c7b68ceec2452d04a747c5f32d559c2337239bd40bdeb8a92783a6f5a8880b29fd42c892f2005ce73daa9e968e635d59f76ea6624ba31d96d8b3870466e093f3f9b28cb7ffa1628ed0d0e0a3b449eb985968f455fc6784b7ca9779385e6f094e9122f1eb3c31d64a80db6b8b2c3a34a907ec4278ee083e736bb4619e701cef6c5fab378307678834895bace0e67d656d24b0eb1bde1b020de264ced9051fe42c751143cb3356bfe3d24388df4b8957df65ff59749ec91a32fd82fc44f19fe8aeaab731db79f707ee3663078710b45363e160d321f74ef9b4c3d03a8a9d139a28e1737036fe14bb03d7e9a02d713aa48c38e9b67966df6d7bcbd5b4e479d980236808b7d89fd7987e58298fb96116a922ebe137842ff7054140b845b2f018c9f5b3594205c5284040e96aa0e8524691277b224584c2d0e6473a53c47c475d544d3186031405047d14bdb2a2dd2eff67d2a88827fc0c5a3fa38dfd3d14b20bf9f15a5a80df33e93535a1e54f3b4c4fade39428159a8f2b05788c42f3ecd27f2785678a650d3faa11282f5f568948aa934ae860762da9568f46656164b0987c2f752d744406e28cb8930b753d335965f3b1db25398e8cedceaef83419ed05e201cc5ea6d0141aceaec41965f6e12a879c6e65984e6c02795afc30191294d4ca5b98d4aaf0d42abdd3230bf3ad8e3ea749f38004ee03e1d34aeb3c3c4a50389b59efd3cb525b2ef956df2ada707e5bd004cc65b9535c87afc4b48e6aa87775be84cc5283d83f1c61b4b2bef922519cdb43853cfd44a55cf2a2bcf235d82dafc4ee43d5ded14fc89a101f98c54e60df2b0a3bd5928b9cf2aa121ca8e4fa9bcba3e4b3ed212110daa7d9e5047152269749a0b86919d391dfaf5483ba5311db9613c08bd129e6a6c8bd973687c1d6788fd300931d7b40fcdf6199ae738793149877c74befbc7a08c7cba3ea56a1c6669deb1cadb91e5d252d61396963373b1a4379ef4353fba9e0973690dee1af3a4130a9db73d1ed0851c0d61f8faf2c0e1ae9428a87d55d2ddce62957d9bf562a4c019687c5c7584418dfc158dd77c062a41a6cb49f21deb95e48c0fe9189d3b3368d5ba6f27f4ebf91045438dd70715fa819aa3e2c4436f57ce9d9724eb9fa5f66205cac2d81e8cd19d25425e5f98842c021bd588d1433ba79db6c270e53da6994b27b256d239ae25683f15a93441d5d9d7e403330e5db5505ba775c3532f1ccb06ce237abc0614a3c11567e538761a6c548531909b828b888ddc6a7802bc0471c42b23efb0c29bc0e341b38b79133b059e4ff73c4d36c7ab105df5286956d8706bd5393866761cf9ba56b1fc23fb520c83e17ee6fa04ad90116b7455ffa8ee4dd67f6ba18f6148b722e876ef2c1010c11978d838540849d8bd3114d50591f5c67e1b405e9d6d0271dfdbd66b1cb4a8277a788ab3cb5ecdb885f4405f9db19ce094ed8720668494b4bd270e22ef6d2eca09175cb0579437d811605488a04439b0751b30ae40f1593feee82419f193684bfb4fdb3d6401d5b8c7e83485fb1134e2f277dfbe64be5ba096f851c05056e53481df8b60e2fb9e8ac833e2d6c12605f887eab4f07f55ab43b60b70e382bd816e1962017ce81c6d44454f05bf8dd2086b60d66987100b2809200e9912d8ace9f20b1f49486f01db91b89e3a7685cc77ef4467508bb31d0ba00ead193f49892a6d3c1419c7dd5a8ef13f113d4ce4e504a96f93bd25739524e8769f4a2d0ddfe5bfd557689da734322d3769bda5538029accbad045fb0bd7"
)
key = b"".join(bytes.fromhex("0" + hex(i)[2:]) for i in range(16))
cipher = AES.new(key=key, mode=AES.MODE_CBC, iv=key)
assert len(ct) % 16 == 0, "Failed"
with open("Flag.png", "wb") as file:
file.write(cipher.decrypt(ct))
print("Flag.png has been written")
if __name__ == "__main__":
main()
OK 🙂.
let’s complete
I Decide to analyze function TLS Callback 0 to determine if there are any thread work with any function and found this function.
‘sub_401760’
so let’s analyze it.
As we can see the function so big so we need to track it step by step
As we can see the function start with create file “flag.png” then store some values at array after that xor these value with 0xab ,then move some functions to call it, I decide to decode these values.
from pwn import xor
def decrypt(arr: list[int]):
print(
xor(
b"".join(bytes.fromhex(hex(element)[2:]) for element in arr), [0xAB]
).decode("utf-8")
)
def main():
ct1 = [159, 159, 133, 154, 2576975258, 133, 159, 155]
ct2 = [200, 217, 223, 207, 199, 199, 133, 207, 51143]
ct3 = [200, 199, 196, 216, 206, 216, 196, 200, 192, 206, 223]
ct4 = [252, 248, 234, 236, 206, 223, 231, 202, 216, 223, 238, 217, 217, 196, 217]
ct5 = [222, 200, 217, 223, 201, 202, 216, 206, 133, 207, 199, 199]
for i in range(1,6):
eval(f'decrypt(ct{i})')
if __name__ == "__main__":
main()
And I got these results
44.122.1.40
crtdll.dll
closesocket
WSAGetLastError
ucrtbase.dll
IP,library's ,functions.
cool now we have the IP which the Trojan send the flag to it,but we need to determine how encrypt it?
As we can see it’s connect with this ip ‘44.122.1.40’ on port 54000
then encrypt the image using rc4 with this key v72[0] = 0xA9532DAC;
v72[1] = 0xA6E21FCF;
after that send it to the attacker
solution:
Now we have the IP of attacker and the port where connected together ,and the key which used in rc4 so we need to find the packet which contain this encrypted data.
As we can see this packet contain the data so let’s decrypt it.
script:
from Crypto.Cipher import ARC4
def main():
key = b"".join([bytes.fromhex(hex(i)[2:])[::-1] for i in [0xA9532DAC, 0xA6E21FCF]])
cipher = ARC4.new(key=key)
ct = "31965df3cb445d41e66d5cd8ac46c21ccd7f62626ed280fdac81db9fda4726c0fa1359e909442a06a50398759eb71959505dd9c6a2cb80df38d96719120f3a19a07364432487dd5a9c0bd7a986cc5185c4e08dbf3b2314782cf940b0fa2c399f1ffe3a33ae9c1bd80a649779ecd6131e6df1e2c00e2a1dac602c57661e6d6d84eaada60fec42f9c195191bd538b0917399026055df57ed3328ab37883856dcd6bfdd08d1952adf3c9ab626c968f35a0b453fde38ebf38d9317ba4dd4456fe222257ee87bed115d505149aadd87c2f1d20fcdf11621de7b7fa796deb95939e5403d0bc32e1f73f0ba19f89e1aedf2ac2ca822317b12888078903433fb3b35e7fe455c13454d0898e597e2a20832e49f3522e7accec4032c1f72fb0c56cc46b90ee41d60533d6f74204c1efeee9d8aea6cb37fc1f01c43caaadef5210ab08cba1c7beaa4b652b046d6f1c34a0b3234fac308c379da59a9840a51243d40a7dd2c845b6a70ddae791caf2d2f5bbf6a047c34255af7d259e8ce6da4a47065abe5b48b043d5ee2ab3c2c8bc2bd550b6eb445042a6141247549e91b317e408b9f14ef409f7edeed9766e1d5d3d7bdfc2bced9f361428314a67f59302dd62cc9f5539a278bab6ad701a7911ac79e63a012060a6e9db28396866dfdb752938ab8d6fab6836403b7f050dc02785465b6a005226d688ad6404b124a42b22edb3dde8c0ab78b7f8a31e7ca5314ff8ad102c7eccf5e63ea584f13fa83f8c5a5e83920a77f69820d2d1d0527867444d064a5efed41b63cfd94eb3afc3f4598b66640345a1a78a724e1b86fb9a15df5ec4415196f5fe313f66736c28a98cd6bb8549c396d42d4babb1a877e414816445ed219e33edd7d00feae808af5e5619cd3e82e5728d52fc8b6b3cbd1638aa2cfb9fe14c87d00fafc1e67968665512ed06e61b794a95d2f4055b704b742d5b44528dec68bc94387422c8f60bbd5ab84d6541ceb4085944b8a8a62f0573c62645736d53d8fffb2bc7293fe5062ccd1294bec718c354037b1c2b4198eceaf0cc53e29955dea343c46733c85b0d9056f3ef122792a113fce0808420a6d805c7fe4d94ccecd42860c077a40f4012770bb8e3cc84dc5005f4260cba49c07098f592580d666e5cd1e224eedfc884b5b6c0778880c88424fa1bb29c03ebcb4b42666528510d2eb8dd888d7d20d9d7dccc4042115f4df355644bc181b2cf22182e5efe967a279f235d61677ea670a8b7a62acec201390c0d1e064c43a9ef095fb0b777638d2b5adb53d66e84e8971e6e2261dc4a6c20bca2a5cdbc002683adf027f9415ff9e5d4b352315b8127c364169ab692782427fa6a02e06b40429b76a3034136e5b7d318e0256a425be7be7c74c6aca7849f2bf8e131654526580fe53f5b2c09e84d2d2fc156fa3e7e5a36ff57610a19c8d3386778cb7717fec9dec0f7e35fa7ee39968b7dd5c1654e86e6aa0382e55fb187c090c6e31ee55f5ec2e2a1f6f0b53627333d6887cdfdca09e003382487cf42b0d9eaaaba714107efce07c68011ce83bb9b130572a9dbe206399d66138446936abe572f42abae486214ec1fa24839fd3f41eb90c1d56029ee038156040edd7c74503ea6a83cef11fe6db284696efecd0230fa7d6423ec5bd87bfe76a1540165987ce66e7116a75bde48d7771de4271a6b6ca62ccda6563bceeddb083c166aa5e802240a5f2e70f343911a63851e63bd9d9a71dde03dccff8fb86bc538fc37d2ea200faf837614a2ca7e1e2c1df382a0bebe9fe9315699e279d27215b721e6baae3e7ced24be114ceccb9c627110c1eac4edaff8ec059a242f3de42a992cd6dcb8b319ac76594d0aa906e8475814bc9a8d1878fcad1dfa7cd6ffb90feb8a6ab8c6b071136bde259d85cb9a87dd90dcd2049990c4c94f800cc31136a4fde68ea51009af6c15348f61ce3c44d61746ea598e1605ce9eaab9ab8f2c875e051d115e7fdb2284743fce5d683244f9a2a6884f9e9e95bea86aa6b200b434a843c3e454ef935383cc9e1cf087c9a16fc1192423060555127261b0fddb4457ad2f0e9e6947b25d1b2801a7ae62682bca822b318076aacb6a949356e9cc7347c54bb5eddf57dee7fc3697df7665a9242e5c79272ca4c3cedc0ba1db744f51727497a80ee9ae5ab6fcafe3d5026045fc5c941079d5b5ef80a068b557f82cc4fcc8a41e97355925f0df7ad33d92ef3f6536689ddb91a11d9012158161e61f1b2e8478404a88edf4fd6c46e14b1f6b7942d6a9aa1841988b393e5d71b5967975c0e292fee03de4d6db83c58a3ff0456b65d537d3ec62acb47c5722b4e03334409c631cc8a66750d6cf898cdc9c00094d06144126ee51134e0eb5d4be2eff39ba5ab19bd1f5bb27ff57b1f53f8f3fe9658aaf34f0c13a4ddce32d404fb6cda472511bf9a5f94976c705b8cf05de725397e7fe8bf1bea0bc681346f98a30a11062a852d1890b728b3ee51fc7c0431a4f465695565e835056291d5908c2c07067e9a939e49874645734a87f6397e9b43a8bdb08a80a9e48f531664e83f89b13014cc17f4143578e21f99c5cfedf2313a05fc14f52dbaac1465459ad8e36429452a542bd5469ac544ab51b9c421d71299281449d38c055e6f14ebec5b97ba6a20b915154f4ec32159172f5c3c6e46395e37e51368e940c63a2d99dd10beace38b7b30967f0c8e9310669222adbdf9f706546a6503f409fc1b641807b8305dcbbb37d937d3aa1e75505e99df2979c3573e539c6efedff4087ca650beb8fb7756234bb0294858b1eb1fa0aef665d8ae459599ef78b49c621baa7836d5f2ac6834a7b5e49e12163047e861ef1c37e394c0c187c3906e9e5e3018c75977445fbb1e287f1f2a6aa89354b31e49d0ac210809c98666f250029ed7639cfc623433506837c3fdc554aa26277c935f634ad1ac3b1c784fb58e20e989e772b151869e74afee5e81997b72f865919624167226a24f302cd76d2c1d45b1d1e86b0d48e56e9071c5a43f64183690fd616b1de8a021e52d303f4ee17b6152fccf3cf725cc73ba267204b1d07b4f248d02259aa83ada5bea791998e013beefcdae986150fe0bb37657c2e353cf8e5681f4aae9d0679b8a52363fa461aa9adb1f4f964baa742ecd6c8ace9a5847750c46648b46d3602727d2c77e3a5c06ffe47eba0d692e7cc2fedc544e89c098e72d0c3088ca72273b007396396641d7f911934cc7a2c02c2d88bbe30ef7a28a14d28fe010b6f21bad9063bdcc74e9f6a9264602a69810a102c291ac3c2332f341f2cc7cabcb04beeafa8859709f030d2cf9498cd6a0690c2aeeb895b6f773f36375962fc02b27f0ad98ea6eef325ddf452857c143c4ec944d58a835961cb274f4c0498f515502759f6117c1d6667fcba7729f38182e7811b79f27fadbab67bfaa490c31e74d0d42ab95fd7c780762a1ceee77a63d676838289a43e29eef375583ebf657f0e89d1a49220df1900f1167fa3bde80bc90a8bcfc655dd342414549bb2d74f5d424dc81bd76acd91f800d912f9e81b66534d32810b3bc59ced5379b165f67f80cb887245e703c92e3cf6f691ab9c1d0198015b69060700968e5ae007e8d9a40ee036f2e43a2ec12fbec8a68d6245027691851667689fc6c4ce15974bbb42f62584a13bcc8a3cd43623f50aa53d3ea583a2802e7fcd0935527a2fd3abf11ca5951edd0729c05c09af581d69c425bb451035cdaf99b31f8e9180901d2604e4b87e4c2db7fd3a5947513fc2cf7c71a2f9883ae241ad96915fc159708c0f168b608887ee52abd005669aaa633f5f51006f71719e08cac727614c7565331d8c72f6d9118edb9a355c554a46b615ef3b2f216ac158540986d425ffbc106fbfba2063c48982cc7ba98c29230118a5d8270386b785883ceb46600c06ebb15e5dbc936bd0e041221a4271df48627d8640f73779794139a794f41845d1c54d66ee834b7d6b5ddf95fcb9deaab11512e1a7bd6ff63ad10c3f648b5870ea65714bfec2c3c4c0fda862e10f8ee2052a52bceb2b581d9654eb595eee5e3fbed33e5315aa30946f6a7248653a3660df98694e24a84dfe6b337382faa99f38ac077083188214d28e97447276d5bf7b2242056b1ba9452ed7b620acdda53ed13dc19d5271d9064c7acaf909e1d6036fe727691df27cc2cc7b41e337a777f020e67b572e0765107e6f443d8f9340ef5d49f94912309c6d42c993bc550ab723b8ca3c1378bfaa60b3951020ab5f355aedc6f2b24205bcb1e359d27ae32bbe32e943b699611484cb692a5848220054b0cf1ef2c184ad9180660ac178b4a4ae62fc92ab31493381bfef4a0192aba219dffd1cf82924eb2805d85a98a0ef20d2a9245876a5b95afddc888f409f1bb2ef87f118533498abff7be204bf9506607e1f5e8ab7cc3e1d15844e5ed327a435ffb92eea54a43675a295b3347a361042ea5ddc68a6469bd75325e91c43cb6c2e9ebb9474c219a0e09126d63bfa58f3d13c3d0f7c14ececcac33bbce8d16ff94c002601bc9ad1cd1149aff69036513d4829c19705b0e157c51d5c8bd236de527ee3c879b3fccff6a9584239f0d30d194f735a4b85351fe4fef127ae7bdca87f75b48785dca84d04d07f5aa15c781142ea26c2adb61bccefc3a7d4aae903c21d0dfa846789079290fdfa2b0deba48a7457674eb261b1e0f0258cdf4fd1b2fe36de48e85ddd7ed6f457a6fe36cd930e13f740250"
decrypted = cipher.decrypt(bytes.fromhex(ct))
with open("flag.png", "wb") as file:
file.write(decrypted)
print("flag.png written")
if __name__ == "__main__":
main()