Analyzing Supply Chain Malware Artifacts
Overview
Supply chain attacks compromise legitimate software distribution channels to deliver malware through trusted update mechanisms. Notable examples include SolarWinds SUNBURST (2020, affecting 18,000+ customers), 3CX SmoothOperator (2023, a cascading supply chain attack originating from Trading Technologies), and numerous npm/PyPI package poisoning campaigns. Analysis involves comparing trojanized binaries against legitimate versions, identifying injected code in build artifacts, examining code signing anomalies, and tracing the infection chain from initial compromise through payload delivery. As of 2025, supply chain attacks account for 30% of all breaches, a 100% increase from prior years.
Prerequisites
- Python 3.9+ with
pefile, ssdeep, hashlib
- Binary diff tools (BinDiff, Diaphora)
- Code signing verification tools (sigcheck, codesign)
- Software composition analysis (SCA) tools
- Access to legitimate software versions for comparison
- Package repository monitoring (npm, PyPI, NuGet)
Practical Steps
Step 1: Binary Comparison Analysis
#!/usr/bin/env python3
"""Compare trojanized binary against legitimate version."""
import hashlib
import pefile
import sys
import json
def compare_pe_files(legitimate_path, suspect_path):
"""Compare PE file structures between legitimate and suspect versions."""
legit_pe = pefile.PE(legitimate_path)
suspect_pe = pefile.PE(suspect_path)
report = {"differences": [], "suspicious_sections": [], "import_changes": []}
# Compare sections
legit_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in legit_pe.sections}
suspect_sections = {s.Name.rstrip(b'\x00').decode(): {
"size": s.SizeOfRawData,
"entropy": s.get_entropy(),
"characteristics": s.Characteristics,
} for s in suspect_pe.sections}
# Find new or modified sections
for name, props in suspect_sections.items():
if name not in legit_sections:
report["suspicious_sections"].append({
"name": name, "reason": "New section not in legitimate version",
"size": props["size"], "entropy": round(props["entropy"], 2),
})
elif abs(props["size"] - legit_sections[name]["size"]) > 1024:
report["suspicious_sections"].append({
"name": name, "reason": "Section size significantly changed",
"legit_size": legit_sections[name]["size"],
"suspect_size": props["size"],
})
# Compare imports
legit_imports = set()
if hasattr(legit_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in legit_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
legit_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
suspect_imports = set()
if hasattr(suspect_pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in suspect_pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
suspect_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")
new_imports = suspect_imports - legit_imports
if new_imports:
report["import_changes"] = list(new_imports)
# Check code signing
report["legit_signed"] = bool(legit_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
report["suspect_signed"] = bool(suspect_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)
return report
def hash_file(filepath):
"""Calculate multiple hashes for a file."""
hashes = {}
with open(filepath, 'rb') as f:
data = f.read()
for algo in ['md5', 'sha1', 'sha256']:
h = hashlib.new(algo)
h.update(data)
hashes[algo] = h.hexdigest()
return hashes
if __name__ == "__main__":
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} <legitimate_binary> <suspect_binary>")
sys.exit(1)
report = compare_pe_files(sys.argv[1], sys.argv[2])
print(json.dumps(report, indent=2))
Validation Criteria
- Trojanized components identified through binary diffing
- Injected code isolated and analyzed separately
- Code signing anomalies documented
- Infection timeline reconstructed from build artifacts
- Downstream impact scope assessed across affected systems
- IOCs extracted for detection and blocking
References
1---2name: analyzing-supply-chain-malware-artifacts3description: Investigate supply chain attack artifacts including trojanized software updates, compromised build pipelines, and sideloaded dependencies to identify intrusion vectors and scope of compromise.4license: Apache-2.05---6# Analyzing Supply Chain Malware Artifacts78## Overview910Supply chain attacks compromise legitimate software distribution channels to deliver malware through trusted update mechanisms. Notable examples include SolarWinds SUNBURST (2020, affecting 18,000+ customers), 3CX SmoothOperator (2023, a cascading supply chain attack originating from Trading Technologies), and numerous npm/PyPI package poisoning campaigns. Analysis involves comparing trojanized binaries against legitimate versions, identifying injected code in build artifacts, examining code signing anomalies, and tracing the infection chain from initial compromise through payload delivery. As of 2025, supply chain attacks account for 30% of all breaches, a 100% increase from prior years.1112## Prerequisites1314- Python 3.9+ with `pefile`, `ssdeep`, `hashlib`15- Binary diff tools (BinDiff, Diaphora)16- Code signing verification tools (sigcheck, codesign)17- Software composition analysis (SCA) tools18- Access to legitimate software versions for comparison19- Package repository monitoring (npm, PyPI, NuGet)2021## Practical Steps2223### Step 1: Binary Comparison Analysis2425```python26#!/usr/bin/env python327"""Compare trojanized binary against legitimate version."""28import hashlib29import pefile30import sys31import json323334def compare_pe_files(legitimate_path, suspect_path):35 """Compare PE file structures between legitimate and suspect versions."""36 legit_pe = pefile.PE(legitimate_path)37 suspect_pe = pefile.PE(suspect_path)3839 report = {"differences": [], "suspicious_sections": [], "import_changes": []}4041 # Compare sections42 legit_sections = {s.Name.rstrip(b'\x00').decode(): {43 "size": s.SizeOfRawData,44 "entropy": s.get_entropy(),45 "characteristics": s.Characteristics,46 } for s in legit_pe.sections}4748 suspect_sections = {s.Name.rstrip(b'\x00').decode(): {49 "size": s.SizeOfRawData,50 "entropy": s.get_entropy(),51 "characteristics": s.Characteristics,52 } for s in suspect_pe.sections}5354 # Find new or modified sections55 for name, props in suspect_sections.items():56 if name not in legit_sections:57 report["suspicious_sections"].append({58 "name": name, "reason": "New section not in legitimate version",59 "size": props["size"], "entropy": round(props["entropy"], 2),60 })61 elif abs(props["size"] - legit_sections[name]["size"]) > 1024:62 report["suspicious_sections"].append({63 "name": name, "reason": "Section size significantly changed",64 "legit_size": legit_sections[name]["size"],65 "suspect_size": props["size"],66 })6768 # Compare imports69 legit_imports = set()70 if hasattr(legit_pe, 'DIRECTORY_ENTRY_IMPORT'):71 for entry in legit_pe.DIRECTORY_ENTRY_IMPORT:72 for imp in entry.imports:73 if imp.name:74 legit_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")7576 suspect_imports = set()77 if hasattr(suspect_pe, 'DIRECTORY_ENTRY_IMPORT'):78 for entry in suspect_pe.DIRECTORY_ENTRY_IMPORT:79 for imp in entry.imports:80 if imp.name:81 suspect_imports.add(f"{entry.dll.decode()}!{imp.name.decode()}")8283 new_imports = suspect_imports - legit_imports84 if new_imports:85 report["import_changes"] = list(new_imports)8687 # Check code signing88 report["legit_signed"] = bool(legit_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)89 report["suspect_signed"] = bool(suspect_pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].Size)9091 return report929394def hash_file(filepath):95 """Calculate multiple hashes for a file."""96 hashes = {}97 with open(filepath, 'rb') as f:98 data = f.read()99 for algo in ['md5', 'sha1', 'sha256']:100 h = hashlib.new(algo)101 h.update(data)102 hashes[algo] = h.hexdigest()103 return hashes104105106if __name__ == "__main__":107 if len(sys.argv) < 3:108 print(f"Usage: {sys.argv[0]} <legitimate_binary> <suspect_binary>")109 sys.exit(1)110 report = compare_pe_files(sys.argv[1], sys.argv[2])111 print(json.dumps(report, indent=2))112```113114## Validation Criteria115116- Trojanized components identified through binary diffing117- Injected code isolated and analyzed separately118- Code signing anomalies documented119- Infection timeline reconstructed from build artifacts120- Downstream impact scope assessed across affected systems121- IOCs extracted for detection and blocking122123## References124125- [ReversingLabs - 3CX Supply Chain Analysis](https://www.reversinglabs.com/blog/what-went-wrong-with-the-3cx-software-supply-chain-attack-and-how-it-could-have-been-prevented)126- [Fortinet - SolarWinds Supply Chain Attack](https://www.fortinet.com/resources/cyberglossary/solarwinds-cyber-attack)127- [Picus - 3CX SmoothOperator Analysis](https://www.picussecurity.com/resource/blog/smoothoperator-analysis-of-3cxdesktopapp-supply-chain-attack)128- [MITRE ATT&CK T1195 - Supply Chain Compromise](https://attack.mitre.org/techniques/T1195/)