Skip to content

Commit

Permalink
Merge pull request #456 from kevross33/patch-65
Browse files Browse the repository at this point in the history
Update static_pe_anomaly.py
  • Loading branch information
doomedraven authored Oct 28, 2024
2 parents 4ce9562 + 07fdea8 commit a6a3a2f
Showing 1 changed file with 149 additions and 143 deletions.
292 changes: 149 additions & 143 deletions modules/signatures/all/static_pe_anomaly.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

import re
from datetime import datetime

from lib.cuckoo.common.abstracts import Signature


class PEAnomaly(Signature):
name = "static_pe_anomaly"
description = "Anomalous binary characteristics"
Expand All @@ -33,112 +31,113 @@ def run(self):
"10.0": (2014, 6),
}

if "pe" not in self.results.get("static", {}):
return False

compiletime = datetime.strptime(self.results["static"]["pe"]["timestamp"], "%Y-%m-%d %H:%M:%S")
osver = self.results["static"]["pe"]["osversion"]
osmajor = int(osver.split(".")[0], 10)
if osmajor < 4 and compiletime.year >= 2000:
self.data.append({"anomaly": "Minimum OS version is older than NT4 yet the PE timestamp year is newer than 2000"})
self.ttps += ["T1099"] # MITRE v6
self.ttps += ["T1070"] # MITRE v6,7,8
self.ttps += ["T1070.006"] # MITRE v7,8
self.mbcs += ["OB0006", "F0005", "F0005.004"]
self.weight += 1

# throw out empty timestamps
if compiletime.year > 1970 and osver in bad_date_map:
if compiletime.year < bad_date_map[osver][0] or (
compiletime.year == bad_date_map[osver][0] and compiletime.month < bad_date_map[osver][1]
):
self.data.append(
{"anomaly": "Timestamp on binary predates the release date of the OS version it requires by at least a year"}
)
self.ttps += ["T1099"] # MITRE v6
self.ttps += ["T1070", "T1070.006"] # MITRE v7,8
self.mbcs += ["OB0006", "F0005", "F0005.004"]
self.weight += 1

if "sections" in self.results["static"]["pe"]:
bigvirt = False
unprint = False
foundsec = None
foundcodesec = False
foundnamedupe = False
lowrva = 0xFFFFFFFF
imagebase = int(self.results["static"]["pe"]["imagebase"], 16)
eprva = int(self.results["static"]["pe"]["entrypoint"], 16) - imagebase
seennames = set()
for section in self.results["static"]["pe"]["sections"]:
if section["name"] in seennames:
foundnamedupe = True
seennames.add(section["name"])
if "IMAGE_SCN_CNT_CODE" in section["characteristics"]:
foundcodesec = True
if "\\x" in section["name"]:
unprint = True
secstart = int(section["virtual_address"], 16)
secend = secstart + int(section["virtual_size"], 16)

if (secend - secstart) >= 100 * 1024 * 1024:
bigvirt = True

# seconds are mapped first to last, so the last section matched is the correct one
if eprva >= secstart and eprva < secend:
foundsec = section
if secstart < lowrva:
lowrva = secstart
if foundnamedupe:
self.data.append({"anomaly": "Found duplicated section names"})
self.weight += 1
if unprint:
self.data.append({"anomaly": "Unprintable characters found in section name"})
self.weight += 1
if not foundsec and foundcodesec:
# we check for code sections to not FP on resource-only DLLs where the EP RVA will be 0
self.data.append({"anomaly": "Entrypoint of binary is located outside of any mapped sections"})
self.weight += 1
if foundsec and "IMAGE_SCN_MEM_EXECUTE" not in foundsec["characteristics"]:
# Windows essentially turns DEP off in this case, but it was only seen (as far as named packers go) in
# one instance I could think of years ago in a rare packer
self.data.append({"anomaly": "Entrypoint of binary points to a non-executable code section"})
self.weight += 1
if bigvirt:
# used to blow up memory dumpers
self.data.append({"anomaly": "Contains a section with a virtual size >= 100MB"})
self.weight += 1
if "resources" in self.results["static"]["pe"]:
for resource in self.results["static"]["pe"]["resources"]:
if int(resource["size"], 16) >= 100 * 1024 * 1024:
self.data.append({"anomaly": "Contains a resource with a size >= 100MB"})
self.weight += 1
target = self.results.get("target", {})
if target.get("category") in ("file", "static") and target.get("file"):
pe = self.results["target"]["file"].get("pe", [])
if pe:

if "versioninfo" in self.results["static"]["pe"]:
for ver in self.results["static"]["pe"]["versioninfo"]:
if (
ver["name"] == "OriginalFilename"
and ver["value"].lower().endswith(".dll")
and "PE32" in self.results.get("target", {})["file"].get("type", "")
and "DLL" not in self.results.get("target", {})["file"].get("type", "")
):
self.data.append(
{"anomaly": "OriginalFilename version info claims file is a DLL but binary is a main executable"}
)
compiletime = datetime.strptime(pe["timestamp"], "%Y-%m-%d %H:%M:%S")
osver = pe["osversion"]
osmajor = int(osver.split(".")[0], 10)
if osmajor < 4 and compiletime.year >= 2000:
self.data.append({"anomaly": "Minimum OS version is older than NT4 yet the PE timestamp year is newer than 2000"})
self.ttps += ["T1099"] # MITRE v6
self.ttps += ["T1070"] # MITRE v6,7,8
self.ttps += ["T1070.006"] # MITRE v7,8
self.mbcs += ["OB0006", "F0005", "F0005.004"]
self.weight += 1

if "reported_checksum" in self.results["static"]["pe"] and "actual_checksum" in self.results["static"]["pe"]:
reported = int(self.results["static"]["pe"]["reported_checksum"], 16)
actual = int(self.results["static"]["pe"]["actual_checksum"], 16)
if reported and reported != actual:
self.data.append({"anomaly": "Actual checksum does not match that reported in PE header"})
self.weight += 1
# throw out empty timestamps
if compiletime.year > 1970 and osver in bad_date_map:
if compiletime.year < bad_date_map[osver][0] or (
compiletime.year == bad_date_map[osver][0] and compiletime.month < bad_date_map[osver][1]
):
self.data.append(
{"anomaly": "Timestamp on binary predates the release date of the OS version it requires by at least a year"}
)
self.ttps += ["T1099"] # MITRE v6
self.ttps += ["T1070", "T1070.006"] # MITRE v7,8
self.mbcs += ["OB0006", "F0005", "F0005.004"]
self.weight += 1

if "sections" in pe:
bigvirt = False
unprint = False
foundsec = None
foundcodesec = False
foundnamedupe = False
lowrva = 0xFFFFFFFF
imagebase = int(pe["imagebase"], 16)
eprva = int(pe["entrypoint"], 16) - imagebase
seennames = set()
for section in pe["sections"]:
if section["name"] in seennames:
foundnamedupe = True
seennames.add(section["name"])
if "IMAGE_SCN_CNT_CODE" in section["characteristics"]:
foundcodesec = True
if "\\x" in section["name"]:
unprint = True
secstart = int(section["virtual_address"], 16)
secend = secstart + int(section["virtual_size"], 16)

if (secend - secstart) >= 100 * 1024 * 1024:
bigvirt = True

# seconds are mapped first to last, so the last section matched is the correct one
if eprva >= secstart and eprva < secend:
foundsec = section
if secstart < lowrva:
lowrva = secstart
if foundnamedupe:
self.data.append({"anomaly": "Found duplicated section names"})
self.weight += 1
if unprint:
self.data.append({"anomaly": "Unprintable characters found in section name"})
self.weight += 1
if not foundsec and foundcodesec:
# we check for code sections to not FP on resource-only DLLs where the EP RVA will be 0
self.data.append({"anomaly": "Entrypoint of binary is located outside of any mapped sections"})
self.weight += 1
if foundsec and "IMAGE_SCN_MEM_EXECUTE" not in foundsec["characteristics"]:
# Windows essentially turns DEP off in this case, but it was only seen (as far as named packers go) in
# one instance I could think of years ago in a rare packer
self.data.append({"anomaly": "Entrypoint of binary points to a non-executable code section"})
self.weight += 1
if bigvirt:
# used to blow up memory dumpers
self.data.append({"anomaly": "Contains a section with a virtual size >= 100MB"})
self.weight += 1
if "resources" in pe:
for resource in pe["resources"]:
if int(resource["size"], 16) >= 100 * 1024 * 1024:
self.data.append({"anomaly": "Contains a resource with a size >= 100MB"})
self.weight += 1

if "versioninfo" in pe:
for ver in pe["versioninfo"]:
if (
ver["name"] == "OriginalFilename"
and ver["value"].lower().endswith(".dll")
and "PE32" in self.results.get("target", {})["file"].get("type", "")
and "DLL" not in self.results.get("target", {})["file"].get("type", "")
):
self.data.append(
{"anomaly": "OriginalFilename version info claims file is a DLL but binary is a main executable"}
)
self.weight += 1

if "reported_checksum" in pe and "actual_checksum" in pe:
reported = int(pe["reported_checksum"], 16)
actual = int(pe["actual_checksum"], 16)
if reported and reported != actual:
self.data.append({"anomaly": "Actual checksum does not match that reported in PE header"})
self.weight += 1

if self.weight:
return True
return False


class StaticPEPDBPath(Signature):
name = "static_pe_pdbpath"
description = "The PE file contains a PDB path"
Expand Down Expand Up @@ -187,40 +186,43 @@ def run(self):
"- copy",
]

pdbpath = self.results.get("static", {}).get("pe", {}).get("pdbpath", "")
if pdbpath:
for suspiciousname in suspiciousnames:
if suspiciousname in pdbpath.lower():
if self.severity != 3:
self.severity = 3
self.data.append({"anomaly": "the pdb path contains a suspicious string"})
self.description = "The PE file contains a suspicious PDB path"
break

for devterm in devterms:
if devterm in pdbpath.lower():
if self.severity != 2 and self.severity != 3:
self.severity = 2
self.data.append(
{
"anomaly": "the pdb path contains a reference to a development path or term that may suggest a non-enterprise environment development/compilation"
}
)
self.description = "The PE file contains a suspicious PDB path"
break

regex = re.compile("[a-zA-Z]:\\\\[\x00-\xFF]{0,500}[^\x00-\x7F]{1,}[\x00-\xFF]{0,500}\.pdb")
if re.match(regex, pdbpath):
if self.severity != 2 and self.severity != 3:
self.severity = 2
self.data.append({"anomaly": "the pdb path contains non-ascii characters"})
self.description = "The PE file contains a suspicious PDB path"

self.data.append({"pdbpath": pdbpath})
ret = True
target = self.results.get("target", {})
if target.get("category") in ("file", "static") and target.get("file"):
pe = self.results["target"]["file"].get("pe", [])
if pe:
pdbpath = pe["pdbpath"]
if pdbpath:
for suspiciousname in suspiciousnames:
if suspiciousname in pdbpath.lower():
if self.severity != 3:
self.severity = 3
self.data.append({"anomaly": "the pdb path contains a suspicious string"})
self.description = "The PE file contains a suspicious PDB path"
break

return ret
for devterm in devterms:
if devterm in pdbpath.lower():
if self.severity != 2 and self.severity != 3:
self.severity = 2
self.data.append(
{
"anomaly": "the pdb path contains a reference to a development path or term that may suggest a non-enterprise environment development/compilation"
}
)
self.description = "The PE file contains a suspicious PDB path"
break

regex = re.compile("[a-zA-Z]:\\\\[\x00-\xFF]{0,500}[^\x00-\x7F]{1,}[\x00-\xFF]{0,500}\.pdb")
if re.match(regex, pdbpath):
if self.severity != 2 and self.severity != 3:
self.severity = 2
self.data.append({"anomaly": "the pdb path contains non-ascii characters"})
self.description = "The PE file contains a suspicious PDB path"

self.data.append({"pdbpath": pdbpath})
ret = True

return ret

class PECompileTimeStomping(Signature):
name = "pe_compile_timestomping"
Expand All @@ -235,16 +237,20 @@ class PECompileTimeStomping(Signature):
mbcs = ["OB0006", "F0005", "F0005.004"]

def run(self):
rawcompiletime = self.results.get("static", {}).get("pe", {}).get("timestamp", "")
if rawcompiletime:
compiletime = datetime.strptime(rawcompiletime, "%Y-%m-%d %H:%M:%S")
currentyear = datetime.now().year
currentmonth = datetime.now().month
if compiletime.year > currentyear:
self.data.append({"anomaly": "Compilation timestamp is in the future"})
return True
elif compiletime.year == currentyear and compiletime.month > currentmonth:
self.data.append({"anomaly": "Compilation timestamp is in the future"})
return True
target = self.results.get("target", {})
if target.get("category") in ("file", "static") and target.get("file"):
pe = self.results["target"]["file"].get("pe", [])
if pe:
rawcompiletime = pe["timestamp"]
if rawcompiletime:
compiletime = datetime.strptime(rawcompiletime, "%Y-%m-%d %H:%M:%S")
currentyear = datetime.now().year
currentmonth = datetime.now().month
if compiletime.year > currentyear:
self.data.append({"anomaly": "Compilation timestamp is in the future"})
return True
elif compiletime.year == currentyear and compiletime.month > currentmonth:
self.data.append({"anomaly": "Compilation timestamp is in the future"})
return True

return False

0 comments on commit a6a3a2f

Please sign in to comment.