Detection & Response Case Study: AppleJeus


This document is based on the analysis from our friends, the GReAT team at Kaspersky Lab, here:

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

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

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": "",
    "ext_ip": "",
    "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",
    "COMMAND_LINE": "/System/Library/Frameworks/QuickLook.framework/Resources/",
    "PROCESS_ID": 30113,
    "USER_NAME": "some_user",
    "FILE_PATH": "/System/Library/Frameworks/QuickLook.framework/Versions/A/Resources/",

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

Resulting D&R Rules for the IOCs


# If both sub-expression 1 AND 2 match.
op: and
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is either a
  - op: is windows
  # Sub-expression 2
  # ==================
  # If either sub-expression 2.1 OR 2.2 match.
  - op: or
      # 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


# If both sub-expression 1 AND 2 match.
op: and
  # 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
      # 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


# If both sub-expression 1 AND 2 match.
op: and
  # 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
    case sensitive: false


# If both sub-expression 1 AND 2 match.
op: and
  # Sub-expression 1
  # ==================
  # The event is coming from a Windows host and is either a
  - op: is windows
  # Sub-expression 2
  # ==================
  # If either sub-expression 2.1 OR 2.2 match.
  - op: or
      # 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
        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, but beyond that, we rely on VirusTotal to provide this secondary signal.

As a user of, if you configure your VirusTotal API Key, will give you access to it as a Detection component (you can refer to the API as an operator). 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.
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.
  # 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 {
        author = "Florian Roth"
        description = "Detect FallChill RC4 Keys, modified by Maxime Lamothe-Brassard"
        reference = ""
        date = "2018-08-21"
        $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 = "" 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
        uint16(0) == 0x5a4d and 1 of them


Spot Checks


Whether you call it a Spot Check, or a Fleet Check, or any other name, we all have this time where something comes along and we need to check across our organization to determine if an IOC is present.

This sometimes comes in as a tip from a law enforcement agency, sometimes it's a report from a vendor selling threat intelligence and sometimes it's just IOCs extracted from a piece of malware during an incident.

Regardless of how it comes in, we all want a good solution for this. That's how we got to the new SpotCheck feature present in the LimaCharlie Python CLI/API.

We've spent a lot of time trying to boil down this feature to its core. It needed to be simple to use, to the point and easy to automate. This is what it looks like:

python -m limacharlie.SpotCheck --tags finance_dept --file c:\\evil.exe --file c:\\windows\\system32\\payload.dat --registry-key hklm\\software\\secret_storage --yara ./apt_43.yara

This is an example command line: you specify which hosts to check (by platform or by tag), then you specify a list of IOCs (as many as you want) to check. Press enter, the tool starts running and outputs line-based records of which agents have been checked, which have had errors and more importantly which IOCs were found.

Of course this is just the beginning, but this is the list of current IOC checks you can do:

  • File/directory recursively using name patterns.
  • File hashes recursively using name patterns.
  • Registry keys/value (Windows).
  • Yara system-wide scans (memory and files).
  • Yara files scan recursively using name patterns.
  • Yara process memory and files by process name pattern.

All of these are done via the public LimaCharlie API, so if you want to customize it for your purposes by having a look at the code for the Python API.

Over time this list will grow and we may use this as a basis for similar functionality through the web interface.


Python API 2.0.0


The LimaCharlie Python API brings many new quality of life changes.

The core of those changes is how Credentials are used. In the past, credentials had to be passed individually to API calls or to CLI components. This was a pain because it required a lot of copy and pasting of OIDs and API keys.

Now, you have 3 main ways of dealing with credentials.

You can still use individual credentials passed to API calls and CLI modules. However, all parts of the Python API now accept None as OID and API key, and if neither are present, they will fall back onto two new methods:

  1. Environment variables have been introduced:
    1. LC_OID: this is the Organization ID you would like to use.
    2. LC_API_KEY: this is the API Key you would like to use.
    3. LC_CREDS_FILE: if the LC_API_KEY is not present, this can point to a simple YAML file on disk with "oid" and "api_key" values to use.
  2. If no environment variables are present, the API will fallback to the "~/.limacharlie" file in the current user's home directory. The contents of this file are identical to the LC_CREDS_FILE mentioned above.

For operators, this method #2 tends to be the easiest since it doesn't rely on setting environment variables manually.

To facilitate this, we also introduced the follow CLI call: "python -m limacharlie login". This will prompt you for your OID and API Key and will generate the "~/.limacharlie" in the current user's home directory for you. This is by far the simplest way to get going. Afterwards, no more need to specify credentials manually.

We really hope this simple yet powerful mechanism will make your life easier as much as it did for us. For us, this solidifies the position of the Python API as more than a library to simplify automation of the REST API. We see the Python API as the official CLI for LimaCharlie, much like the "aws" and "gcloud" or "firebase" CLIs.


LC Agent 4.3.3 Changes


The LimaCharlie Agent is now available in version 4.3.3.

To upgrade your Organization, all you have to do is head to the "Sensor Downloads" section, and click the "Update to New Version" button. This will bring all of your agents up to the latest Stable version. If you notice any issues - although none are expected - you can always click the "Restore Previous Version" button to downgrade to the previous Stable version.

Now that the housekeeping is done, what's new?

  1. Quality of life bug fixes around directory listings and Yara system-wide scans.
    1. Investigation IDs are now propagated in the results of Yara scans.
    2. Directory listings now report full absolute file paths for every item, making recursive listing easier to interpret.
    3. Directory listings now interpret the file name pattern case insensitively on Windows.
  2. We've added a "reg_list" command to list the keys and values in a Windows registry on demand.
  3. We've added a "dir_find_hash" command to look for specific hashes in files given a starting directory, a file name pattern and a recursion depth.

These changes are mainly in support to the new LC Python API (2.0.0) and its new Spot Check capability to make organization-wide hunts for IOCs easier (see our other blog post dedicated to this topic).


Advanced Windows Events


LimaCharlie offers great cross-platform events. We strive to have events fired with the same meaning whether they are from a MacOS, Windows or Linux host. There is a time however where we put focus on very platform-specific events in order to facilitate in-depth detections. The following are specialized Windows events.

Remote Thread

The NEW_REMOTE_THREAD event indicates that a process created a thread remotely into another process. This is often used by malware to inject code into another process to make the malicious activity look like it's coming from a different process. In fact, we've written another blog article about it here.

Registry Operations

Windows has a unique system called the Registry. It is responsible for storing most of the configuration for the Operating System and applications installed. This makes it system of great interest for malware authors who use it to extract sensitive information or to set their malware to start covertly during Windows startup.

LimaCharlie supports 3 events, REGISTRY_CREATE, REGISTRY_DELETE and REGISTRY_WRITE. The CREATE event is generated whenever a process creates a new registry key while the DELETE is generated whenever a process deletes a registry key or value. The WRITE is generated whenever a process writes a new value to an existing key. Put together, these events give you a great insight into any registry usage by a process. Each of those events includes the unique process identifier that performed the action as well as the path to the relevant registry key.

Remote Process Handles

In Windows, a process (with the appropriate privileges) can open a Handle to another process. A Handle allows its owner to perform actions that were requested at Handle creation time. Many actions are possible, but the core ones of interest are Reading memory, Writing memory and Creating Threads.

Whenever a process creates a Handle with one of those access rights to another process, a REMOTE_PROCESS_HANDLE event is created. This event contains the unique process identifier of the creating process as well as the target process. Although this event is not an indicator of bad behavior in and of itself, it is a core part of better understanding malware behavior like lateral movement or credentials theft on Windows.

Example Usage

There has been great articles written on the subject which is linked below. Most use Sysmon as a reference event nomenclature. If you'd like to see the mapping of Sysmon events to LimaCharlie events, we have you covered here.

Serverless Endpoint Detection and Response

Asset Management in LimaCharlie


Although LimaCharlie was not meant specifically for Asset Management it is a great platform for acquiring ground truth information about assets. This information can be extremely valuable when evaluating whether a new vulnerability in a piece of software affects you, or knowing which assets a specific user accessed.

Available information includes:

Host name
Ex: database-server-1
From: contained in all events

OS Version
Ex: Windows 7 64bit
From: os_version command

Patch Level
Ex: Update for Microsoft Office 2010 (KB2597087) 64-Bit Edition)
From: os_packages command

Installed Packages
Ex: Microsoft SQL Server 2008 R2 Management Objects (x64) @ 10.50.1750.9
From: os_packages command

Ex: stevejobs @ database-server-1
From: USER_OBSERVED events


LimaCharlie can be thought of as the "primary colors" of security which can be combined and utilized in a limitless number of ways to solve specific organizational challenges. Here are some possible scenarios:

Weekly Software Inventory
A weekly cron job can be run that uses the LimaCharlie Python API to gather a list of the installed packages and versions using the os_packages command (this script as a starting point). The results get stored in a small database. Using this approach anyone in the organization can query the database for vulnerable versions of software, event licensing auditing or any other of a number of scenarios.

Incident Response with Compromised User
At some point during an Incident Response, responders can become aware that the credentials of a specific user are compromised. the likely approach is to reset the credentials of the user, but using LimaCharlie we can also:

  • know if the user logs into any other assets in case the credentials are still valid somewhere, and
  • isolate any asset the user credentials were used to log into. This is easy and can be done in about 30 seconds and is effective immediately across your organization using D&R rules:
    • Detection (if user is observed and the agent is not tagged):
op: and
  - op: is
    path: event/USER_NAME
    value: company\alice
    event: USER_OBSERVED
  - op: is tagged
    tag: compromised-isolated
    not: true
Response (isolate the computer on the network, tag the agent and alert):
- action: task
  command: segregate_network
- action: add tag
  tag: compromised-isolated
- action: report
  name: compromised-credentials-observed

All of this is just scratching the surface of what you can do with LimaCharlie using Asset Management information. Have an idea? Want help to figure out the best way to get going? Drop us a line, we'll be happy to help.

Happy hunting!


Striking a Balance Between Data & Cost


The LimaCharlie agents can generate a lot of data and by default it does not all get sent back. This article will cover the different mechanisms available to select the type of data you want, where it can be filtered down and how to best build detections with this in mind.

Where is the data?

There are several control points for data in LimaCharlie.

  1. The agent is where all events are generated. A subset of the events are sent up to the cloud, the others are stored locally for a short period of time.
  2. The cloud receives the events from the agents and processes them with various analytic systems (like D&R rules).
  3. The outputs are simply the various forwarding locations you've selected for your data.

How does the agent deal with events?

The agent has a list of events that need to be sent to the cloud. This list is dynamic and controlled by the various "exfil_" commands. By issuing an "exfil_add X", you tell the agent to start sending events of type X to the cloud. You can optionally set a time expiration per event type. Doing an "exfil_get" gives you a list of events sent back. A default "exfil list" is automatically sent to your agents.

When an event is generated in the sensor, if it's in the "exfil" list, it's sent to the cloud. If it is NOT in that list, it is kept in memory. For how long? That depends, the capacity is set to a maximum of about 5000 events or 10MB of memory, so a busier host will result in a shorter buffer.

These events in memory are not lost. You can access them mainly via the "history_dump" command. When the agent receives it, it will sent back to the cloud either the entire content of this memory, or if specified all events in memory of a specific typFilter Events After Analysis

Now that the cloud has received events, it can use them to perform analytics. This is where each event (and stream of events) goes through all the Detection & Response rules you've setup. The next step is to forward those events to the outputs you've configured (like a Syslog endpoint for example).

Many users of LimaCharlie want to apply D&R rules onto data, but not necessarily have that data forwarded to their outputs. Storage can be expensive and although data from the agent to the cloud is very efficiently transferred, the data from the cloud to your output is much more verbose (and friendly) JSON. This can result in large amounts of data. This JSON compresses EXTREMELY well, but it can still be cumbersome to receive it all.

The solution to this problem is to use the blacklist/whitelist parameters in each the output. Adding an event type in the blacklist tells the output NOT to forward any events of that type while having an event type in the whitelist means that ONLY events of that type should be forwarded.

How Should I Select the Relevant Data?

The following is a general rule of thumb:

If you have no interest in a particular event type, both for storage as well as analytics, make sure it's not in your "exfil list" by using a D&R rule on event CONNECTED that sends the relevant "exfil_del" command to the agent. This stops it at the source.

If you want to use the event type in detections, similarly use a D&R rule to send an "exfil_add" (if the event type is not already present by default) to your agent.

Next, use the blacklist in the outputs for the events which you don't want to store yourself. This will make sure they don't get forwarded to you.

Of course most SOCs have somewhat more complex setups. For example many will send high-value events to highly-available storage like Splunk or ELK, and will send all other events to colder storage like Amazon S3. This helps to get the relevant data for operational purposes while keeping the cost down.

How do Detections Interact with Events?

The final point of discussion is around the use of events by D&R rules. Some of the events generated by the agent are extremely verbose and it's unlikely you want to send them to the cloud at all times because of bandwidth usage. This is where the power of the D&R rules comes through.

It's often advantageous to write D&R rules in two stages. The first stage is a rule that uses the default events that are always sent to the cloud. Its role is not to determine whether something is "bad" beyond all doubt, but rather to operate as a filter.

This first stage usually uses a "report" Response clause with "publish" to false, indicating the alert is not meant for human consumption. The other Response elements of these first stage rules is to issue commands to the agent to gather the extra data necessary. For example, it may issue a "history_dump FILE_CREATE" to retrieve all recent file creation events from the agent. If a clear state needs to be set clearly between the first and second stage, it is common to apply a Tag to the agent, like "suspicious-exec", which is used by the second stage to determine which agents the rule should be evaluated against.

The second stage rule's job is to look for those file creation events (for example) occurring on agents with the "suspicious-exec" tag for whatever specifically bad behavior is targeted. If found, then the Response component uses the "report" action to trigger an alert.


There are many strategies around data in LimaCharlie. It's easy to get started without having to worry about, but there is also a lot of power behind it to help optimize with MSSPs' infrastructure.

We're always happy to discuss these strategies with you so drop us a line!

Happy hunting.


A New Look


Coming up with a symbol to represent all of the knowledge, passion and hope that is poured into a company is no easy task.

LimaCharlie started out as an open source project. Along the way we made the decision to commercialize it so we could focus our efforts and change the landscape of capabilities available to managed security providers. Our technology company was formed from a big idea and lifetimes spent in the pursuit of innovation. 

Cyber space has become a modern battlefront and  we feel the weight of the responsibility that comes from making tools that are used to protect people. 

With all of that... I am very happy to present to you the symbol we have constructed to represent all that we are and hope to be.