saferwall/saferwall

Windows Defender latest version is crashing - "Clean"

prsyahmi opened this issue · 4 comments

Latest Windows defender engine is crashing due to some new import not being handled by loadlibrary

I'm merely notifying in case people don't notice this since Saferwall UI show "Clean" and doesn't return error if something happen during scan.

There is a workaround by using wine + mploader while we waiting loadlibrary gets updated, hopefully. In case people interested:

Build mploader

mploader need to be compiled statically

put mploader.exe & conf dir inside mploader dir at saferwall source dir

saferwall/mploader/mploader.exe
saferwall/mploader/conf/exp_info.json
saferwall/mploader/conf/ignore_apis.txt
saferwall/mploader/conf/mploader.conf

On plus side, using mploader.exe will show results that are not reported by loadlibrary's mpclient before.

build/docker/Dockerfile.gowindefenderwine

################################
# STEP 1 build executable binary
################################

FROM golang:1.17-alpine AS build-stage

ENV VENDOR windefenderwine

# Install git + SSL ca certificates.
# Git is required for fetching the dependencies.
# Ca-certificates is required to call HTTPS endpoints.
RUN apk update && apk add --no-cache git ca-certificates tzdata \
	&& update-ca-certificates 2>/dev/null || true

# Set the Current Working Directory inside the container.
WORKDIR $GOPATH/src/saferwall/$VENDOR/

# Copy go mod and sum files.
COPY go.mod go.sum ./

# Download all dependencies. Dependencies will be cached if the go.mod
# and go.sum files are not changed.
RUN go mod download

# Copy our go files.
COPY . .

# Build the binary.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
	go build -a -installsuffix cgo -ldflags '-extldflags "-static"' \
	-o /go/bin/$VENDOR-svc cmd/services/multiav/$VENDOR/main.go

############################
# STEP 2 build a small image
############################

FROM scottyhardy/docker-wine:latest
LABEL maintainer=""
LABEL version="0.0.1"
LABEL description="windows defender port + wine into linux with nsq consumer"

# Environment variables.
ENV WINDOWS_DEFENDER_DB_UPDATE_DATE	/av_db_update_date.txt
ENV WINDOWS_DEFENDER_INSTALL_DIR /opt/windowsdefender
ENV WINDOWS_DEFENDER_UPDATE	"https://go.microsoft.com/fwlink/?LinkID=121721&arch=x86"

# Set the Current Working Directory inside the container.
WORKDIR /saferwall

RUN apt-get install cabextract

RUN mkdir -p $WINDOWS_DEFENDER_INSTALL_DIR/engine
COPY mploader/mploader.exe $WINDOWS_DEFENDER_INSTALL_DIR/mploader.exe
COPY mploader/conf $WINDOWS_DEFENDER_INSTALL_DIR/conf

# Update virus definition file.
RUN wget -U "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0" -O $WINDOWS_DEFENDER_INSTALL_DIR/engine/mpam-fe.exe $WINDOWS_DEFENDER_UPDATE

RUN cd $WINDOWS_DEFENDER_INSTALL_DIR/engine \
	&& cabextract mpam-fe.exe \
	&& rm mpam-fe.exe \
	&& echo -n "$(date +%s)" >> $WINDOWS_DEFENDER_DB_UPDATE_DATE

# Create an app user so our program doesn't run as root.
RUN groupadd -r saferwall \
	&& useradd --no-log-init --create-home -r -g saferwall saferwall

# Copy our static executable.
COPY --from=build-stage /go/bin/windefenderwine-svc .

# Copy the config files.
COPY configs/services/multiav/windefender conf/

# Update permissions.
#RUN usermod -u 101 saferwall \
#	&& groupmod -g 102 saferwall \
#	&& chown -R saferwall:saferwall . \
#	&& chown -R saferwall:saferwall $WINDOWS_DEFENDER_INSTALL_DIR
RUN chown -R saferwall:saferwall . \
	&& chown -R saferwall:saferwall $WINDOWS_DEFENDER_INSTALL_DIR

# Switch to our user.
USER saferwall

ENTRYPOINT ["/saferwall/windefenderwine-svc", "-config", "/saferwall/conf"]

cmd/services/multiav/windefenderwine/main.go

// Copyright 2021` Saferwall. All rights reserved.
// Use of this source code is governed by Apache v2 license
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"flag"
	"os"

	"github.com/saferwall/saferwall/internal/config"
	"github.com/saferwall/saferwall/internal/log"
	"github.com/saferwall/saferwall/internal/multiav/windefenderwine"
	"github.com/saferwall/saferwall/services/multiav"
)

// Version indicates the current version of the application.
var Version = "1.0.0"

var flagConfig = flag.String(
	"config", "./../../../../configs/services/multiav/windefender",
	"path to the config file")

func main() {

	flag.Parse()

	// Create root logger tagged with server version.
	logger := log.New().With(context.TODO(), "version", Version)
	if err := run(logger); err != nil {
		logger.Errorf("failed to run the server: %s", err)
		os.Exit(-1)
	}
}

func run(logger log.Logger) error {

	c := multiav.Config{}

	env := os.Getenv("SAFERWALL_DEPLOYMENT_KIND")

	logger.Infof("loading %s configuration from %s", env, *flagConfig)

	err := config.Load(*flagConfig, env, &c)
	if err != nil {
		return err
	}

	scanner := windefenderwine.Scanner{}
	logger = log.NewCustom(c.LogLevel).With(context.TODO(), "version", Version)
	s, err := multiav.New(c, logger, scanner)
	if err != nil {
		return err
	}

	s.Start()
	return nil
}

internal/multiav/windefenderwine/windefenderwine.go

// Copyright 2022 Saferwall. All rights reserved.
// Use of this source code is governed by Apache v2 license
// license that can be found in the LICENSE file.

package windefenderwine

import (
	"os"
	"path"
	"strings"

	multiav "github.com/saferwall/saferwall/internal/multiav"
	"github.com/saferwall/saferwall/internal/utils"
)

// Our consts
const (
	loadlibraryPath = "/opt/windowsdefender/"
	mpclient        = "./mploader.exe"
	mpenginedll     = "/engine/mpengine.dll"
)

// Scanner represents an empty struct that can be used to a method received.
type Scanner struct{}

// GetVersion returns update version.
func GetVersion() (string, error) {
	mpenginedll := path.Join(loadlibraryPath, mpenginedll)
	out, err := utils.ExecCmd("exiftool", "-ProductVersion",
		mpenginedll)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(strings.Split(out, ":")[1]), nil
}

// ScanFile a file with Windows Defender scanner.
func (Scanner) ScanFile(filePath string) (multiav.Result, error) {

	var err error
	res := multiav.Result{}

	// get current working directory
	dir, err := utils.Getwd()
	if err != nil {
		return res, err
	}

	// mpclient requires us to run from loadlibrary folder or it fails
	if err := os.Chdir(loadlibraryPath); err != nil {
		return res, err
	}
	defer os.Chdir(dir)

	// Execute the scanner with the given file path
	// main(): usage: ./mpclient [filenames...]
	res.Out, err = utils.ExecCmd("wine", mpclient, "-f", filePath, "-u")
	if err != nil {
		return res, err
	}

	// main(): Scanning /samples/locky...
	// EngineScanCallback(): Scanning input
	// EngineScanCallback(): Threat Ransom:Win32/Locky.A identified.
	lines := strings.Split(res.Out, "\n")
	for _, line := range lines {
		if !strings.Contains(line, "Threat ") {
			continue
		}
		if strings.Contains(line, "No Threat identified ") {
			continue
		}

		detection := strings.TrimPrefix(line, "Threat ")
		res.Output = strings.TrimSuffix(detection, " identified.\r")
		res.Infected = true
		break
	}

	return res, nil
}

Create docker image?

docker build -t saferwall/windefenderwine -f build/docker/Dockerfile.gowindefenderwine .

Hello @prsyahmi

Thanks a lot for reporting this. Totally worth it, I will make a PR today.

Much appreciated for the detailed issue and code suggestion.

You can pull the last container of gowindefender, try it and let me know if it works for you.

Hi @LordNoteworthy , Thanks I'll definitely give it a try later

Hi @LordNoteworthy, tested and works perfectly. Thanks!