Detection & Response Case Study: AppleJeus

 

This document is based on the analysis from our friends, the GReAT team at Kaspersky Lab, here: https://securelist.com/operation-applejeus/87553/

We will step you through a sample approach to reading through a report like this and how we turn an interesting read into automated detection using LimaCharlie.io.

The IOCs

First thing we are always trying to do is optimize our ROI. In this case, using the basic IoC provided by the authors takes no time, so let's do that. Usually listed at the very end of the report is the information we are looking for.

File path

  • C:\Recovery\msn.exe
  • C:\Recovery\msndll.log
  • C:\Windows\msn.exe
  • C:\WINDOWS\system32\uploadmgrsvc.dll
  • C:\WINDOWS\system32\uploadmgr.dat

Domains and IPs

  • www.celasllc[.]com/checkupdate.php
  • 196.38.48.121
  • 185.142.236.226
  • 80.82.64.91
  • 185.142.239.173

These looks fairly unique. It is important to at least give it a sanity check as some less detail-oriented reports often list as IoC files and domains that are legitimate and common.

Let's move ahead and turn those into simple D&R rules.

To do this, we break down the files and map them into observables. For example a .exe is likely observable as a NEW_PROCESS, while a .dll is likely going to be observed by default as a CODE_IDENTITY.

The other files however are not likely going to be reported by default since they are neither executable code (like .exe or .dll) nor documents like a .pdf (which would be reported as a NEW_DOCUMENT). We could use them as part of an active spot-check on our organization, but we'll keep that in our back pocket for later.

The IPs and domain should be observable in NEW_TCP4_CONNECTION and NETWORK_SUMMARY, and DNS_REQUEST events respectively.

D&R Rules Theory

Creating D&R rules allow us to detect and report these observables in real-time as they occur on the agents. This has the advantage of accelerating detection and mitigation, but the disadvantage of not being able to see things that are not producing events, like a process that has executed in the past.

There are three main elements to simple rules:

  1. op: is windows will tell the rule to only apply to Windows systems.

  2. events: XXXX will tell the rule it should only apply to certain event types.

  3. path: XXXX, value: XXXX, op: XXXX will describe a simple comparison of the element at path in the event and the specific value compared with op. If op is is then the value is compared for equality for example.


To understand the path listed in the rules, compare them to the JSON event in question. The path is simply the name of each level, starting at the root, that must be followed to the appropriate value. The special character * means zero-or-any-number-of-levels while the ? means one-level-of-any-name.

For example given this event (shortened for demonstration purposes):

{
  "routing": {
    "hostname": "MacBook-Pro.local",
    "event_type": "NEW_PROCESS",
    "event_id": "26bbee42-ac21-475b-b652-ad603f6aa7ad",
    "oid": "c82e5d18-d519-4ef5-a4ac-c454a95d31ca",
    "int_ip": "192.168.1.72",
    "ext_ip": "186.147.235.76",
    "sid": "09530d35-2df9-4fd2-845e-80a0c06efaa3",
    "event_time": 1536260514146
  },
  "event": {
    "USER_ID": 501,
    "PARENT": {
      "USER_ID": 0,
      "COMMAND_LINE": "/sbin/launchd",
      "PROCESS_ID": 1,
      "USER_NAME": "root",
      "FILE_PATH": "/sbin/launchd",
      "PARENT_PROCESS_ID": 0
    },
    "COMMAND_LINE": "/System/Library/Frameworks/QuickLook.framework/Resources/quicklookd.app/Contents/MacOS/quicklookd",
    "PROCESS_ID": 30113,
    "USER_NAME": "some_user",
    "FILE_PATH": "/System/Library/Frameworks/QuickLook.framework/Versions/A/Resources/quicklookd.app/Contents/MacOS/quicklookd",
    "PARENT_PROCESS_ID": 1
  }
}

The path event/?/USER_NAME would result in root.

Resulting D&R Rules for the IOCs

NEW_PROCESS

# If both sub-expression 1 AND 2 match.
op: and
rules:
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is either a
  # NEW_PROCESS or EXISTING_PROCESS.
  - op: is windows
    events:
      - NEW_PROCESS
      - EXISTING_PROCESS
  # Sub-expression 2
  # ==================
  # If either sub-expression 2.1 OR 2.2 match.
  - op: or
    rules:
      # Sub-expression 2.1
      # ==================
      # The file path is ***, case incensitive
      - op: is
        path: event/FILE_PATH
        value: C:\Recovery\msn.exe
        case sensitive: false
      # Sub-expression 2.2
      # ==================
      # The file path is ***, case incensitive
      - op: is
        path: event/FILE_PATH
        value: C:\Windows\msn.exe
        case sensitive: false

CODE_IDENTITY

# If both sub-expression 1 AND 2 match.
op: and
rules:
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is a CODE_IDENTITY.
  - op: is windows
    event: CODE_IDENTITY
  # Sub-expression 2
  # ==================
  # If either sub-expression 2.1 OR 2.2 OR 2.3 match.
  - op: or
    rules:
      # Sub-expression 2.1
      # ==================
      # The file path is ***, case incensitive
      - op: is
        path: event/FILE_PATH
        value: C:\Recovery\msn.exe
        case sensitive: false
      # Sub-expression 2.2
      # ==================
      # The file path is ***, case incensitive
      - op: is
        path: event/FILE_PATH
        value: C:\Windows\msn.exe
        case sensitive: false
      # Sub-expression 2.3
      # ==================
      # The file path is ***, case incensitive
      - op: is
        path: event/FILE_PATH
        value: C:\WINDOWS\system32\uploadmgrsvc.dll
        case sensitive: false

DNS_REQUEST

# If both sub-expression 1 AND 2 match.
op: and
rules:
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is a DNS_REQUEST.
  - op: is windows
    event: DNS_REQUEST
  # Sub-expression 2
  # ==================
  # The domain name is ***.
  - op: is
    path: event/DOMAIN_NAME
    value: www.celasllc.com
    case sensitive: false

NEW_TCP4_CONNECTION and NETWORK_SUMMARY

# If both sub-expression 1 AND 2 match.
op: and
rules:
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is either a
  # NEW_TCP_4_CONNECTION or a NETWORK_SUMMARY.
  - op: is windows
    events:
      - NEW_TCP4_CONNECTION
      - NETWORK_SUMMARY
  # Sub-expression 2
  # ==================
  # If either sub-expression 2.1 OR 2.2 match.
  - op: or
    rules:
      # Sub-expression 2.1
      # ==================
      # The source or destination (NEW_TCP_4_CONNECTION) is one of
      # those IPs.
      - op: matches
        path: event/?/IP_ADDRESS
        re: (196\.38\.48\.121|185\.142\.236\.226|80\.82\.64\.91|185\.142\.239\.173)
      # Sub-expression 2.2
      # ==================
      # The source or destination of one of the connections (NETWORK_SUMMARY) 
      # is one of those IPs.
      - op: matches
        path: event/PROCESS/NETWORK_ACTIVITY/?/IP_ADDRESS
        re: ^(196\.38\.48\.121|185\.142\.236\.226|80\.82\.64\.91|185\.142\.239\.173)$

Responding with D&R Rules

These rules were for the Detection (the part that indicates what a rule matches) part of the D&R rules, now we need to specify a Response (the part that specifies what to do when the Detection matches) for them.

In this case, until we build confidence that there will truly not be any false positives, we will simply Report the detections:

# This is just a list of actions to take.
# Report will generate a detection (that will be forwaded
# wherever you specified) that we name simply "jeus"
- action: report
  name: jeus

With these in place, we can be confident that if the implant, as descibed in the report executes on any of our machines, we'll be notified right away.

Hashes and VirusTotal

You may notice we have not mentioned hashes. The report does not contain any SHA256 hashes (the hashing used primarily by LimaCharlie.io), but beyond that, we rely on VirusTotal to provide this secondary signal.

As a user of LimaCharlie.io, if you configure your VirusTotal API Key, LimaCharlie.io will give you access to it as a Detection component (you can refer to the API as an operator). LimaCharlie.io will even perform caching of results for you.

This means that if we have a general D&R rule for VirusTotal we expect to be notified anyway if any executable on our machines matches with something in VT. This is an example general rule for VT that reports any executable flagged as malicious by two or more Anti Viruses:

# The "lookup" operator simply says to compare the value in "path" 
# with *something*.
op: lookup
# We specify we want to compare the CODE_IDENITY event's HASH.
event: CODE_IDENTITY
path: event/HASH
# And we want to compare the hash with a LimaCharlie Resource 
# (these can be APIs or threat feeds) called "api/vt" (the VirusTotal
# API resource).
resource: 'lcr://api/vt'
# This VT API returns metadata about which AntiVirus flagged the hash. We
# use the "metadata_rules" to apply further logic to the VT metadata.
metadata_rules:
  # If if length of the list containing AV products claiming the hash
  # to be malicious is greater than 1, we will return a positive match.
  # So if that list is of two or more items, we'll match.
  op: is greater than
  value: 1
  path: /
  # This indicates we do not want to compare the path "/" of the metadata
  # itself, but rather the length of the JSON element at that path.
  length of: true

The Spot Checks

Now that we have covered data that available by default, we could think about doing spot checks on our machines. This may or may not be worth it for you. If your organization is not usually the type of organization targeted by this APT, chances are that only doing "passive" checks as above is enough.

For the sake of this discussion however we will assume you do want to go the extra mile.

Here is what we can check for:

Yara Signatures

See jeus.yara at the end of this doc.

  • RC4 Key
  • PDB
  • Domain Name
  • User Agent
  • Registry Key
  • File Prefix

File Presence

  • C:\Windows\system32\uploadmgr*
  • msndll.dat
  • msndll.tmp
  • msncf.dat
  • C:\Recovery*.exe

Running SpotChecks

The following is a single SpotCheck for all the IOCs above. Note that it requires the limacharlie Python API version 2.0.0 or above, pip install limacharlie --upgrade should do it.

python -m limacharlie.SpotCheck --no-linux --no-macos --n-concurrent 3 --yara ./jeus.yara --file-pattern c:\\windows\\system32\\ "uploadmgr*" 0 --file-pattern c:\\ "msndll*" 2 --file-pattern c:\\recovery\\ "*.exe" 0

Let's analyze a bit this command line:

  • python -m limacharlie.SpotCheck simply instantiates the SpotCheck CLI tool within the Python API.
  • --no-linux --no-macos specifies that we only intend to scan our Windows hosts.
  • --n-concurrent 3 says the SpotCheck tool should scan 3 host at a time.
  • --yara ./jeus.yara specifies we want to do a system-wide scan (all files AND memory) using the Yara signature in this file (provided as attachment below).
  • --file-pattern c:\\windows\\system32\\ "uploadmgr*" 0 says we want to look for files in the c:\windows\system32\ (NOT subdirectories, the 0 is the max level of subdirectories) who's name begins with uploadmgr.
  • --file-pattern c:\\ "msndll*" 2 says we want to look for files who's name begins with msndll anywhere starting at the c:\ directory and up to two levels of subdirectories down.
  • --file-pattern c:\\recovery\\ "*.exe" 0 finally says we want to look for any .exe files in c:\recovery (no subdirectories).

As soon as we begin running this tool, it will begin scanning the hosts in our organization (via the REST interface) for those IOCs.

All matches and activity will be reported to STDOUT line by line:

  • A line starting with . (UUID): indicates the host with this agent ID (represented here as UUID) is done being scanned.
  • A line starting with ? (UUID): indicates that the host in question matches the type of hosts to scan, but it is currently offline, the SpotCheck tool will keep trying to reach it.
  • A line starting with X (UUID): some-error-information indicates that the check of this host did not finish because of an error.
  • A line starting with ! (UUID): {some-metadata} indicates that one of our IOCs has been found on the host. The exact metadata will depend on the type of IOC found.

Here is an example of a YARA signature match:

! (09530d37-2df9-4fd2-845e-80c0c06afaa3): {"yara": {"PROCESS": {"USER_ID": 0, "COMMAND_LINE": "/Library/Handsoff/HandsOffDaemon", "PROCESS_ID": 110, "USER_NAME": "root", "FILE_PATH": "/Library/Handsoff/HandsOffDaemon", "PARENT_PROCESS_ID": 1}, "RULE_NAME": "APT_FallChill_RC4_Keys", "PROCESS_ID": 110}}

Once the scan is finished, the SpotCheck tool will exit.

Yara Signature

rule APT_FallChill_RC4_Keys_and_files {
    meta:
        author = "Florian Roth"
        description = "Detect FallChill RC4 Keys, modified by Maxime Lamothe-Brassard"
        reference = "https://securelist.com/operation-applejeus/87553/"
        date = "2018-08-21"
    strings:
        $rc4_code = { C7 ?? ?? DA E1 61 FF
                      C7 ?? ?? 0C 27 95 87
                      C7 ?? ?? 17 57 A4 D6
                      C7 ?? ?? EA E3 82 2B }
        $pdb1 = "Z:\\jeus\\" nocase
        $pdb2 = "H:\\DEV\\TManager\\" nocase
        $domain1 = "www.celasllc.com" nocase
        $useragent1 = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)" nocase
        $reg1 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\TaskConfigs\\Description" nocase
        $fileprefix1 = "\\uploadmgr" nocase
    condition:
        uint16(0) == 0x5a4d and 1 of them
}

 
 
blog2.png