Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/kevoreilly/CAPEv2
Browse files Browse the repository at this point in the history
  • Loading branch information
doomedraven committed Mar 5, 2024
2 parents 24f11ee + 0277e8c commit 76754ee
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 13 deletions.
62 changes: 51 additions & 11 deletions modules/processing/parsers/CAPE/XWorm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,49 @@
import dnfile
from Cryptodome.Cipher import AES

pattern = re.compile(
confPattern = re.compile(
rb"""(?x)
\x72(...)\x70\x80...\x04
""",
re.DOTALL,
)

mutexPattern = re.compile(
mutexPattern1 = re.compile(
rb"""(?x)
\x72(...)\x70\x80...\x04
\x72...\x70\x28...\x0A
""",
re.DOTALL,
)

mutexPattern2 = re.compile(
rb"""(?x)
\x72(...)\x70\x80...\x04\x2A
""",
re.DOTALL,
)

installBinNamePattern = re.compile(
rb"""(?x)
\x72(...)\x70\x80...\x04
\x72...\x70\x80...\x04
\x72...\x70\x28...\x0A
""",
re.DOTALL,
)

installDirPattern = re.compile(
rb"""(?x)
\x72(...)\x70\x80...\x04
\x72...\x70\x80...\x04
\x72...\x70\x80...\x04
\x72...\x70\x28...\x0A
""",
re.DOTALL,
)

mutexPatterns = [mutexPattern1, mutexPattern2]


def deriveAESKey(encryptedMutex: str):
md5Hash = hashlib.md5(encryptedMutex.encode()).hexdigest()
Expand Down Expand Up @@ -52,16 +80,18 @@ def extract_config(data):

## Mutex is used to derive AES key, so if it's not found, the extractor is useless
## The main problem is Mutex is not found in fixed location, so this trick is used to find the Mutex
mutexMatched = mutexPattern.findall(data)
if mutexMatched:
mutex = dn.net.user_strings.get_us(int.from_bytes(mutexMatched[0], "little")).value
else:
return

for match in pattern.findall(data):
for pattern in mutexPatterns:
mutexMatched = pattern.findall(data)
if mutexMatched:
mutex = dn.net.user_strings.get_us(int.from_bytes(mutexMatched[0], "little")).value
AESKey = deriveAESKey(mutex)
break
else:
return

for match in confPattern.findall(data):
er_string = dn.net.user_strings.get_us(int.from_bytes(match, "little")).value
extracted.append(er_string)
AESKey = deriveAESKey(mutex)

for i in range(5):
with suppress(Exception):
Expand All @@ -76,9 +106,19 @@ def extract_config(data):
config_dict["SPL"] = conf[3]
else:
config_dict["Port"] = ""
config_dict["Key"] = conf[1]
config_dict["AES Key (decrypt/encrypt connections)"] = conf[1]
config_dict["SPL"] = conf[2]
config_dict["AES Key (decrypt configs)"] = AESKey
config_dict["Mutex"] = mutex

installBinMatch = installBinNamePattern.findall(data)
installDirMatch = installDirPattern.findall(data)

if installDirMatch:
installDir = dn.net.user_strings.get_us(int.from_bytes(installDirMatch[0], "little")).value
config_dict["InstallDir"] = decryptAES(AESKey, installDir, AES.MODE_ECB)
if installBinMatch:
installBinName = dn.net.user_strings.get_us(int.from_bytes(installBinMatch[0], "little")).value
config_dict["InstallBinName"] = decryptAES(AESKey, installBinName, AES.MODE_ECB)

return config_dict
4 changes: 2 additions & 2 deletions tests_parsers/test_oyster.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from modules.processing.parsers.CAPE.Oyster import extract_config


def test_zloader():
def test_oyster():
with open("tests/data/malware/8bae0fa9f589cd434a689eebd7a1fde949cc09e6a65e1b56bb620998246a1650", "rb") as data:
conf = extract_config(data.read())
assert conf == {
"C2": ["https://connectivity-check.linkpc.net/"],
"Dll Version": "v1.1 #ads 5",
"Dll Version": "v1.0 #ads 2",
"Strings": ["api/connect", "Content-Type: application/json", "api/session"],
}

0 comments on commit 76754ee

Please sign in to comment.