Skip to content

Commit 7d2bd1c

Browse files
Merge pull request #1456 from TheHive-Project/analyzers-security-hardening
EmlParser - Security Hardening
2 parents 99097fa + 82e4f3e commit 7d2bd1c

1 file changed

Lines changed: 32 additions & 9 deletions

File tree

analyzers/EmlParser/parse.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ def artifacts(self, raw):
168168
tags=["body:attachment", "autoImport:true"] + h["tag"],
169169
)
170170
)
171-
filepath = os.path.join(self.job_directory, "output", h.get("filename"))
171+
filepath = os.path.join(
172+
self.job_directory,
173+
"output",
174+
_attachment_output_name(h.get("filename", ""), h.get("hash")),
175+
)
172176
file_key = ("file", filepath)
173177
if file_key not in seen:
174178
seen.add(file_key)
@@ -193,6 +197,24 @@ def _add_ioc(ioc_list, data, tags):
193197
ioc_list.append({"data": data, "tag": list(tags)})
194198

195199

200+
def _safe_attachment_filename(raw_name):
201+
"""Reduce an attachment name to a safe basename, or None if unusable."""
202+
if not raw_name or "\x00" in raw_name:
203+
return None
204+
# normalise Windows separators first; "\" is not a separator on POSIX
205+
base = os.path.basename(raw_name.replace("\\", "/"))
206+
if not base or base in (".", "..") or os.path.isabs(base) or re.match(r"^[A-Za-z]:", base):
207+
return None
208+
return base
209+
210+
211+
def _attachment_output_name(raw_name, sha256):
212+
"""On-disk basename for an attachment, falling back to its hash."""
213+
return _safe_attachment_filename(raw_name) or "attachment_{}".format(
214+
(sha256 or "unknown")[:16]
215+
)
216+
217+
196218
def parseEml(filepath, job_directory, wkhtmltoimage, sanitized_rendering):
197219
ep = eml_parser.EmlParser(include_raw_body=True, include_attachment_data=True)
198220
with open(filepath, "rb") as f:
@@ -322,16 +344,17 @@ def parseEml(filepath, job_directory, wkhtmltoimage, sanitized_rendering):
322344
for a in decoded_email.get("attachment"):
323345
a["mime"] = magic.from_buffer(binascii.a2b_base64(a.get("raw")))
324346
if isinstance(a.get("raw"), bytes):
325-
path, filename = os.path.split(a.get("filename", ""))
326-
if path != "":
327-
os.umask(0)
328-
os.makedirs(
329-
f"{job_directory}/output/{path}", exist_ok=True, mode=0o777
330-
)
331-
filepath = os.path.join(job_directory, "output", path, filename)
347+
filename = _attachment_output_name(
348+
a.get("filename", ""), (a.get("hash") or {}).get("sha256")
349+
)
350+
out_dir = os.path.join(job_directory, "output")
351+
filepath = os.path.join(out_dir, filename)
352+
if not os.path.realpath(filepath).startswith(
353+
os.path.realpath(out_dir) + os.sep
354+
):
355+
raise ValueError("attachment path escapes sandbox")
332356
with open(filepath, "wb") as f:
333357
f.write(base64.b64decode(a["raw"]))
334-
f.close()
335358
a["raw"] = a.get("raw").decode("ascii")
336359
result["attachments"].append(a)
337360
iocs["hash"].append(

0 commit comments

Comments
 (0)