Implement oletools to filter out bad macros

main
Florent Daigniere 2 years ago
parent 9c6e9b05db
commit 3e45a791cf

@ -394,7 +394,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "oletools", "postfix", "dovecot", "unbound", "nginx"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Retrieve global variables - name: Retrieve global variables
@ -439,7 +439,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "oletools", "postfix", "dovecot", "unbound", "nginx"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Retrieve global variables - name: Retrieve global variables

@ -46,6 +46,10 @@ watchdog
# core/postfix # core/postfix
postfix-mta-sts-resolver postfix-mta-sts-resolver
# core/oletools
python-magic
oletools
# optional/fetchmail # optional/fetchmail
requests requests

@ -42,6 +42,7 @@ marshmallow==3.18.0
marshmallow-sqlalchemy==0.28.1 marshmallow-sqlalchemy==0.28.1
multidict==6.0.2 multidict==6.0.2
mysql-connector-python==8.0.31 mysql-connector-python==8.0.31
oletools==0.60.1
packaging==21.3 packaging==21.3
passlib==1.7.4 passlib==1.7.4
podop @ file:///app/libs/podop podop @ file:///app/libs/podop
@ -52,7 +53,9 @@ pycares==4.2.2
pycparser==2.21 pycparser==2.21
Pygments==2.13.0 Pygments==2.13.0
pyOpenSSL==22.1.0 pyOpenSSL==22.1.0
pyparsing==3.0.9 pyparsing==2.4.7
python-dateutil==2.8.2
python-magic==0.4.27
python-dateutil==2.8.2 python-dateutil==2.8.2
pytz==2022.6 pytz==2022.6
PyYAML==6.0 PyYAML==6.0

@ -0,0 +1,31 @@
# syntax=docker/dockerfile-upstream:1.4.3
# oletools image
FROM base
ARG VERSION=local
LABEL version=$VERSION
RUN set -euxo pipefail \
; apk add --no-cache netcat-openbsd libmagic libffi \
; curl -sLo olefy.py https://raw.githubusercontent.com/HeinleinSupport/olefy/f8aac6cc55283886d153e89c8f27fae66b1c24e2/olefy.py \
; chmod 755 olefy.py
RUN echo $VERSION >/version
HEALTHCHECK --start-period=60s CMD echo PING|nc -q1 127.0.0.1 11343|grep "PONG"
EXPOSE 11343/tcp
USER nobody:nobody
ENV \
OLEFY_BINDADDRESS="0.0.0.0" \
OLEFY_BINDPORT="11343" \
OLEFY_OLEVBA_PATH="/app/venv/bin/olevba" \
OLEFY_PYTHON_PATH="/app/venv/bin/python3" \
OLEFY_TMPDIR="/dev/shm/" \
OLEFY_MINLENGTH="300" \
OLEFY_DEL_TMP="1" \
OLEFY_DEL_TMP_FAILED="1"
CMD /app/olefy.py

@ -0,0 +1,12 @@
OLETOOLS_MACRO_MRAPTOR {
expression = "(OLETOOLS_A & OLETOOLS_W) | (OLETOOLS_A & OLETOOLS_X) | (OLETOOLS_W & OLETOOLS_X)";
message = "Rejected (malicious macro - mraptor)";
policy = "leave";
score = 20.0;
}
OLETOOLS_MACRO_SUSPICIOUS {
expression = "OLETOOLS & OLETOOLS_SUSPICIOUS";
message = "Rejected (malicious macro)";
policy = "leave";
score = 20.0;
}

@ -0,0 +1,61 @@
oletools {
# default olefy settings
servers = "{{ OLETOOLS_ADDRESS }}"
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
patterns {
OLETOOLS_MACRO = '^.....M..$';
OLETOOLS_AUTOEXEC = '^A....M..$';
OLETOOLS_SUSPICIOUS = '^.....MS.$';
# see https://github.com/decalage2/oletools/blob/master/oletools/mraptor.py
OLETOOLS_A = '(?i)\b(?:Auto(?:Exec|_?Open|_?Close|Exit|New)|Document(?:_?Open|_Close|_?BeforeClose|Change|_New)|NewDocument|Workbook(?:_Open|_Activate|_Close|_BeforeClose)|\w+_(?:Painted|Painting|GotFocus|LostFocus|MouseHover|Layout|Click|Change|Resize|BeforeNavigate2|BeforeScriptExecute|DocumentComplete|DownloadBegin|DownloadComplete|FileDownload|NavigateComplete2|NavigateError|ProgressChange|PropertyChange|SetSecureLockIcon|StatusTextChange|TitleChange|MouseMove|MouseEnter|MouseLeave|OnConnecting))|Auto_Ope\b';
OLETOOLS_W = '(?i)\b(?:FileCopy|CopyFile|Kill|CreateTextFile|VirtualAlloc|RtlMoveMemory|URLDownloadToFileA?|AltStartupPath|WriteProcessMemory|ADODB\.Stream|WriteText|SaveToFile|SaveAs|SaveAsRTF|FileSaveAs|MkDir|RmDir|SaveSetting|SetAttr)\b|(?:\bOpen\b[^\n]+\b(?:Write|Append|Binary|Output|Random)\b)';
OLETOOLS_X = '(?i)\b(?:Shell|CreateObject|GetObject|SendKeys|RUN|CALL|MacScript|FollowHyperlink|CreateThread|ShellExecuteA?|ExecuteExcel4Macro|EXEC|REGISTER|SetTimer)\b|(?:\bDeclare\b[^\n]+\bLib\b)';
}
# mime-part regex matching in content-type or filename
mime_parts_filter_regex {
#UNKNOWN = "application\/octet-stream";
DOC2 = "application\/msword";
DOC3 = "application\/vnd\.ms-word.*";
XLS = "application\/vnd\.ms-excel.*";
PPT = "application\/vnd\.ms-powerpoint.*";
GENERIC = "application\/vnd\.openxmlformats-officedocument.*";
}
# mime-part filename extension matching (no regex)
mime_parts_filter_ext {
doc = "doc";
dot = "dot";
docx = "docx";
dotx = "dotx";
docm = "docm";
dotm = "dotm";
xls = "xls";
xlt = "xlt";
xla = "xla";
xlsx = "xlsx";
xltx = "xltx";
xlsm = "xlsm";
xltm = "xltm";
xlam = "xlam";
xlsb = "xlsb";
ppt = "ppt";
pot = "pot";
pps = "pps";
ppa = "ppa";
pptx = "pptx";
potx = "potx";
ppsx = "ppsx";
ppam = "ppam";
pptm = "pptm";
potm = "potm";
ppsm = "ppsm";
slk = "slk";
}
}

@ -14,6 +14,7 @@ log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING"))
os.environ["REDIS_ADDRESS"] = system.get_host_address_from_environment("REDIS", "redis") os.environ["REDIS_ADDRESS"] = system.get_host_address_from_environment("REDIS", "redis")
os.environ["ADMIN_ADDRESS"] = system.get_host_address_from_environment("ADMIN", "admin") os.environ["ADMIN_ADDRESS"] = system.get_host_address_from_environment("ADMIN", "admin")
os.environ["OLETOOLS_ADDRESS"] = system.get_host_address_from_environment("OLETOOLS", "oletools:11343")
if os.environ.get("ANTIVIRUS") == 'clamav': if os.environ.get("ANTIVIRUS") == 'clamav':
os.environ["ANTIVIRUS_ADDRESS"] = system.get_host_address_from_environment("ANTIVIRUS", "antivirus:3310") os.environ["ANTIVIRUS_ADDRESS"] = system.get_host_address_from_environment("ANTIVIRUS", "antivirus:3310")

@ -103,16 +103,33 @@ services:
- {{ dns }} - {{ dns }}
{% endif %} {% endif %}
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-{{ version }}}
hostname: oletools
restart: always
networks:
- noinet
depends_on:
{% if resolver_enabled %}
- resolver
dns:
- {{ dns }}
{% endif %}
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-{{ version }}} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-{{ version }}}
hostname: antispam hostname: antispam
restart: always restart: always
env_file: {{ env }} env_file: {{ env }}
networks:
- default
- noinet
volumes: volumes:
- "{{ root }}/filter:/var/lib/rspamd" - "{{ root }}/filter:/var/lib/rspamd"
- "{{ root }}/overrides/rspamd:/etc/rspamd/override.d:ro" - "{{ root }}/overrides/rspamd:/etc/rspamd/override.d:ro"
depends_on: depends_on:
- front - front
- oletools
{% if resolver_enabled %} {% if resolver_enabled %}
- resolver - resolver
dns: dns:
@ -199,3 +216,6 @@ networks:
{% if ipv6_enabled %} {% if ipv6_enabled %}
- subnet: {{ subnet6 }} - subnet: {{ subnet6 }}
{% endif %} {% endif %}
noinet:
driver: bridge
internal: true

@ -34,6 +34,7 @@ group "default" {
"antispam", "antispam",
"front", "front",
"imap", "imap",
"oletools",
"smtp", "smtp",
"webmail", "webmail",
@ -152,6 +153,15 @@ target "front" {
tags = tag("nginx") tags = tag("nginx")
} }
target "oletools" {
inherits = ["defaults"]
context = "core/oletools/"
contexts = {
base = "target:base"
}
tags = tag("oletools")
}
target "imap" { target "imap" {
inherits = ["defaults"] inherits = ["defaults"]
context = "core/dovecot/" context = "core/dovecot/"

@ -65,10 +65,20 @@ services:
depends_on: depends_on:
- front - front
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
hostname: oletools
restart: always
networks:
- noinet
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
restart: always restart: always
env_file: mailu.env env_file: mailu.env
networks:
- default
- noinet
volumes: volumes:
- "/mailu/filter:/var/lib/rspamd" - "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim" - "/mailu/dkim:/dkim"
@ -96,3 +106,6 @@ networks:
driver: default driver: default
config: config:
- subnet: 192.168.203.0/24 - subnet: 192.168.203.0/24
noinet:
driver: bridge
internal: true

@ -65,10 +65,20 @@ services:
depends_on: depends_on:
- front - front
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
hostname: oletools
restart: always
networks:
- noinet
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
restart: always restart: always
env_file: mailu.env env_file: mailu.env
networks:
- default
- noinet
volumes: volumes:
- "/mailu/filter:/var/lib/rspamd" - "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim" - "/mailu/dkim:/dkim"
@ -103,3 +113,6 @@ networks:
driver: default driver: default
config: config:
- subnet: 192.168.203.0/24 - subnet: 192.168.203.0/24
noinet:
driver: bridge
internal: true

@ -65,10 +65,20 @@ services:
depends_on: depends_on:
- front - front
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
hostname: oletools
restart: always
networks:
- noinet
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
restart: always restart: always
env_file: mailu.env env_file: mailu.env
networks:
- default
- noinet
volumes: volumes:
- "/mailu/filter:/var/lib/rspamd" - "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim" - "/mailu/dkim:/dkim"
@ -102,3 +112,6 @@ networks:
driver: default driver: default
config: config:
- subnet: 192.168.203.0/24 - subnet: 192.168.203.0/24
noinet:
driver: bridge
internal: true

@ -65,10 +65,20 @@ services:
depends_on: depends_on:
- front - front
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
hostname: oletools
restart: always
networks:
- noinet
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
restart: always restart: always
env_file: mailu.env env_file: mailu.env
networks:
- default
- noinet
volumes: volumes:
- "/mailu/filter:/var/lib/rspamd" - "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim" - "/mailu/dkim:/dkim"
@ -103,3 +113,6 @@ networks:
driver: default driver: default
config: config:
- subnet: 192.168.203.0/24 - subnet: 192.168.203.0/24
noinet:
driver: bridge
internal: true

@ -65,10 +65,20 @@ services:
depends_on: depends_on:
- front - front
oletools:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-local}
hostname: oletools
restart: always
networks:
- noinet
antispam: antispam:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local}
restart: always restart: always
env_file: mailu.env env_file: mailu.env
networks:
- default
- noinet
volumes: volumes:
- "/mailu/filter:/var/lib/rspamd" - "/mailu/filter:/var/lib/rspamd"
- "/mailu/dkim:/dkim" - "/mailu/dkim:/dkim"
@ -104,3 +114,6 @@ networks:
driver: default driver: default
config: config:
- subnet: 192.168.203.0/24 - subnet: 192.168.203.0/24
noinet:
driver: bridge
internal: true

@ -0,0 +1 @@
Implement OLETools and block bad macros in office documents
Loading…
Cancel
Save