Skip to content

Commit

Permalink
Merge pull request #346 from target/ScanVsto
Browse files Browse the repository at this point in the history
ScanVsto
  • Loading branch information
phutelmyer authored Mar 7, 2023
2 parents a463909 + b34e726 commit 3837231
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 14 deletions.
5 changes: 5 additions & 0 deletions configs/python/backend/backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,11 @@ scanners:
priority: 5
options:
analyze_macros: True
'ScanVsto':
- positive:
flavors:
- 'vsto_file'
priority: 5
'ScanX509':
- positive:
flavors:
Expand Down
16 changes: 16 additions & 0 deletions configs/python/backend/taste/taste.yara
Original file line number Diff line number Diff line change
Expand Up @@ -905,3 +905,19 @@ rule credit_cards {
condition:
any of them
}

rule vsto_file
{
meta:
description = "Detects Microsoft Office VSTO files"
reference = "https://www.deepinstinct.com/blog/no-macro-no-worries-vsto-being-weaponized-by-threat-actors"
type = "text"
strings:
$ = "urn:schemas-microsoft-com:asm.v1"
$ = /assemblyIdentity name=('|")[\w.]+\.vsto('|")/
$ = /dependencyType=('|")install('|")/
$ = /codebase=('|")[\w.]+\.manifest('|")/
condition:
magic.mime_type() == "text/xml" and
all of them
}
29 changes: 15 additions & 14 deletions docs/README.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions misc/kubernetes/backend-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,11 @@ data:
priority: 5
options:
limit: 100
'ScanVsto':
- positive:
flavors:
- 'vsto_file'
priority: 5
'ScanX509':
- positive:
flavors:
Expand Down
79 changes: 79 additions & 0 deletions src/python/strelka/scanners/scan_vsto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
This module provides a scanner that extracts information from VSTO files.
It defines the following class:
- ScanVsto: Scans VSTO files and extracts information like the name, assembly identity, dependencies,
publisher, and certificate.
"""


import base64
import hashlib

import xmltodict

from strelka import strelka


class ScanVsto(strelka.Scanner):
"""
Scanner class for extracting information from VSTO files.
This class provides a `scan` method that extracts information from VSTO files, and stores it in the `event`
dictionary attribute of the class.
"""

def scan(self, data, file, options, expire_at):
"""
Extracts information from the VSTO file.
Args:
data: The binary data of the VSTO file to be scanned.
file: File associated with data.
options: Any options passed in from the backend configuration file.
expire_at: The expiry time for this scan.
"""
try:
# As Vsto is in an XML format, parse the XML data
xml = xmltodict.parse(data)

# Extract the VSTO name
if props := xml.get("Properties"):
for prop in props.get("property", []):
if prop["vt:lpwstr"].endswith("vstolocal"):
self.event["vsto"] = prop["vt:lpwstr"].split("|")[0]

# Extract the assembly identity, dependencies, publisher, and certificate information
if asm := xml.get("asmv1:assembly"):
if asm.get("assemblyIdentity"):
self.event["identity"] = asm["assemblyIdentity"]["@name"]
self.event["dependency"] = {
"manifest": asm["dependency"]["dependentAssembly"]["@codebase"],
"name": asm["dependency"]["dependentAssembly"][
"assemblyIdentity"
]["@name"],
}
self.event["publisher"] = asm["publisherIdentity"]["@name"]
self.event["certificate"] = {
"b64": asm["Signature"]["KeyInfo"]["msrel:RelData"][
"r:license"
]["r:issuer"]["Signature"]["KeyInfo"]["X509Data"][
"X509Certificate"
],
"md5": hashlib.md5(
base64.b64decode(
asm["Signature"]["KeyInfo"]["msrel:RelData"][
"r:license"
]["r:issuer"]["Signature"]["KeyInfo"]["X509Data"][
"X509Certificate"
]
)
).hexdigest(),
}

except Exception as e:
print(e)
self.flags.append(f"{self.__class__.__name__} Exception: {str(e)[:100]}")
104 changes: 104 additions & 0 deletions src/python/strelka/tests/fixtures/test.vsto
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0"
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
xmlns="urn:schemas-microsoft-com:asm.v2"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1"
xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="TestName.vsto" version="1.0.0.1" publicKeyToken="09fad4d541a5a080" language="neutral" processorArchitecture="msil"
xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="TestPublisher" asmv2:product="TestProduct"
xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="false" />
<compatibleFrameworks
xmlns="urn:schemas-microsoft-com:clickonce.v2">
<framework targetVersion="4.5" profile="Full" supportedRuntime="4.0.30319" />
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="TestInstaller.dll.manifest" size="13456">
<assemblyIdentity name="TestIdentityName.dll" version="1.0.0.1" publicKeyToken="09fad4d541a5a080" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>hjtoNGCkEXL0ZSfJTK4WsgrsRjtcww3+IrtNnfR8h8o=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>
<publisherIdentity name="CN=TEST\test" issuerKeyHash="db88bb5ceab87f9c0fcc2ab36c189c2c" />
<Signature Id="StrongNameSignature"
xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<DigestValue>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</SignatureValue>
<KeyInfo Id="StrongNameKeyInfo">
<KeyValue>
<RSAKeyValue>
<Modulus>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
<msrel:RelData
xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata">
<r:license
xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS"
xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode">
<r:grant>
<as:ManifestInformation Hash="eb78b614be2e9c728abd7470cbd0a076e757d677e7264da98306d0d19548de86" Description="" Url="">
<as:assemblyIdentity name="TestIdentityName.vsto" version="1.0.0.1" publicKeyToken="09fad4d541a5a080" language="neutral" processorArchitecture="msil"
xmlns="urn:schemas-microsoft-com:asm.v1" />
</as:ManifestInformation>
<as:SignedBy />
<as:AuthenticodePublisher>
<as:X509SubjectName>CN=TEST\test</as:X509SubjectName>
</as:AuthenticodePublisher>
</r:grant>
<r:issuer>
<Signature Id="AuthenticodeSignature"
xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<DigestValue>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
<X509Data>
<X509Certificate>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</r:issuer>
</r:license>
</msrel:RelData>
</KeyInfo>
</Signature>
</asmv1:assembly>
35 changes: 35 additions & 0 deletions src/python/strelka/tests/test_scan_vsto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pathlib import Path
from unittest import TestCase, mock

from strelka.scanners.scan_vsto import ScanVsto as ScanUnderTest
from strelka.tests import run_test_scan


def test_scan_vsto(mocker):
"""
Pass: Sample event matches output of scanner.
Failure: Unable to load file or sample event fails to match.
"""
test_scan_event = {
"elapsed": mock.ANY,
"dependency": {
"manifest": "TestInstaller.dll.manifest",
"name": "TestIdentityName.dll",
},
"publisher": "CN=TEST\\test",
"certificate": {
"b64": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg==",
"md5": "db89bb5ceab87f9c0fcc2ab36c189c2c",
},
"identity": "TestName.vsto",
"flags": [],
}

scanner_event = run_test_scan(
mocker=mocker,
scan_class=ScanUnderTest,
fixture_path=Path(__file__).parent / "fixtures/test.vsto",
)

TestCase.maxDiff = None
TestCase().assertDictEqual(test_scan_event, scanner_event)

0 comments on commit 3837231

Please sign in to comment.