Home pointer overflow 2024 Reverse400-1
Writeup
Cancel

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

img_29.png

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.

img_30.png

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 the zlib 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)

img_31.png

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 the key and iv, 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 the weird_mod_8 value
    • the result of the previous call is appended to bytesarray
  • 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})))ü©$¸Î¯kž2ó"'‚

very weird, but you know, the flag is there.

Flag

user: root:
Trending Tags