Intro
I like reverse engineering software for fun, just to understand how it works under the hood. I’ve learned and keep learning a lot by reading other people’s code. But one thing rarely changes: the use of “snake oil” crypto to hide data that doesn’t need hiding. In this post, I’ll walk through a couple of examples of this kind of security-by-obscurity.
Please note that the findings below don’t say anything about the overall quality or security of the mentioned software. They only reflect the poor or missing security in the specific parts I analyzed.
Binalyze is a cybersecurity platform that automates and accelerates digital forensics and incident response processes.
I downloaded a scanner from their website and tried to understand what it actually does. It’s supposedly used to gather evidence from a machine. Looking through the files, I found a large offnetwork_darwin_arm64 binary, along with Task.dat and mitre.zip.
I opened Task.dat in a text editor and saw a base64-encoded blob. When I tried to decode it, it turned out to be encrypted. Why would anyone both encrypt and base64-encode a file? There must be something super important in there if they’re going to that much trouble, right? I opened the binary in a disassembler, and here’s how you can decrypt this “super secret” file:
import base64
import json
def rc4(key, data):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
out = bytearray(len(data))
for k in range(len(data)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out[k] = data[k] ^ S[(S[i] + S[j]) % 256]
return bytes(out)
def main():
with open("Task.dat", "r") as f:
raw = f.read()
data = base64.b64decode(raw)
key = b"REDACTED"
decrypted = rc4(key, data)
obj = json.loads(decrypted)
with open("Task.json", "w") as f:
f.write(json.dumps(obj, indent=2))
print(f"Task.data decrypted")
if __name__ == "__main__":
main()It’s just RC4 with a hardcoded key, wrapped in base64. After decrypting it and opening it in an editor, I found… nothing important. No secret information whatsoever. The only remotely “sensitive” thing in there is this section, which only appears if you enable encryption for data collection:
"encryption": {
"enabled": true,
"password": "secretPassword"
}I don’t understand why this config file is encrypted at all. And if you’re going to encrypt it, why base64-encode it on top? It adds nothing in terms of security or reliability. Anyone can still modify Task.dat, since there’s no signature check whatsoever.
Here’s another example from the same software. It embeds every helper binary inside offnetwork_darwin_arm64, then extracts and runs them with parameters at execution time (into the current working directory, for some reason). As a bit of “snake oil” protection, when it launches these child processes, it passes along a machine ID so that, supposedly, only the parent process can run them. What does this mighty protection actually look like?
import hashlib
import socket
def muid():
hostname = socket.gethostname()
if not hostname:
hostname = "unknown"
name = hostname.lower()
utf16le = name.encode("utf-16-le")
sha1 = hashlib.sha1(utf16le).digest()
h = sha1.hex()
return f"{h[0:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:32]}"
if __name__ == "__main__":
print(muid())It’s just the SHA1 hash of the hostname, formatted to look like a UUID. This “security” check is snake oil. Anyone who knows how to use a disassembler or AI can find it in minutes. If they’d invested in an Apple Developer Certificate instead, they could have avoided Gatekeeper warnings and gotten real binary signature verification and a proper trust chain.
Does it end there? Of course not. There’s yet another layer of encryption to get through. The binary ships with mitre.zip, which contains a bunch of YARA rules which are also encrypted. Is it RC4 again? Of course not, that would be too consistent. This time it’s AES-256-GCM. Decrypt that, and you get… another zip file. Plain zip? Nope, it’s a WinZip-AES-encrypted archive with its own password. Decrypt that, and you finally get to the actual YARA rules.
As an extra bit of fun, the drone binary has its own separate mitre.zip embedded inside which has the exact same weird encryption scheme, duplicated. I genuinely don’t understand why they didn’t just reference the same file instead of embedding a duplicate copy.
To prove my claims, here is excerpt from mitre__Families__3CX__RC4_key_3CX.yara file.
meta:
...
reference = "https://www.trendmicro.com/en_us/research/23/c/information-on-attacks-involving-3cx-desktop-app.html"
...
strings:
$ = "3jB(2bsG#@c7"My point is this: as a software vendor, I understand wanting to hide or encrypt something to protect your IP. But there’s no value in encrypting data that doesn’t need protecting, or in stacking layer after layer of encryption when the keys are hardcoded right there in the binary. Obscurity isn’t security and if the key ships alongside the lock, you haven’t built a lock at all.
