Overview

We are going to be analyzing an unknown python stealer that comes bundled as a pyinstaller. Initially we took the standard static approach of extracting and decompiling the python code but the decompiler did not fully work (python version 3.11) and the python code was heavily obfuscated. Normally we would use a trick of replacing eval and exec with print statements in the python runtime as a way to simply deobfuscate the script but the fact that it was not correctly decompiled makes this difficult. Instead we are going to approach it dynamically.

References

Sample

2a19ba63e85ce75d5f2d884011dfc94f616b176ed89a67c1acc0fe2179e8b591 UnpacMe

Analysis

  • First extracting the pyinstaller we can see that the main python file is called mPSCzi.pyc and the bundled python dll is python 3.11
  • Decompiling with pylingual show us the obfuscated code (snip below)
strpjab = tvnboaswivayssk - sjeqavagljouxg
tzvzwyilyuilob = fmikzvuexqgiygyrsw - laswhphcwhrgd
nvpvfxkszm = tvxzshhdfwer * tvnboaswivayssk
mxszwtywur = stzhmggz - mguyywdexiw
eval(bmurybzixs('d3NqcmN3Z3JweGlrYXp6biArIGpsc2h6cmlndCArIG92Z2hjZ3JpZ2JicCArIHh3Y3l1d3hmdnRlICsgZnl5bGp4eGV0ICsgYWZtanVwb2V3ICsgamZ5cG5mciArIGxhc3docGhjd2hyZ2QgKyBwZ3Z0YWp4ZWl5YWxyZyArIHN0emhtZ2d6ICsgc2plcWF2YWdsam91eGcgKyB0dnh6c2hoZGZ3ZXIgKyBnZXZzem9lcHpmd3B4YSArIG5saWxkdWliYnpuICsgbndiaHFobWZlcmZ6ZnV1ICsgdnV4Y2h5a3RuICsgenJvdXRndmdoY2FxbXJzYXUgKyB0dm5ib2Fzd2l2YXlzc2sgKyBmbWlrenZ1ZXhxZ2l5Z3lyc3cgKyB3d2l0ZG55ZyArIG1ndXl5d2RleGl3').decode())

Dynamic Analysis

Our dynamic approach will be to directly load the compiled script using the regular python interpreter instead of the pysintaller binary. This will allow us to load symbols for the python dll and attach to it with a debugger. Using the debugger we will break on the function PyParser_ASTFromString which is the internal method used for dynamically compiling python called by eval and exec.

  • Dumping the strings from the python 3.11 dll we can see the specific version is 3.11.9
  • We need the install the exact version so that we can run the compiled mPSCzi.pyc file
  • We can then add a break point on PyParser_ASTFromString and add a log command in the breakpoint to log the first argument (the python code) {s:utf8(rcx, A00000)}
  • With this we can then see all of the evaled code is then dumped into the log

The dumped code can be found here pastebin

Final Stage C2s

The final stage is also obfuscated but parts of the code can be run to deobfuscate it dynamically.

import base64

ZWHmjDkfVvkVdclV = 'bWdzdHN0dWRpby5zaG9w'
RYBTeAeZbiUOxeloi = base64.b64decode(ZWHmjDkfVvkVdclV).decode()

print(f'https://{RYBTeAeZbiUOxeloi}/sunrise')
print(f'https://{RYBTeAeZbiUOxeloi}/luminous')
https://mgststudio.shop/sunrise
https://mgststudio.shop/luminous