WPNinjas HeaderWPNinjas Header

Enhancing Network Security Insights with IDS/IPS of Ubiquiti Dream Machine Pro and Microsoft Sentinel

In my previous post, I explored the basics of integrating Ubiquiti Dream Machine Pro logs with Microsoft Sentinel, setting the stage for advanced network monitoring and security analysis. Today, we’re taking a significant leap forward by incorporating the threat detection capabilities of the device, which is using Suricata, an open-source network threat detection tool. This extension is enabled as soon IDS or IPS is enabled in the Unifi Admin interface. But sadly, Ubiquiti is not forwarding the detections to syslog by default.

Setting the Stage for Threat Detection

Suricata offers a robust framework for monitoring network traffic in real-time, identifying potential threats, and logging them for further analysis. Configuring Suricata on the Ubiquiti Dream Machine Pro requires a bit of under-the-hood changes, specifically with one of the device’s configuration files via SSH.

Configuring Suricata on Ubiquiti Dream Machine Pro

The key to enabling Suricata’s threat detection lies in manipulating its configuration file, located at “/usr/share/ubios-udapi-server/ips/config/suricata_ubios_high.yaml”. By accessing this file, users can adjust Suricata to their specific security monitoring needs. The simplest is by using the VI editor. 

vi suricata_ubios_high.yaml

For non linux admins:

  1. Press “INSERT” to enter the edit mode
  2. Adjust the lines as mentioned in the next section (lines 28-46 and 83-87)
  3. Press Esc to enter Command mode
  4. Type :wq to write and quit the file.
%YAML 1.1
---
runmode: workers

include: /run/ips/config/homenet.yaml
include: /run/ips/config/rules.yaml

classification-file: /usr/share/ubios-udapi-server/ips/config/classification.config
reference-config-file: /usr/share/ubios-udapi-server/ips/config/reference.config
threshold-file: /run/ips/config/threshold.config
default-log-dir: /run/utm/

stats:
  enabled: no
  interval: 60

outputs:
  - eve-log:
      enabled: no
      filetype: unix_dgram #regular|syslog|unix_dgram|unix_stream|redis
      filename: eve_stat.json
      types:
        - stats:
            totals: yes       # stats for all threads merged together
            threads: no       # per thread stats
            deltas: yes        # include delta values

  - eve-log:
      enabled: yes
      filetype: syslog #regular|syslog|unix_dgram|unix_stream|redis
      identity: "suricata"
      facility: local5
      types:
        - alert:
            payload: no             # enable dumping payload in Base64
            payload-buffer-size: 4kb # max size of payload buffer to output in eve-log
            http-body: no                # enable dumping of http fields
            tagged-packets: yes
        - http:
            extended: yes
        - tls:
            extended: yes
        - dns:
            query: yes     # enable logging of DNS queries
            answer: no   # enable logging of DNS answers
            custom: [a]

  - eve-log:
      enabled: yes
      filetype: unix_dgram #regular|syslog|unix_dgram|unix_stream|redis
      filename: eve_alert.json
      types:
        - alert:
            payload: no             # enable dumping payload in Base64
            payload-buffer-size: 4kb # max size of payload buffer to output in eve-log
            http-body: no                # enable dumping of http fields
            tagged-packets: yes
        - http:
            extended: yes
        - tls:
            extended: yes
        - dns:
            query: yes     # enable logging of DNS queries
            answer: no   # enable logging of DNS answers
            custom: [a]
        - drop:
            alerts: yes      # log alerts that caused drops
            flows: start     # start or all: 'start' logs only a single drop
                             # per flow direction. All logs each dropped pkt.
        - files:
logging:
  default-log-level: notice
  default-output-filter:
  outputs:
  - console:
      enabled: no
      # type: json
  - file:
      enabled: yes
      level: info
      filename: /run/ips/suricata.log
      # type: json
  - syslog:
      enabled: yes
      facility: local5
      format: "[%i] <%d> -- "
      # type: json

app-layer:
  protocols:
    tls:
      enabled: yes
      detection-ports:
        dp: 443
      #no-reassemble: yes
      # bypass encryption packets in the kernel or in hardware
      # ref: https://github.com/OISF/suricata/blob/96b48d71042c282a51c83607eb8e25a1bf1e4bdb/suricata.yaml.in#L885
      encrypt-handling: bypass
    dcerpc:
      enabled: detection-only
    ftp:
      enabled: detection-only
      memcap: 32mb
    tftp:
      enabled: detection-only
    ssh:
      enabled: detection-only
    smtp:
      enabled: yes
      mime:
        decode-mime: yes
        decode-base64: yes
        decode-quoted-printable: yes
        header-value-depth: 2000
        extract-urls: yes
        body-md5: no
      inspected-tracker:
        content-limit: 100000
        content-inspect-min-size: 32768
        content-inspect-window: 4096
    imap:
      enabled: no
    msn:
      enabled: no
    smb:
      enabled: yes
      detection-ports:
        dp: 139, 445
    smb2:
      enabled: no
    dns:
      #global-memcap: 16mb
      #state-memcap: 512kb
      #request-flood: 500
      tcp:
        enabled: yes
        detection-ports:
          dp: 53
      udp:
        enabled: yes
        detection-ports:
          dp: 53
    http:
      enabled: yes
      memcap: 32mb

      libhtp:
         default-config:
           personality: IDS
           request-body-limit: 100kb
           response-body-limit: 100kb
           request-body-minimal-inspect-size: 32kb
           request-body-inspect-window: 4kb
           response-body-minimal-inspect-size: 40kb
           response-body-inspect-window: 16kb
           response-body-decompress-layer-limit: 2
           http-body-inline: auto
           #randomize-inspection-sizes: yes
           #randomize-inspection-range: 10
           double-decode-path: no
           double-decode-query: no
    http2:
      enabled: yes
    modbus:
      enabled: no
    enip:
      enabled: no
    dnp3:
      enabled: no
    nfs:
      enabled: no
    ikev2:
      enabled: no
    krb5:
      enabled: no
    dhcp:
      enabled: no
    ntp:
      enabled: no
    snmp:
      enabled: no
    sip:
      enabled: no
    rfb:
      enabled: no
    mqtt:
      enabled: no
    rdp:
      enabled: no

asn1-max-frames: 256

#run-as:
#  user: suri
#  group: suri

sensor-name: usg-sensor
pid-file: /var/run/suricata.pid
#daemon-directory: "/"

coredump:
  max-dump: unlimited

host-mode: auto

max-pending-packets: 1000

#default-packet-size: 1514

legacy:
  uricontent: enabled

action-order:
   - reject
   - pass
   - drop
   - alert

engine-analysis:
  rules-fast-pattern: yes
  rules: yes

pcre:
  match-limit: 2000
  match-limit-recursion: 1000

host-os-policy:
  windows: [0.0.0.0/0]
  bsd: []
  bsd-right: []
  old-linux: []
  linux: []
  old-solaris: []
  solaris: []
  hpux10: []
  hpux11: []
  irix: []
  macos: []
  vista: []
  windows2k3: []

defrag:
  memcap: 8mb
  hash-size: 32768
  trackers: 32767 # number of defragmented flows to follow
  max-frags: 32767 # number of fragments to keep (higher than trackers)
  prealloc: yes
  timeout: 60

flow:
  memcap: 16mb
  hash-size: 65536
  prealloc: 500
  emergency-recovery: 30
  #managers: 1 # default to one flow manager
  #recyclers: 1 # default to one flow recycler thread

vlan:
  use-for-tracking: true

flow-timeouts:

  default:
    new: 30
    established: 300
    closed: 0
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-closed: 0
    emergency-bypassed: 50
  tcp:
    new: 60
    established: 600
    closed: 60
    bypassed: 100
    emergency-new: 5
    emergency-established: 100
    emergency-closed: 10
    emergency-bypassed: 50
  udp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50
  icmp:
    new: 30
    established: 300
    bypassed: 100
    emergency-new: 10
    emergency-established: 100
    emergency-bypassed: 50

stream:
  memcap: 16mb
  checksum-validation: yes      # reject wrong csums
  inline: auto                  # auto will use inline mode in IPS mode, yes or no set it statically
  bypass: yes                   # bypass packets when stream.reassembly.depth is reached
  reassembly:
    memcap: 8mb
    depth: 512kb                  # reassemble 512kb into a stream
    toserver-chunk-size: 1280
    toclient-chunk-size: 1280
    randomize-chunk-size: yes

host:
  hash-size: 4096
  prealloc: 500
  memcap: 4mb

detect:
  profile: low # tend to consume CPU
  custom-values:
    toclient-groups: 3
    toserver-groups: 25
  sgh-mpm-context: auto
  inspection-recursion-limit: 1000
  #delayed-detect: yes

  prefilter:
    default: mpm

  grouping:
    #tcp-whitelist: 53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080
    #udp-whitelist: 53, 135, 5060

  profiling:
    #inspect-logging-threshold: 200
    grouping:
      dump-to-disk: false
      include-rules: false      # very verbose
      include-mpm-stats: false

mpm-algo: ac
spm-algo: bm

threading:
  set-cpu-affinity: yes
  cpu-affinity:
    - management-cpu-set:
        cpu: [ 0 ]  # include only these cpus in affinity settings
    - receive-cpu-set:
        cpu: [ "all" ]  # include only these cpus in affinity settings
    - worker-cpu-set:
        cpu: [ "all" ]
        prio:
          default: "high"
    - verdict-cpu-set:
        cpu: [ 1 ]
        prio:
          default: "high"
  detect-thread-ratio: 1.0

nfq:
  mode: repeat
  repeat-mark: 1
  repeat-mask: 1
  bypass-mark: 1
  bypass-mask: 1
  fail-open: yes

include: /run/ips/config/iface.yaml

Eve-log: Dual Output with a Caveat

A pivotal feature for forwarding threat detection logs is Suricata’s “eve-log” functionality, which allows data to be sent to the local syslog daemon. This capability is crucial not only for internal logging purposes but also for leveraging Ubiquiti services that prepare the data for the web interface. Interestingly, “eve-log” can be configured to output data in multiple formats simultaneously, enhancing the versatility of logging options.

However, it’s important to note that while “eve-log” can have multiple outputs, the logging of dropped packets (“drops”) is limited to a single instance. This limitation means that while detections are logged, information on whether these threats are subsequently blocked on follow-up attempts may not be readily available. Users must trust in the configured security measures to effectively mitigate these threats.

Suricata Operational Logs

In addition to threat detection logs, Suricata’s operational logs can also be directed to the local syslog daemon. These logs are invaluable for troubleshooting and provide insights into the inner workings and performance of Suricata within the network infrastructure.

First Result

After applying the necessary configuration changes, the new logs start flowing into Microsoft Sentinel.

The Path to Microsoft Sentinel Integration

However, a challenge arises: the logs, now augmented with Suricata’s threat detection data, are not automatically parsed by the existing “UbiquitiAuditEvent” function in Sentinel and we need to extend it with custom processing to fully leverage the new data. There could even be the discussion if these dedicated logs should not flow into a separate table which would allow for example different retention policy. I decided in this blog to leave it as it is and parse them when using with a own workspace function which I called “UbiquitiSuricataAlert”.

Ubiquiti_CL 
    | where facility == 21 
    | extend MessageJson= trim_start(@"DreamMachinePro suricata\[[0-9]*\]: ",message)
    | extend SrcMacAddr = tostring(parse_json(MessageJson).src_mac)
    | extend SrcIpAddr = tostring(parse_json(MessageJson).src_ip)
    | extend SrcPortNumber = tostring(parse_json(MessageJson).src_port)
    | extend DstIpAddr = tostring(parse_json(MessageJson).dest_ip)
    | extend DstMacAddr = tostring(parse_json(MessageJson).dest_mac)
    | extend DstPortNumber = tostring(parse_json(MessageJson).dest_port)
    | extend FlowId = tostring(parse_json(MessageJson).flow_id)
    | extend NetworkProtocol = tostring(parse_json(MessageJson).proto)
    | extend DvcInboundInterface = tostring(parse_json(MessageJson).in_iface)
    | extend EventVendor = "Ubiquiti-Suricata"
    | extend EventCategory = tostring(parse_json(MessageJson).event_type)
    | extend Service  = tostring(parse_json(MessageJson).app_protocol)
    | extend DvcAction  = tostring(parse_json(MessageJson).alert.action)
    | extend AlertMetadata = tostring(parse_json(MessageJson).alert.metadata)
    | extend AlertSignature = tostring(parse_json(MessageJson).alert.signature)
    | extend AlertCategory = tostring(parse_json(MessageJson).alert.category)
    | extend DvcIpAddr = host
    | project TimeGenerated
        , EventVendor
        , EventCategory
        , AlertMetadata
        , AlertSignature
        , Service
        , FlowId
        , DvcInboundInterface
        , DvcAction
        , SrcMacAddr
        , SrcIpAddr
        , SrcPortNumber
        , DstMacAddr
        , DstIpAddr
        , DstPortNumber
        , NetworkProtocol
        , DvcIpAddr
        

Creating Alerts based on the detections

Now, we have the data prepared and would like to show the detections also as Alerts in Sentinel. For this we need to create an Analytic Rule. On the first page we can define a name and description for the detection. As plenty of different alerts with variable Tactics and Techniques we have should leverage a generic name.

The rule logic step contains the mani possibilities. As we have created a Workspace Function in the step before we can now leverage a very simple query which will just create a detection on each occurence. 

In the Alert enrichment section we can map the source and destination IP of the alert as an IP entity. 

We can also include specific columns in the alert details which can help our security analysts during investigation. I add the signature and the complete AlertMetadata which is a json object with different information about the alert.

Then as every Suricata alert could be a different detection we can leverage the possibility of Microsoft Sentinel to set the title to the threat signature.

As I want the alert popping up on my dashboard I choose a schedule of 15minutes. Potentially I should choose 5 minutes as the Ubiquiti Dream Machine Pro is by default blocking traffic of a threat detection for 5 minutes. Going down to 5 minutes could help if I would create an automation which then can create a firewall rule for a longer time. 

As each row represents one detection I also want that it creates for each result an own alert which ends in a 1:1 relationship. 

By clicking on “Test with current data” we can see how many alerts would be created in the past 50 execution. As you can see there are plenty of detection just in my home lab.

The next page in the Analytic rule wizard is in my opinion one of the most important. In my example I would like to create a incident for detections, but if attacks are always from one IP and to the same IP then it belongs to the same attack and can be combined in one incident and handled by a Security Analyst in one step. 

I skipped now the Automated response part which in this example could be for example automatically creating firewall rules to block the specific source if external or disabling a switch port for internal IP’s. Showcasing such scenarios will be part of one of my next blogs. 
After a while the first Incidents are created in Microsoft Sentinel and as you can see the incident combines multiple alerts and is enriched with important information.

Conclusion

Integrating Suricata’s threat detection capabilities of Ubiquiti Dream Machine Pro with Microsoft Sentinel marks a significant step forward in network security monitoring in my lab.

The journey towards enhanced security is ongoing, and with the right tools and configurations, organizations can better protect their digital assets in an ever-evolving threat environment. Sadly that Ubiquiti is not sending this information by default to the configured Syslog server.

Follow me

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.