Skip to content

Commit ae887ad

Browse files
authored
Merge pull request #237 from target/ScanMsi
ScanMsi
2 parents ed23a66 + 048b035 commit ae887ad

File tree

6 files changed

+156
-33
lines changed

6 files changed

+156
-33
lines changed

.github/workflows/pr-actions.yaml

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,38 @@ jobs:
1717
- name: Install dependencies
1818
run: |
1919
sudo apt-get -q update
20-
sudo apt-get install --no-install-recommends -qq automake \
21-
build-essential \
22-
libfuzzy-dev \
23-
gcc \
24-
git \
25-
libarchive-dev \
26-
libmagic-dev \
27-
libssl-dev \
28-
libzbar0 \
29-
libgl1 \
30-
python3-setuptools \
31-
libgmp-dev \
32-
libpcap-dev \
33-
libbz2-dev \
34-
libgomp1 \
35-
python3-dev \
36-
python3-wheel \
37-
mupdf-tools \
38-
mupdf \
39-
libglu1-mesa \
40-
libtool \
41-
pkg-config \
42-
swig \
43-
tesseract-ocr
20+
sudo apt-get install --no-install-recommends -qq \
21+
automake \
22+
build-essential \
23+
curl \
24+
gcc \
25+
git \
26+
libglu1-mesa \
27+
libtool \
28+
make \
29+
swig \
30+
python3-dev \
31+
python3-pip \
32+
python3-wheel \
33+
pkg-config \
34+
antiword \
35+
libarchive-dev \
36+
libfuzzy-dev \
37+
libmagic-dev \
38+
libssl-dev \
39+
libzbar0 \
40+
libgl1 \
41+
python3-setuptools \
42+
redis-server \
43+
tesseract-ocr \
44+
unrar \
45+
upx \
46+
jq \
47+
exiftool && \
4448
python -m pip install --upgrade pip
4549
pip install validators setuptools --upgrade
4650
pip install --no-cache-dir -r src/python/requirements.txt
4751
- name: Test with pytest
4852
run: |
4953
pytest
5054
51-

build/python/backend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LABEL maintainer="Target Brands, Inc. TTS-CFC-OpenSource@target.com"
55
ARG YARA_VERSION=4.2.3
66
ARG YARA_PYTHON_VERSION=4.2.3
77
ARG CAPA_VERSION=4.0.1
8-
ARG EXIFTOOL_VERSION=12.48
8+
ARG EXIFTOOL_VERSION=12.52
99

1010
# Update packages
1111
RUN apt-get -qq update && \

configs/python/backend/backend.yaml

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,13 @@ scanners:
238238
priority: 5
239239
options:
240240
parser: "html5lib"
241-
'ScanIni':
242-
- positive:
243-
filename: '(\.([Cc][Ff][Gg]|[Ii][Nn][Ii])|PROJECT)$'
244-
flavors:
245-
- 'ini_file'
246-
- 'olecf_file'
247-
priority: 5
241+
# 'ScanIni':
242+
# - positive:
243+
# filename: '(\.([Cc][Ff][Gg]|[Ii][Nn][Ii])|PROJECT)$'
244+
# flavors:
245+
# - 'ini_file'
246+
# - 'olecf_file'
247+
# priority: 5
248248
'ScanIso':
249249
- positive:
250250
flavors:
@@ -338,6 +338,30 @@ scanners:
338338
flavors:
339339
- 'browser_manifest'
340340
priority: 5
341+
'ScanMsi':
342+
- positive:
343+
flavors:
344+
- 'image/vnd.fpx'
345+
- 'application/x-msi'
346+
priority: 5
347+
options:
348+
tmp_directory: '/dev/shm/'
349+
keys:
350+
- 'Author'
351+
- 'Characters'
352+
- 'Company'
353+
- 'CreateDate'
354+
- 'LastModifiedBy'
355+
- 'Lines'
356+
- 'ModifyDate'
357+
- 'Pages'
358+
- 'Paragraphs'
359+
- 'RevisionNumber'
360+
- 'Software'
361+
- 'Template'
362+
- 'Title'
363+
- 'TotalEditTime'
364+
- 'Words'
341365
'ScanMmbot':
342366
- positive:
343367
flavors:
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import json
2+
import subprocess
3+
import tempfile
4+
5+
from strelka import strelka
6+
7+
8+
class ScanMsi(strelka.Scanner):
9+
"""Collects metadata parsed by Exiftool.
10+
11+
Options:
12+
keys: exiftool key values to log in the event.
13+
Defaults to all.
14+
tmp_directory: Location where tempfile writes temporary files.
15+
Defaults to '/tmp/'.
16+
"""
17+
18+
def scan(self, data, file, options, expire_at):
19+
# Get a list of keys to collect from the MSI file
20+
keys = options.get('keys', [])
21+
22+
# Get the temporary directory to write the MSI file to
23+
tmp_directory = options.get('tmp_directory', '/tmp/')
24+
25+
with tempfile.NamedTemporaryFile(dir=tmp_directory) as tmp_data:
26+
# Write the MSI data to the temporary file
27+
tmp_data.write(data)
28+
tmp_data.flush()
29+
30+
# Run exiftool to extract metadata from the file
31+
try:
32+
(stdout, stderr) = subprocess.Popen(
33+
['exiftool', '-d', '"%s"', '-j', tmp_data.name],
34+
stdout=subprocess.PIPE,
35+
stderr=subprocess.DEVNULL,
36+
).communicate()
37+
except Exception as e:
38+
# Handle any exceptions raised while running exiftool
39+
self.flags.append(f'msi_extract_error: {e}')
40+
return
41+
42+
if stdout:
43+
# Load the metadata from exiftool's JSON output
44+
try:
45+
exiftool_dictionary = json.loads(stdout)[0]
46+
except ValueError as e:
47+
# Handle any errors while parsing the JSON output
48+
self.flags.append(f'msi_parse_error: {e}')
49+
return
50+
51+
for k, v in exiftool_dictionary.items():
52+
# Only collect the keys specified in the `keys` list
53+
if keys and k not in keys:
54+
continue
55+
56+
# Add the metadata key and value to the event
57+
self.event[k] = v
3.5 KB
Binary file not shown.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import datetime
2+
from pathlib import Path
3+
from strelka.scanners.scan_msi import ScanMsi
4+
5+
6+
def test_scan_msi(mocker):
7+
"""
8+
This tests the ScanMsi scanner.
9+
It attempts to validate several given MSI metadata values.
10+
11+
Pass: Metadata values from file match specified values.
12+
Failure: Unable to load file or metadata values do not match specified values.
13+
"""
14+
15+
scanner = ScanMsi(
16+
{
17+
"name": "ScanMsi",
18+
"key": "scan_msi",
19+
"limits": {"scanner": 10}
20+
},
21+
"test_coordinate",
22+
)
23+
24+
mocker.patch.object(ScanMsi, "upload_to_coordinator", return_value=None)
25+
scanner.scan_wrapper(
26+
Path(Path(__file__).parent / "fixtures/test.msi").read_bytes(),
27+
{
28+
"uid": "12345",
29+
"name": "somename"
30+
},
31+
{
32+
"scanner_timeout": 5
33+
},
34+
datetime.date.today(),
35+
)
36+
37+
assert scanner.event.get("Software") == "InstallShield® X - Professional Edition 10.0"
38+
assert scanner.event.get("Keywords") == "Installer,MSI,Database"
39+
assert scanner.event.get("Author") == "Microsoft Corporation"

0 commit comments

Comments
 (0)