Reverse400-1
Hightlighted techniques
Learning the game
We are presented with a file called Reverse
. I run the file
command against it:
Reverse400.exe: PE32+ executable (console) x86-64, for MS Windows, 6 sections
1
2
## ./Reverse400.exe
Encrypted flag: bd7e9dad4a5fe0e7911f93cb1bf5a321
From the icon we can figure out that this is a compiled Python program
Playing the game
Decompile Python
To do this there are actually two steps:
- extraction
- decompiling
Extracting the executable
To extract the .pyc files I used pyinstxtractor, but there’s a web version that lets you upload an executable and returns the extracted files if you don’t want to download stuff now.
this yielded a folder with a bunch of files, from those I chose one that caught my attention.
Decompiling Reverse400.pyc
For the decompilation I used pycdc
pycdc Reverse400.pyc > Reverse400.pyc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Source Generated with Decompyle++
# File: Reverse400.pyc (Python 3.11)
pyobfuscate = lambda getattr: getattr.items()()
Il = chr(114) + chr(101)
lI = '[^a-zA-Z0-9]'
lIl = chr(115) + chr(117) + chr(98)
__import__('sys').setrecursionlimit(100000000)
exec(
getattr(__import__("zlib"), "decompress")
(bytes.fromhex(
'789ced5ded6e1b4b8e7d15efaf488947f0fe4de0579817308c8693f8de35e0c417892f66068b7df7952c75abbbc8734856557f48967167d2eaaa220fc94316ab25cb4df3f673fbfcf0e3ebf787abe6f3eb7ffe7a5c7d6cd65f9a9be6f6f7ebaf2fbbd1ef4fdf5eb737ba59379f9b9b4dd37c7bf9fed8349b6f2fcdc3af3fbfbdfcfdf3f5cb5eda3f5f7e3e6e2f6f6e9b9bb73bb77b2dcdea6e2b75b5bedefeb77bb5bd7fb3dacbfcbc9dbdbb79bcb3d7f2367170737be36d607dbffe7210dbdcdeddb7d748556be2e7c3f8663f6bf04abc4e87b702d7ebbbf6cee7f6e2637b71ff89c9db2ebe5b1d5e7decd67c6a2fb606edc11e7f6e13abbdd0056e819d23557efae015f46bdbf4c30b9766d3559f68e4faeabad5eb90c97d11ebd53e0049743a4afdefff5d0fef6cf368b7f0e7c38f6d7e248b062bb709960ef793ac8fcfa2ea5be21eefeda2bd5e6f3359a01e2278f9f55d1def4df9f6f2e3afe7c77f5f8b8104510f543b57d75d29190fb77776f6d4a4397373bb2d093702c711c11f4fcf699474030f843c28ebc5e9cfc7d787d7d75f4780ab6d26df716b0626f453339299a185524e6fb5b9b88b6c89c622b811b4ea5a5e508212f752dfe476fb40913fc60b3c5e787ffd561e5abafff9fcf2f5e1f937aa07ddbcffe91783e3ed3f9e5f1e5eaffb779e5e1f457d1bae1920356b7a7e0457ccd1f2e7b8d3adb44d8fe158f7e7b189ca26bae67b1435a2a2b3ca98ea47496b7a9beac7dade72e6ee5ee1d470e7fdfef44bdf7893c9b13d56fcacdc15297fada36b3ceef1432f7c7b7ef8fd7bef87e1c09f6a5bd2faf7af977ff59aeb7ccfe4b78b48623b61e2ee95a2d9f99fae5ddddd935aad885e45cb86b3321def197506599ad940fbf7c7374f85ea96a2b71dfee8b818c10f0535812c5526d7a0897b533319d36779fdf46cab517bba4f05ad7add7677e8271a779ddb9bd08a02f700334fecbaccf75443e1e9da32aa594dd2a395a5876ef6a5904863c62d24a1d65f0a5ab6e587f14a4fd822009c856537ed1f2d4af1ccec464ff5d5f1e9cdb4a52aea527d6dae2347090006cbb75f7367014558deaad1ac32ed5c969a9811f101e382fb0a291abbcb1585b96d3fa8bac22d1243d4b7d2d0f30e9775dd8a6bde1730a2aed6661bd70cea7f89cfe4d2bc1e21bf49a0a629961d2ed61f83ed40b37ffede2be3abe396b6a6aaf6e1df933785eda0c44d4b9f6468fdf487e25ae4ed5dab7f73af3363f720eaf1f9f7e310e01bb49b7a5a526574ce6ed61f2fbfdecc7ffab9eb9b953759408e0c77e2cfdd563cc2a9ba47009b016f13b547ca7732bf77cd40cf9e9b8337f66fd0ed1cc24c59753bd95675f218511cdd36ad0e2d3ff7b8572a0757e2def6ce2029fa9e8270bbb297887b73d5e1b8b4b043d2e1e2f88e5c009f02af15e76cc5616d8a62e97470bd9db58e370fa91a79417346bedb991bfff6c2bddb8b5a531660cebfe846d4df6bc91bbe5ea0a09352cc385c04da3acf9be929ae4e62f58369ef1d8262ff7806756aed1ec8557d1e5753d8ed7e7c7dd8c2ab8bae240b0aeb4d8b860895a01154590521ba62775c28455931e11d583a293e50ee16fdf0a19552678c012ae6963ae198328f7bb3b2aba3dd1078b42fe039b9370fea332de220afdc3113b11d56ce571979232f328ef9f7695b55f7edb33113a8c4ef658919d45a7cd4296485614e1de9e1cd25b6911bcdcb91c7d3f337ff2979b09f0fa5939bd9aea37621ddcbf936690e55f761598ae18b96f7b53ff390192367232767854f1ff31f306a3e7b71e90b3eaf739a2b1eb0149798b2273c53ab1fb640139f1d3a436a9ddfc77e5890b77155f7449d670f81bd6dd427d9f54e31cec3723fe5fc493fed478a7a59b9cc134995b3a3a8bea53b6faede09ce0075db41eff6dbd7946be7c8479c0a7ae4319ed83ad643e8ce0e7251fd68bd94e75ff59955d48e857be2a2679035fc1868994738401ec56b99345b5f482ea639ca97a4975c1a7e683b66561f2e422d90024bb132f3e835db5b49b20b19b349122936db3ee53fc3c70f9505cf330e1765ef1ed5aa325350e9a88d28abfd76ab446cedf37dda56ff2523dbaefccf436b83418ee41ee3c73c8cd7794470b88897607951a79b2b386777b6882e66896f8ad77b8ab3b813c3e1a2566f010fb4537d1246ce2a7d7a661c50c21b6ea5be251c8561a6cdd7ca50aba451c1b23cf61b228e13f6b44f6e666e45cadd95ebb123e4a2cecfaf1b9cb897fd56cc1cdd85e3a2ca5e7834089831fedb0505d41b29372b3d9225d066e9c44a6af6e1a2da0700a58ad0265527b9ca3f0f252fecad64f4676272a94ed0193e3c55502698f1a31f58738fb8f53e8794d9238ffc7e773b2df3bdafca4d50a1adc724a9fcc0679cc794d492291fb064b5d4937fd8bef6c927fc60ae4292b81f088ff129e2c003d39378cc1f37b1eee777f0739611b652f48e55c623aec2cc2d7a07ddf9048b72406d86e6fa1dbe8c3d77ccedb113ddadaef331513258f12dfed20dc08de3c0a0a94f778cd620bf0f57633eee2bab2c56aa96fe0ac1840f6fc22a2b1f20c885f5f062eace37effb1e71a5c895d7fba99accc7af3fd9fd5c6fe7ee57effed9b45fdd7bb87dfcea94fd8d1db211dfd11bfdd38345c637bd99d20df5d18ef6fb00b59c80a9710a706bd859d7d4512addfcee43b932a1950a8493f4f1fd38bf8d30de7bc7f3b04fdba646b20b907be44d71ce10dd573f608cf87c662efec1cc3d4be394e4abdd12effee9be466a339cbbbffa94dede5dca9bc725606cf3f6f72b902e433482a28f6d067f4043d5a52922f6a677553bdfaeae91da8d4b465f5437b81f1eeb57c1fb3f63d6d7315214b8afb1988807f14a43e2090f2903ca4deab6613e1d4625e123104c9dc82cb10848d928773749da511312d8963c8e1b7a47137b1c53ac4a4179cb269b2d34e9218edcd628c3221dda183c7c8cdd0eb27733e4912f3d7c4a01ecde688d3443f6b264caa5da42322d5ea208fd9c6988f25bbf6b0e7ab25a33855083e73d89bbdb398a585b8e44cce86dfad795f45003a5bfe94384496a08e41776e9f99213d832ae5f2f19a05ba419c9f07443a33cef9ee0610830a8a1aea085833bb191310c285dfa10efa9fa22263d9498d91eab04704cf78d7a572341df2c3d3727efae3857b3d203e98a795b4a197acfdf3eb200854d57d6290c33d0f8f6428db94430c95b1265e4054d11e74a61b80cf89e8da9fd39e16dc41076c2430bdd39149d0158bac57a5ccfb8f2173f452f2ee659b0595833aaac5c358cfa943b168a1314ebddbd306bc2ce0ce47a7a27e0cccb23a2be0223f108964627652ad696b3a403b29e673a2a646966c0598abe2f66025bc6f5ebbba27f02c9d8775cbc2789c39939ca1f8cb934e975862e4d7abf3e5c9af49e332e4dbacf4823392e4dba2ee8d2a4f7fe799f5dcae57ddc22a0a39313d832ae5fdf55062490d0d824fd79fa33ee87fc4fecf70981f71aea775ac578c46892192a973e743a678ef447354b2c0252b488f6312ac2533317780099e873ae5c0e748639b306accb674c8d250b64799c7bc86aa195eb3ca1f62f251f89bbdb398a585bcea2ce2681a4439666061c7a47d772c6cc04b68cebd70bfd214492139c7453fdf62f00c0a19380e3958c8386aea50ff1d6a72f62eabe5d536bd31e66628a8052d8c805a444e3495f981289d47953f4499cceb4d6c212e7a68c0e34ee4bf113a53901a9f32e8f6771eeb36463804b3635b7ec2c932e872963c92689de028a443dde0aad5ce709b5ac29f932124f6328e3b42e6751dd24a5bfe94384496a885431e8d2f32527b0655cbfbeab0c482069c4f6705d7300c3da0d8df0b5a9f3bc3382ad34d42d7d88f7427d11539fae529d974f62f97d05a61b796c554992ea719661d82cac0cbd3b758751cf642a72393428594cab50787f8aec88705515bf38c09e693b73f8e77d761a975ebb08e8e8e404b68cebd77795010924ecb8093aee66bc4f3a9d7833cf086f285bfad0a5956fef18c30280460a60a6293a35fbd2ca2fab95d749ed52ceda7fe0189a6d0641ec548ccab713fa9433873943d344b8e9cab680ed10b619bf92fa8d610a088abde5c641b4522516cdf0abd289a88cd2e62649d8021a173ce8b1f9728c6da40b5d9c669ff508b107999b19759648ef8b9ec09671fdfade73403186e4028692d75be9b680bb84e851da3269863923f7a9a9f3bd194585fa45f7858591d7c36dcb3a2e2e646f3734e19f5dc24122e8292ff14aeff6e5d2bf88216560214f4334b52947a53218911401a5af9107480948a1c1fe481d7339365290aa450539c876d3cb938a0928c763a738832e90800ab8a17756b456f5c903d63769f82e4a901231abfbff933467e14a40a628cbd9fd4599740e4aaceec8dfc2a0db67ebba4994d02e016f53ee9df06c1d378e9281c09c068e48bda4d4484a0602634183c1e40bced8998b881807a168d66ee8511d02d32c206ae8389a9d4249e669a74807232173bd7af1c4440a83e7726182823a14666f20ad8d28479564c3966890a7711650504a3e8a888bd5aacf2437866ba30fb9a566686230fb838140b2706a2b661981508cf0aba5c9a7653d8cb71ec3c8e6d69be0ae7ae9507c800e798b8790c3d3193025f12e7e412b20a08b6a2a0dff70856aa201c5b059b3da573006634aaa7b0bcf5096372125081a7a954954a888374f34148be9fb1e62860f42a3ba8bdecf89acaa1c270ee134b7c28a1300b3116e8e6d22edb8bd8a8648f76c467e49a43001360116374a7748fa11672e878b9c6fdbd73d6d900b8c8911ea0845b004372c30ac8fcb91ae2c09d0883849c36a1188188034a9296a36bf36fd30144560ac2965cc5209da833843f1d517a8fe51dd3887adc3af93b1f6fca314be9d04b69b702a1293031d563ac1d149b09807f8105e300b8108f46431123b259864c0746ea564b2449d6bf50262e97dae787cd7abf34998b4f9c2199ab1241e8a33710a7178a65199b5108174d447aa3a15617ee6c5a2752a4141a419ab888d1046b7470da2143b42264ea244cc72dc278a1907f5a86b813d494fe62bc12475775a0028542a638125a241828fd10041e2081aad2858eb504829b11b7158a57393c9362b046223675cfdf4c052010ac9b7b9e2fe808c24d4601e1ad374a62b97d0c04288489941666f69d01740e1cc323906b9e9e81032474846c65a414762f8a8ec62677937aa030f07b2067519be00f63ac64a1f266bac5d07c58f24e0465d31b2254409e6d168fb7f7da5700c4338beca28f16060a13e9fb7f88c6dec705985dd93f222936c2222eeee555538586fb398381af56d1ea31a8fee9afc9a15504530106406fa49728ea31ac8853a3431ec3be7157c332537572273316b7376d8b41c6e8bbbcc4501f7590b2815d920b1d1800e722bb55ef5118ce2900aa814e83cb2d74a8b18161f1cdd708e170c503ee8efcad1cdd259980c9c43519a6078cf3610d32f3e82dc47880456a42aec54acf444e65df5ab36e60adb9a910803f0158fb51c271c25ec50f1c8c5536edf0cd1642903c78889b55894a9be7bb5c88ec55216c9a8912058b5db44180b0a5666882af584e6cbba541477592c8cb1593691996bda10e562b30bc994e250aef5ab71a4aff42fc6cc286d812ca72daa7e6b63a5a496626b5099a10a20aea2dda4ad0744b202c438950b3d3e47e33e9c3075107d56cba9249b7c1951bb2ae87462d604f7110596cf5209b446ee9fb512c22e5d2c4ebb5044d5c9aa13a03e367ed63163e950a7511c2ef3b44d9247cc3b8e983a0a00f042083799ac823945b604948859fc7ea24ef9324cb3bba588b56fd7841b16355d4c88e2a159d2f00fe029ae16e29cbfe9ce382fa4a69395c5f6f776bb5ac79320f772940c04b25d133091649d893fa68ec3a8df95caa91881237ca7db653bb9097053b587190991fdfbb12adc23239dc0f60b84b59385e1477faf13af5033dc656ffa1905570611a639624cc0665a09531c2dd290c67718316ceea6165e5dcb66e83cee48e68b84b9cc9fde3a966d54406ae5a20ad8eb03734af6f7d507f286fa87680e5466ab06c86155818ad44c8040832c97e69c0eb0501a7a8f93dc9668e099760732bd2829c3189522d4184fb7775f34864d446cef892495e1d17a6555b1906f81646ce9ad69d406bb00ccea4fb600720451e424a94313ddb268843d4ff5c85479b1e886c202538d4bc5a165e0432673af07fb9ec0b7572ec7d8da4ae11886707c85fca72cc9488b71529ac2a0cf1254b9e266a64fab256bb6235dc0abf28899011416c4d60702b82f829e9b1578235be5c465eb7380a9964df315e6a56c7dc3091592185bddce3f8dfda82f67dcfd5862457ee8ab55d48435ab82212ab1c69f71c85f420998282d5bc8b382d948339cb094a499bd89e3b3d54c0b745a1a6c5502e37b8a470f44b4b67bca079680e9e71c3514da9e74bc03e4d9d3b8e9f552c512b5cc66595582e7d62991c365e1c644d5a59a7e429e5fc2bb6361bb7919980442aeebc3e5890e2a12b92546b158c216ed4a9ea5511eac54970159c82cca33575d55018a7be84bc49172443ebd1ae6782f525da5e13864f1119436d0340d8ed1edf9582aa7527ea863aa3578ee349b6ff7ea8c7b9f09bf6cadbaa576169f62a7c1dc608ccde4713b0e9c2856750efc8dab64d0cc0bdd691bc56a67cdd265a838a5589bb127ac6420709af48713902e2df066f1d781012f79363385a704a462cf12ea1ab103db67883aede23aa1eec67272282ac98a148f884d5c0143e6e3b69ccaed59249d1798c68c98dd4d5a3337436a0c370969088461a288957aee46b692bb0421b5b2dfb31fe52f73b91af3cdb16ba9f6619eb3dd6d8ca491533967a6a80f04018231b5b1c9a80c81278b78c72d711a3d5db2d1d81b4faa93247641361952ba510b1ff87b126ea72423ceee52998aad342983f526d172547a986f2eeee52ed5bc0ac5d90192ee5683ecd2c8b0270bd2a930703ca26adea14297bb51530cc69f075551da32b05e0527958e99d6bbe5518f7e2184734fa18dc7610af0c16b3d229b74854339528781814a90ed66094c971f28be409fb031c19dead5c4847fe75309558263eaa7f7fd45f6160db86654265db11a809ccccdd0ddbd0a6d07c9e2b482b3e6a0bf3450c27d7859460582aecd4e45abd998b779e73a42f5844c8d18fb0d3aa8b664e90c87d156ddde8935cbf3274d2a7659802a82812033d0abca81ff981505c148e4f2f9d493120515b651e6021392adccb56d9a582b39937edb53984310985a232c4590dbc8ca48c03400c6266019ac694885d6e8d286f3672e452c48c9022817a1c9b09dc351315036e3b19a20b41bf3f5e6d5cdcbf1f1c4bc5a823a120e1c8846326951f5421b8b58cf3453c58bf7820548dc65b135c68a0e038cf081edcd69a5b26a9c62b260c2410b8b193a3bcdc27e77658f39a18efb2d51cb2f3a098cd9bf10c717ddc25c97e085d4749516ae1a6c364d5a148396544bc2ba9555f53afff26f49564c445623e1307664c120259569dc8d0301707d93aa598e92b9d3d5e015b156ad69f378d632126126f74d51975a14a9456e3b75b5268b8cdd54871cfab058118b8158a7ba253d5a9990b4ddab4a9d048c0c033149e6a84af0dcd91b39686171bca66560c4efda7ccaa848ac878b1c05b1bf62587bfaafcc32040c8061d2078067cdba649986b34baa7346dd48674db81a47c98458526a38226c64898459e9a3e4d2be5934dbd6c5573c392ac419844d24b8b93607af1872281b01b239754a5f84c0f50e24c40438841a029dac57e6d22473eee07698e57a835512e9a92a11b3f87d83a7fd151b3b923ed6cba984c5c8164d1fbd3b1cb74a6c327b2a53e118378c086dc72bd6360b105ba621a16a40b03cfeec4ba749a7cb0864a221c56a14a136ccbd3acd8f8fc42e1a943701631b3b5c3653d9088200318befcf269993612bad8a4e98b67a7d4d328fd2356d8085285707d300874a770203eda4537ce1e02613622a9867db566161bae6253d1cc3108eafd056e6a5ca709cef494621e131f4e4d389f6af394a26ddb75c95a49163f69f87c26b7d4359df00eeadaac7c9ce04b2fffae85c511c4e388f62ab8d55f0af166ecddfaa10e91ac01e7cbb7e7d21fae07dae9804c6273577075df06f3e4ab1d40bca68221698902ac7eee4664d17430b0cf7bcaaac38bc4bf8421e6207b48f45cd11d1a9a9a08d4d9268aa120c68119db707cfa480c4fda1048a53cb308cdd1e45197b5c0c4d73b788ceaff2b0a443442400c1fa274213fe624d9b84a21f5c16d717d3292c612b11770d6616f5ef4349dacedcbf45fcb074432f4f85ccfb45093eca87b563d86ab4a3380e36c67020dc52672e9cf3956720481d051352ff599a9616626dcc17006aa905688a7a63895a1a08e7b7f2f3222fce44cbd953cc1c4b90db1df65289546aa99845d1319d1c89aa9e288a985b9b514bef50bc01601131c64afc0ee6abf04ed7ca6aec1a23833d206ba53136b604821c0805bebb37cbce70e64a8219b1917c619dc87065403fd085d6c36e522d533a4dbb9b44248bd3509bdf2e458e8f1e0827c4af0c0898decfffa4989d81d583385834db9e3c491216d84aa0278b4fac19d296247ffc210600d3197f78c46531cdfd34f4aa18191fe9108020b96da612298aa1eecd36ce11735fd4074b429d0b59451419c0d38fdb244e4ba9e540a47b8eeca914a63e15980377675dba87a80d9ecca5d3e89244c3df4bee3d5ec42ca03986073d36eb35cd6977200103dcd1d868c8717dd77e27c0f4af0ec0f21ccd62cb75088a343ec06fecc9f3e524b0655cbfbe2be22790ec0dd2457dcd1f0c7a3b12da4de46d0986e54ce12ec740f9b89941b4a0e2a04d1b820d07daa594d38bf025440a4e6e9b40d6924d123d6ab9e3c32800af293a451af5b7669eb75063c9296cf7a68a446e6a235440e255c2af3805e2b56e7b637dbfbe6e6e87dabb9fd5dd7160b5bedefe0726b6f3d7abe6f6f9e1c7d7ef0f9f51cc8d3c536cf3aecc49ebe50ff10ad917e1f40d245069ed0bc0d22d362a8e313c55add3c41ec742c4566b53c1b64d323dce320c9b8595a177a7ee30ea994c452e8706258bf9ae15edcc435d145a55c52f0eb0228f7c9ce477cd410fa52f0f171ae9421d80e539837b9eae233bd42c39df1727812de3faf55d113f81947a6182f67aff53b1c9ee493d8fde7d119df63b6add35b57672c17c4f11d09430720b2949e1a5c29448a4ce4b312ee674c038a06942d375a0715f8a9fdce4f5602fe05935eea7a38229581697c69c50b21dbb65eb26f585f02ae5085748298c6d375a8367c85e2f27ad259b247a0b283cf15c40560bad5ce70935db29f932124f6328e3b42e67110740d3561518323633e6d04100d3f99213d832ae5fdf7506a0906654d86670d43bfe9cfae90c5b6ce85afa10ef7afa22a63e9bb1c475745d860a096893948305f432ccae7774887223246a755752e2e04144c24b8366f85707e0f01c3409a0429666061c7a47d772c6cc04b68cebd77745ff049251485dbc2789c39939ece5ea3fb9af2b71ffdf970eeeddea987ddbffeda46c848cddfdad86de4c32992c81ebb4d9fd79509a220c6b96f3c15c621a959e2e2593810a433c0466c1e2e1e2abf5956f6beebfac0e078dabd5e1eaeadbcbf7c7cf578fff7e7a5ded2ed7ebd57fafaf9efeb8faf9f27af5ede1f9f9e1ebf3e3eacfc7d787d7d75faba6f9faf7d3f3ebd3cfdf4d737df5e1dbcb8fbf9e9e1f3f5c5ffdf3e5e776e9d5cbaf2b6deae63071bba4697ebc7cfffbf9b169b6ab3e7c585ffdd7edd58776e60797809f0f3f86cb5b18578fcfbf1f0f5056bba4e93b60d565d1feee6af04abe4e7fb6eebb5bb5133e7e6caf3e1d2ed6f79f8202df24b613c2ababc339a2b95f0fe7e678ea7307adbde0807ada0de0abbb7bf75c151af5dac174589b65d9bef0e6c89bf5ff03329e6814'.replace(
'\n', ''))).decode()
)
Reversing the decompiled code
The decompiled code might look a bit menacing at first, but actually all it does is:
- call the
decompress
function in thezlib
module - pipe the result into
exec
So the actual code is compressed, let’s remove the call to exec
and move the result from decompress
to another file
Reversing decompressed code from decompiled code (my head might start to hurt)
Well this is… something.
I tried to reverse this by renaming variables, but that was not a great idea, not only because it took like half an hour to refactor the name of a single variable, but also when it was finally done, the code was not executable anymore.
Oh btw, we can run this and get the same output as before
1
2
3
# python3 really_obfuscated_thing.py
Encrypted flag: bd7e9dad4a5fe0e7911f93cb1bf5a321
So my next thought is that this whole obfuscated mess must be building some code that is at least executable and then running it with exec
or something like that.
So what I tried is hooking exec
by adding the following at the start of the program
1
2
3
4
oexec = exec
def exec(command):
print(command)
oexec(command)
and the output is…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lllllllllllllll,llllllllllllllI,lllllllllllllIl,lllllllllllllII,llllllllllllIll,llllllllllllIlI,llllllllllllIIl=bytearray,print,enumerate,chr,ord,len,bytes
from Crypto.Cipher import AES as IIIIlIIlIIllII
from Crypto.Util.Padding import pad as IIlIlIIllllIlI
def lIlIIIlllllIlllIll(IlIlIlIIlllIIlIlll):return''.join(lllllllllllllII(llllllllllllIll(A)^42)for A in IlIlIlIIlllIIlIlll)
def lllIIllllIIIIlllll(lIIllllllIlllIIIll,llIllIIlllIlIIllII):B=llIllIIlllIlIIllII;A=lIIllllllIlllIIIll;return A<<B&255|A>>8-B
def IllIlllIIlllIIIIlI(lIlIllIlllIlIlIIIl,IlllIIlIIIllIlIlII,lIIIIllIllllIIIlII):
B=lIIIIllIllllIIIlII;A=IlllIIlIIIllIlIlII;C=lllllllllllllll();E=llllllllllllIlI(A);F=llllllllllllIlI(B)
for(D,G)in lllllllllllllIl(lIlIllIlllIlIlIIIl):H=(A[D%E]+B[D%F])%8;C.append(lllIIllllIIIIlllll(G,H))
return C
def IIIllIlIIIIlIlIlIl(IIIIlllIIIIIlIIIll,llIllIlllIIIlllIll,IIllIlllIIIlIIlIlI):A=IIIIlIIlIIllII.new(llIllIlllIIIlllIll,IIIIlIIlIIllII.MODE_CBC,IIllIlllIIIlIIlIlI);return A.encrypt(IIlIlIIllllIlI(IIIIlllIIIIIlIIIll,IIIIlIIlIIllII.block_size))
def lIlIlIllIlIlIlIIll(lIIIIlIlIIIIIIIIIl):return llllllllllllIIl.fromhex(lIIIIlIlIIIIIIIIIl)
IIllIllIlIllIIIIIl='[redacted]'
IIllIIlIllIlIIlIll='fa21c9c2596099915dbc7845c941c14e81594b5c4f69177cc4059da11e782e0b'
IlllIlIlllIIIIIIll='504f43544632303234'
llIllIIlllIlIIllII='437261636b3430302d58'
IllIllllllIIIllIIl=lIlIIIlllllIlllIll(IIllIllIlIllIIIIIl)
IIllllIlIlIIIIIIll=lIlIlIllIlIlIlIIll(IlllIlIlllIIIIIIll)
if llllllllllllIlI(IIllllIlIlIIIIIIll)<16:IIllllIlIlIIIIIIll=IIllllIlIlIIIIIIll.ljust(16,b'\x00')
IlllIIlIIIllIlIlII=llIllIIlllIlIIllII[:32]if llllllllllllIlI(llIllIIlllIlIIllII)>=32 else llIllIIlllIlIIllII.ljust(32,'0')
lIIIIllIllllIIIlII=llllllllllllIIl.fromhex(IlllIIlIIIllIlIlII)
lllllIIllllIlIllll=IllIlllIIlllIIIIlI(IllIllllllIIIllIIl.encode('utf-8'),IIllllIlIlIIIIIIll,lIIIIllIllllIIIlII)
IIIlIIllllIlllIlII=IIIllIlIIIIlIlIlIl(lllllIIllllIlIllll,IIllllIlIlIIIIIIll,lIIIIllIllllIIIlII)
llllllllllllllI('Encrypted flag:',IIIlIIllllIlllIlII.hex())
__import__('sys').exit()
Reversing the deobfuscated code from the decompressed code from the decompiled code (my head 100% hurts now)
Okay this at least I can work with, after a long session of renaming and refactoring I ended up with this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# bytearray,print,enumerate,chr,ord,len,bytes=bytearray,print,enumerate,chr,ord,len,bytes
from Crypto.Cipher import AES as AES
from Crypto.Util.Padding import pad as pad
def xor42(data_string):
return ''.join(chr(ord(A) ^ 42) for A in data_string)
def shifts_masks_sub(data,
param2):
ret = data << param2 & 255 | data >> 8 - param2
return ret
def three_way_encode(param1, key, iv):
bytesarray = bytearray()
param2_len = len(key)
param3_len = len(iv)
for (i, data) in enumerate(param1):
weird_mod_8 = (key[i % param2_len] + iv[i % param3_len]) % 8
bytesarray.append(shifts_masks_sub(data, weird_mod_8))
return bytesarray
def encrypt_flag(encoded_flag, key, param3):
cipher = AES.new(key,
AES.MODE_CBC,
param3)
return cipher.encrypt(
pad(encoded_flag, AES.block_size))
def bytesfromhex(hex_string): return bytes.fromhex(hex_string)
redacted = '[redacted]'
IIllIIlIllIlIIlIll = 'fa21c9c2596099915dbc7845c941c14e81594b5c4f69177cc4059da11e782e0b'
key = '504f43544632303234' # POCTF2024
iv = '437261636b3430302d58' # Crack400-X
redacted_xor42 = xor42(redacted)
key_bytes = bytesfromhex(key)
if len(key_bytes) < 16:
key_bytes = key_bytes.ljust(16, b'\x00')
iv_32 = iv[:32] if len(iv) >= 32 else iv.ljust(32, '0')
iv_32_bytes = bytes.fromhex(iv_32)
encoded_flag = three_way_encode(redacted_xor42.encode('utf-8'), key_bytes, iv_32_bytes)
encrypted_flag = encrypt_flag(encoded_flag, key_bytes, iv_32_bytes)
print('Encrypted flag:', encrypted_flag.hex())
__import__('sys').exit()
Key things to notice
- the
IIllIIlIllIlIIlIll
variable is never used (probably the encrypted flag hidden in there) - There is encryption but the whole flag encryption process takes three steps
- Every character is XORed with decimal 42
- Some form of encoding done in the
three_way_encode()
function - AES encryption
The first and last steps are easy to reverse since we already have the key and iv values for the encryption.
How does the “three way encoding” work? (btw I only called it that because it takes three parameters)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def shifts_masks_sub(data,
param2):
ret = data << param2 & 255 | data >> 8 - param2
return ret
def three_way_encode(param1, key, iv):
bytesarray = bytearray()
param2_len = len(key)
param3_len = len(iv)
for (i, data) in enumerate(param1):
weird_mod_8 = (key[i % param2_len] + iv[i % param3_len]) % 8
bytesarray.append(shifts_masks_sub(data, weird_mod_8))
return bytesarray
three_way_encode
analysis:
- a for loop is initiated iterating over the flag, inside this loop:
- the variable
weird_mod_8
is constructed with data from thekey
andiv
, since it does not depend on the flag, we can reconstruct it shifts_masks_sub
is called passing it data from the current index of the encoded flag and theweird_mod_8
value- the result of the previous call is appended to
bytesarray
- the variable
bytesarray
is returned
shifts_masks_sub
analysis:
param2
is a number between 0 and 7- data is split at
param2
index - the left side is moved to the right
- the right side is moved to the left
- mixed data is returned
Finally, build a script to reverse this mess
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
def decrypt_flag(encrypted_flag, key, iv):
cipher = AES.new(key,
AES.MODE_CBC,
iv)
return cipher.decrypt(pad(encrypted_flag, AES.block_size))
def undo_shifts_masks_sub(ret, param2):
data = ret << (8 - param2) & 255 | ret >> param2
return data
def three_way_decode(encoded_flag, key, iv):
decoded_flag = ""
key_len = len(key)
iv_len = len(iv)
for (i, data) in enumerate(encoded_flag):
weird_mod_8 = (key[i % key_len] + iv[i % iv_len]) % 8
decoded_flag += chr(undo_shifts_masks_sub(data, weird_mod_8))
return decoded_flag
def xor42(data_string):
return ''.join(chr(ord(A) ^ 42) for A in data_string)
if __name__ == "__main__":
key = bytes.fromhex('504f43544632303234')
iv_str = '437261636b3430302d58'
if len(key) < 16:
key = key.ljust(16, b'\x00')
iv_32 = iv_str[:32] if len(iv_str) >= 32 else iv_str.ljust(32, '0')
iv = bytes.fromhex(iv_32)
encrypted_flag_hex = bytes.fromhex("fa21c9c2596099915dbc7845c941c14e81594b5c4f69177cc4059da11e782e0b")
encoded_flag = decrypt_flag(encrypted_flag_hex, key, iv)
flag_xor42 = three_way_decode(encoded_flag, key, iv)
flag = xor42(flag_xor42)
print(flag)
And the output of this is…
poctf{uwsp_4ll_7h47_gl1773r5})))ü©$¸Î¯k2ó"'
very weird, but you know, the flag is there.
Flag
user: root: