spender-sandbox / cuckoo-modified

Modified edition of cuckoo
393 stars 178 forks source link

docm in pdf from latest dridex waves isn't getting the PDF - open docm file ok UI box clicked. #437

Open mallorybobalice opened 7 years ago

mallorybobalice commented 7 years ago

hello,

we're running a rather old version - about 9m old now docm in pdf from latest dridex waves isn't getting the PDF - open docm file ok UI box clicked.

is this a known issue and or fixed can this be fixed in human py or in another way if not?

https://www.virustotal.com/en/file/46b063f2915eae4c7a7fe5e2142d5e248caf65113590610d04c172b211502131/analysis/

017-04-11 12:14:08,795 [root] DEBUG: Started auxiliary module Disguise
2017-04-11 12:14:08,795 [root] DEBUG: Started auxiliary module Human
2017-04-11 12:14:08,795 [root] DEBUG: Started auxiliary module Screenshots
2017-04-11 12:14:08,795 [root] DEBUG: Started auxiliary module Usage
2017-04-11 12:14:08,888 [lib.api.process] INFO: Successfully executed process from path "C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe" with arguments ""xxx.pdf"" with pid 1880
2017-04-11 12:14:08,888 [lib.api.process] DEBUG: Using QueueUserAPC injection.
2017-04-11 12:14:08,967 [lib.api.process] INFO: Injected into suspended 32-bit process with pid 1880
2017-04-11 12:14:10,979 [lib.api.process] INFO: Successfully resumed process with pid 1880
2017-04-11 12:14:10,979 [root] INFO: Added new process to list with pid: 1880
2017-04-11 12:14:11,026 [root] INFO: Cuckoomon successfully loaded in process with pid 1880.
2017-04-11 12:14:11,618 [root] INFO: Disabling sleep skipping.
2017-04-11 12:14:11,618 [root] INFO: Disabling sleep skipping.
2017-04-11 12:14:17,144 [modules.auxiliary.human] INFO: Found button "&Open this file", clicking it
2017-04-11 12:14:19,227 [modules.auxiliary.human] INFO: Found button "&Open this file", clicking it
2017-04-11 12:14:21,313 [modules.auxiliary.human] INFO: Found button "&Open this file", clicking it

2017-04-11 12:16:10,388 [modules.auxiliary.human] INFO: Found button "&Open this file", clicking it
2017-04-11 12:16:12,494 [modules.auxiliary.human] INFO: Found button "&Open this file", clicking it
2017-04-11 12:16:12,729 [root] INFO: Analysis timeout hit, terminating analysis.
2017-04-11 12:16:12,729 [root] INFO: Created shutdown mutex.
2017-04-11 12:16:13,743 [root] INFO: Shutting down package.
2017-04-11 12:16:13,743 [root] INFO: Stopping auxiliary modules.
2017-04-11 12:16:14,257 [root] INFO: Finishing auxiliary modules.
2017-04-11 12:16:14,257 [root] INFO: Shutting down pipe server and dumping dropped files.
2017-04-11 12:16:14,257 [root] INFO: Analysis completed.
mallorybobalice commented 7 years ago

I see. thanks. um. well it's clearly trying to click it multiple times in mine but failing. I have no idea why though :|

mallorybobalice commented 7 years ago

actually, your log is different. mine is trying to repeatedly click the open radio button. yours is clicking OK. (correctly)

can you please copy paste your human.py mine is this:

Sep 24 2016 analyzer/windows/modules/auxiliary/human.py

#!/usr/bin/env python
# Copyright (C) 2010-2015 Cuckoo Foundation, Optiv, Inc. (brad.spengler@optiv.com)
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import random
import logging
import traceback
from threading import Thread
from ctypes import WINFUNCTYPE, POINTER
from ctypes import c_bool, c_int, create_unicode_buffer, create_string_buffer, memmove, sizeof

from lib.common.abstracts import Auxiliary
from lib.common.defines import KERNEL32, USER32
from lib.common.defines import WM_GETTEXT, WM_GETTEXTLENGTH, WM_CLOSE, BM_CLICK
from lib.common.defines import GMEM_MOVEABLE, CF_TEXT

log = logging.getLogger(__name__)

EnumWindowsProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int))
EnumChildProc = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int))

RESOLUTION = {
    "x": USER32.GetSystemMetrics(0),
    "y": USER32.GetSystemMetrics(1)
}

INITIAL_HWNDS = []

CLOSED_OFFICE = False

def foreach_child(hwnd, lparam):
    # List of buttons labels to click.
    buttons = [
        # english
        "yes",
        "ok",
        "accept",
        "next",
        "install",
        "run",
        "agree",
        "enable",
        "don't send",
        "don't save",
        "continue",
        "unzip",
        "open",
        "close the program",
        "save",
        "later",
        "finish",
        "end",
        "allow access",
        "remind me later",
        # german
        "ja",
        "weiter",
        "akzeptieren",
        "ende",
        "starten",
        "jetzt starten",
        "neustarten",
        "neu starten",
        "jetzt neu starten",
        "beenden",
        "oeffnen",
        "schliessen",
        "installation weiterfuhren",
        "fertig",
        "beenden",
        "fortsetzen",
        "fortfahren",
        "stimme zu",
        "zustimmen",
        "senden",
        "nicht senden",
        "speichern",
        "nicht speichern",
        "ausfuehren",
        "spaeter",
        "einverstanden"
    ]

    # List of buttons labels to not click.
    dontclick = [
        # english
        "check online for a solution",
        "don't run",
        "do not ask again until the next update is available",
        "cancel",
        "do not accept the agreement",
        "i would like to help make reader even better",
        # german
        "abbrechen",
        "online nach losung suchen",
        "abbruch",
        "nicht ausfuehren",
        "hilfe",
        "stimme nicht zu"
    ]

    classname = create_unicode_buffer(128)
    USER32.GetClassNameW(hwnd, classname, 128)

    # Check if the class of the child is button.
    if "button" in classname.value.lower() or classname.value == "NUIDialog" or classname.value == "bosa_sdm_msword":
        # Get the text of the button.
        length = USER32.SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
        if not length:
            return True
        text = create_unicode_buffer(length + 1)
        USER32.SendMessageW(hwnd, WM_GETTEXT, length + 1, text)
        textval = text.value.replace('&','')
        if "Microsoft" in textval and (classname.value == "NUIDialog" or classname.value == "bosa_sdm_msword"):
            log.info("Issuing keypress on Office dialog")
            USER32.SetForegroundWindow(hwnd)
            # enter key down/up
            USER32.keybd_event(0x0d, 0x1c, 0, 0)
            USER32.keybd_event(0x0d, 0x1c, 2, 0)
            return False

        # we don't want to bother clicking any non-visible child elements, as they
        # generally won't respond and will cause us to fixate on them for the
        # rest of the analysis, preventing progress with visible elements

        if not USER32.IsWindowVisible(hwnd):
            return True

        # Check if the button is set as "clickable" and click it.
        for button in buttons:
            if button in textval.lower():
                dontclickb = False
                for btn in dontclick:
                    if btn in textval.lower():
                        dontclickb = True
                if not dontclickb:
                    log.info("Found button \"%s\", clicking it" % text.value)
                    USER32.SetForegroundWindow(hwnd)
                    KERNEL32.Sleep(1000)
                    USER32.SendMessageW(hwnd, BM_CLICK, 0, 0)
                    # only stop searching when we click a button
                    return False
    return True

# Callback procedure invoked for every enumerated window.
def foreach_window(hwnd, lparam):
    # If the window is visible, enumerate its child objects, looking
    # for buttons.
    if USER32.IsWindowVisible(hwnd):
        # we also want to inspect the "parent" windows, not just the children
        foreach_child(hwnd, lparam)
        USER32.EnumChildWindows(hwnd, EnumChildProc(foreach_child), 0)
    return True

def getwindowlist(hwnd, lparam):
    global INITIAL_HWNDS
    if USER32.IsWindowVisible(hwnd):
        INITIAL_HWNDS.append(hwnd)
    return True

def move_mouse():
    x = random.randint(0, RESOLUTION["x"])
    y = random.randint(0, RESOLUTION["y"])

    # Originally was:
    # USER32.mouse_event(0x8000, x, y, 0, None)
    # Changed to SetCurorPos, since using GetCursorPos would not detect
    # the mouse events. This actually moves the cursor around which might
    # cause some unintended activity on the desktop. We might want to make
    # this featur optional.
    USER32.SetCursorPos(x, y)

def click_mouse():
    # Move mouse to top-middle position.
    USER32.SetCursorPos(RESOLUTION["x"] / 2, 0)
    # Mouse down.
    USER32.mouse_event(2, 0, 0, 0, None)
    KERNEL32.Sleep(50)
    # Mouse up.
    USER32.mouse_event(4, 0, 0, 0, None)

# Callback procedure invoked for every enumerated window.
def get_office_window(hwnd, lparam):
    global CLOSED_OFFICE
    if USER32.IsWindowVisible(hwnd):
        text = create_unicode_buffer(1024)
        USER32.GetWindowTextW(hwnd, text, 1024)
        if "- Microsoft" in text.value or "- Word" in text.value or "- Excel" in text.value or "- PowerPoint" in text.value:
            # send ALT+F4 equivalent
            log.info("Closing Office window.")
            USER32.SendNotifyMessageW(hwnd, WM_CLOSE, None, None)
            CLOSED_OFFICE = True
    return True

class Human(Auxiliary, Thread):
    """Human after all"""

    def __init__(self, options, config):
        Auxiliary.__init__(self, options, config)
        Thread.__init__(self)
        self.do_run = True

    def stop(self):
        self.do_run = False

    def run(self):
        try:
            seconds = 0
            randoff = random.randint(0, 10)

            # add some random data to the clipboard
            randchars = list("   aaaabcddeeeeeefghhhiiillmnnnooooprrrsssttttuwy")
            cliplen = random.randint(10, 1000)
            clipval = []
            for i in range(cliplen):
                clipval.append(randchars[random.randint(0, len(randchars)-1)])
            clipstr = "".join(clipval)
            cliprawstr = create_string_buffer(clipstr)
            USER32.OpenClipboard(None)
            USER32.EmptyClipboard()

            buf = KERNEL32.GlobalAlloc(GMEM_MOVEABLE, sizeof(cliprawstr))
            lockbuf = KERNEL32.GlobalLock(buf)
            memmove(lockbuf, cliprawstr, sizeof(cliprawstr))
            KERNEL32.GlobalUnlock(buf)
            USER32.SetClipboardData(CF_TEXT, buf)
            USER32.CloseClipboard()

            nohuman = self.options.get("nohuman")
            if nohuman:
                return True

            officedoc = False
            if hasattr(self.config, "file_type"):
                file_type = self.config.file_type
                file_name = self.config.file_name
                if "Rich Text Format" in file_type or "Microsoft Word" in file_type or \
                    "Microsoft Office Word" in file_type or "MIME entity" in file_type or \
                    file_name.endswith((".doc", ".docx", ".rtf", ".mht", ".mso")):
                    officedoc = True
                elif "Microsoft Office Excel" in file_type or "Microsoft Excel" in file_type or \
                    file_name.endswith((".xls", ".xlsx", ".xlsm", ".xlsb")):
                    officedoc = True
                elif "Microsoft PowerPoint" in file_type or \
                    file_name.endswith((".ppt", ".pptx", ".pps", ".ppsx", ".pptm", ".potm", ".potx", ".ppsm")):
                    officedoc = True

            USER32.EnumWindows(EnumWindowsProc(getwindowlist), 0)

            while self.do_run:
                if officedoc and (seconds % 30) == 0 and not CLOSED_OFFICE:
                    USER32.EnumWindows(EnumWindowsProc(get_office_window), 0)

                # only move the mouse 50% of the time, as malware can choose to act on an "idle" system just as it can on an "active" system
                if random.randint(0, 3) > 1:
                    click_mouse()
                    move_mouse()

                if (seconds % (15 + randoff)) == 0:
                    curwind = USER32.GetForegroundWindow()
                    other_hwnds = INITIAL_HWNDS[:]
                    try:
                        other_hwnds.remove(USER32.GetForegroundWindow())
                    except:
                        pass
                    USER32.SetForegroundWindow(other_hwnds[random.randint(0, len(other_hwnds)-1)])

                USER32.EnumWindows(EnumWindowsProc(foreach_window), 0)
                KERNEL32.Sleep(1000)
                seconds += 1
        except Exception as e:
            error_exc = traceback.format_exc()
            log.exception(error_exc)

I guess I could fix this for myself by adding 'Open this file' or is it '&Open this file' into the do not click list (except in all lower case and for all 3 buttons in https://cloud.githubusercontent.com/assets/15211234/24948835/4831d656-1f74-11e7-9cb0-8ed7235e3ff8.png

mallorybobalice commented 7 years ago

as in into

 # List of buttons labels to not click.
    dontclick = ["open this file","always allow opening files of this type","never allow opening files of this type"
....
.....

edit - yep that fixes it. human.py works in interesting ways.

mallorybobalice commented 7 years ago

@keoni161 - i think your human.py is old . my original one matches https://github.com/spender-sandbox/cuckoo-modified/blob/master/analyzer/windows/modules/auxiliary/human.py

it would be good if someone checked the suggested change into the source after vetting it as I don't know how to make a pull req/ don't have the time at the moment. Guessing Brad decided to unfortunately move on https://github.com/spender-sandbox/cuckoo-modified/commit/eb93ef3d41b8fee51b4330306dcd315d8101e021 ?