version: 2
title: Password Spraying Detection
contributor: https://github.com/thatdot
summary: Ingests JSON-formatted IAM-style password authentication log file and creates relationships to detect Password Spraying attacks.
description: |-
  Ingests password-based authentication logs modeled on the top IAM providers (hosted and on-prem) and generates a graph manifesting the following nodes:
  attempt - transaction representing a password authentication attempt
  user - user that originated the attempt
  client - client (computer/mobile/unknown) from which user originated the attempt
  asn - ASN from which user originated the attempt
  asset - asset (server, service, etc.) that the user targeted
  time - time of attempt

  The first standing query uses the manifested graph structure to generate synthetic edges between sequential attempts for a user:
  (attempt1)-[:NEXT]->(attempt2)-[:NEXT]->(attempt3)

  The second standing query looks for four consecutive failed attempts followed by a successful attempt from a user to trigger an alert with a link to the subgraph that represents a potential password spraying attack.

  Ensure that the attempts.json file is in the same directory as Quine and issue the following command to begin:
  java -jar quine-x.x.x.jar -r password_spraying.yaml
  where x.x.x represents the version (e.g., quine-1.3.2.jar represents Quine version 1.3.2).

ingestStreams:
  - name: attempts-file-ingest
    source:
      type: File
      path: $in_file
      format:
        type: Json
    query: |-
      //////////////////////////////
      // Set IDs for nodes
      //////////////////////////////
      MATCH (attempt), (client), (asn), (user), (asset)
      WHERE id(attempt) = idFrom('attempt', $that.eventId, $that.timestamp)
        AND id(client) = idFrom('client', $that.user.id, $that.client.ipAddress)
        AND id(asn) = idFrom('asn', $that.client.asn)
        AND id(user) = idFrom('user', $that.user.id)
        AND id(asset) = idFrom('asset', $that.transaction.entityId)

      //////////////////////////////
      // Bucketing for counters
      //////////////////////////////
      CALL incrementCounter(client, "clientCount", 1) YIELD count AS clientCount
      CALL incrementCounter(client, toLower($that.outcome.result), 1) YIELD count AS clientOutcomeCount
      CALL incrementCounter(user, "userCount", 1) YIELD count AS userCount
      CALL incrementCounter(user, toLower($that.outcome.result), 1) YIELD count AS userOutcomeCount
      CALL incrementCounter(asset, "assetCount", 1) YIELD count AS assetCount
      CALL incrementCounter(asset, toLower($that.outcome.result), 1) YIELD count AS assetOutcomeCount

      //////////////////////////////
      // Client
      //////////////////////////////
      SET client.device = $that.client.device,
          client.ipAddress = $that.client.ipAddress,
          client.userAgent = $that.client.userAgent,
          client: client

      // Identify last time client seen across clients //
      SET client.lastseen = coll.max([$that.timestamp, coalesce(client.lastseen, $that.timestamp)])

      // Percentage of success vs. failure //
      SET client.successPercent = ceil(coalesce((client.success*1.0)/(client.count*1.0)*100.0, 0.0))
      SET client.failurePercent = floor(coalesce((client.failure*1.0)/(client.count*1.0)*100.0, 0.0))
      SET client.state = CASE
          // Set threshold ratios below for each of three cases //
          WHEN client.successPercent >= 90 THEN 'good'
          WHEN client.successPercent >= 75 AND client.successPercent < 90 THEN 'warn'
          WHEN client.successPercent < 75 THEN 'alarm'
          ELSE 'alarm'
        END

      //////////////////////////////
      // User
      //////////////////////////////
      SET user.id = $that.user.id,
          user.alternateId = $that.user.alternateId,
          user.displayName = $that.user.displayName,
          user.type = $that.user.type,
          user: user

      // Identify last time user seen across users //
      SET user.lastseen = coll.max([$that.timestamp, coalesce(user.lastseen, $that.timestamp)])

      // Percentage of success vs. failure //
      SET user.successPercent = ceil(coalesce((user.success*1.0)/(user.count*1.0)*100.0, 0.0))
      SET user.failurePercent = floor(coalesce((user.failure*1.0)/(user.count*1.0)*100.0, 0.0))
      SET user.state = CASE
          // Set threshold ratios below for each of three cases //
          WHEN user.successPercent >= 90 THEN 'good'
          WHEN user.successPercent >= 75 AND user.successPercent < 90 THEN 'warn'
          WHEN user.successPercent < 75 THEN 'alarm'
          ELSE 'alarm'
        END

      //////////////////////////////
      // Attempts
      //////////////////////////////
      SET attempt.schemaVersion = $that.schemaVersion,
          attempt.eventId = $that.eventId,
          attempt.transactionId = $that.transaction.id,
          attempt.timestamp = $that.timestamp,
          attempt.entityId = $that.transaction.entityId,
          attempt.eventType = $that.eventType,
          attempt.transactionType = $that.transaction.type,
          attempt.eventCode = $that.eventCode,
          attempt.displayMessage = $that.displayMessage,
          attempt.outcomeResult = $that.outcome.result,
          attempt.logLevel = $that.level,
          attempt.zone = $that.client.zone,
          attempt.client = $that.client.ipAddress,
          attempt.userSequence = coalesce(userCount,0),
          attempt.clientSequence = coalesce(clientCount,0),
          attempt: attempt

      //////////////////////////////
      // ASN
      //////////////////////////////
      SET asn.id = $that.client.asn,
          asn: asn

      //////////////////////////////
      // Asset
      //////////////////////////////
      SET asset.id = $that.transaction.entityId,
          asset.detail = $that.client.requestUri,
          asset: asset

      // Percentage of success vs. failure //
      SET asset.successPercent = ceil(coalesce((asset.success*1.0)/(asset.count*1.0)*100.0, 0.0))
      SET asset.failurePercent = floor(coalesce((asset.failure*1.0)/(asset.count*1.0)*100.0, 0.0))
      SET asset.state = CASE
          // Set threshold ratios below for each of three cases //
          WHEN asset.successPercent >= 90 THEN 'good'
          WHEN asset.successPercent >= 75 AND asset.successPercent < 90 THEN 'warn'
          WHEN asset.successPercent < 75 THEN 'alarm'
          ELSE 'alarm'
        END

      //////////////////////////////
      // Create relationship between nodes
      //////////////////////////////
      CREATE (user)-[:ORIGINATED]->(attempt)-[:USING]->(client),
             (client)<-[:USING]-(attempt)-[:TARGETED]->(asset),
             (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(asset),
             (attempt)-[:OVER]->(asn)

standingQueries:
  - name: sequence-attempts
    pattern:
      type: Cypher
      parallelism: 32
      query: |-
        ////////////////////////////////////////////////////////
        // Subquery to sequence attempts (attempt)-[:NEXT]->(attempt)
        ////////////////////////////////////////////////////////
        MATCH (client2)<-[:USING]-(attempt1)<-[:ORIGINATED]-(user)-[:ORIGINATED]->(attempt2)-[:USING]->(client1)
        RETURN DISTINCT id(attempt2) AS attempt2
      mode: DISTINCT_ID
    outputs:
      - name: sequence
        preEnrichmentTransformation:
          type: InlineData
        resultEnrichment:
          query: |-
            MATCH (client2)<-[:USING]-(attempt2)<-[:ORIGINATED]-(user)-[:ORIGINATED]->(attempt1 {clientSequence: (attempt2.clientSequence-1)})-[:USING]->(client1)
            WHERE id(attempt2) = $that.id
              AND id(client1) = id(client2)
            CREATE (attempt2)<-[:NEXT]-(attempt1)
          parameter: that
        destinations:
          - type: Drop
  - name: detect-password-spraying
    pattern:
      type: Cypher
      query: |-
        ///////////////////////////////////////////////////////////////////////////////////
        // Subquery to match 4 consecutive failed attempts followed by a successful attempt
        ///////////////////////////////////////////////////////////////////////////////////
        MATCH (attempt1 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt2 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt3 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt4 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt5 {outcomeResult:"SUCCESS"})-[:USING]->(client4)
        RETURN DISTINCT id(attempt1) AS attempt1
      mode: DISTINCT_ID
    outputs:
      - name: alert
        preEnrichmentTransformation:
          type: InlineData
        resultEnrichment:
          query: |-
            MATCH (attempt1 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt2 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt3 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt4 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt5 {outcomeResult:"SUCCESS"})
            WHERE id(attempt1)=$that.id
            RETURN 'Password Spraying Attack: ' + 'http://localhost:8080/#' + text.urlencode('MATCH (user)-[:ORIGINATED]->(attempt1 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt2 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt3 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt4 {outcomeResult:"FAILURE"})-[:NEXT]->(attempt5 {outcomeResult:"SUCCESS"})-[:USING]->(client) WHERE id(attempt1)="' + toString(strId(attempt1)) + '" RETURN DISTINCT user,attempt1,attempt2,attempt3,attempt4,attempt5,client') AS QuineUILink
          parameter: that
        destinations:
          - type: StandardOut
nodeAppearances:
  # Attempt Appearance for FAILURE *******************
  - predicate:
      propertyKeys:
        - outcomeResult
      knownValues:
        outcomeResult: "FAILURE"
      dbLabel: attempt
    icon: ion-log-in
    color: "#F44336"
    size:
    label:
      type: Property
      key: timestamp
      prefix: "attempt: "
    # Attempt Appearance for SUCCESS *******************
  - predicate:
      propertyKeys:
        - outcomeResult
      knownValues:
        outcomeResult: "SUCCESS"
      dbLabel: attempt
    icon: ion-log-in
    color: "#32a852"
    size:
    label:
      type: Property
      key: timestamp
      prefix: "attempt: "
    # Client Type (computer/mobile/unknown) Appearance *******************
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "computer"
        state: "good"
      dbLabel: client
    icon: ion-android-laptop
    color: "#32a852"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "computer"
        state: "warn"
      dbLabel: client
    icon: ion-android-laptop
    color: "#d68400"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "computer"
        state: "alarm"
      dbLabel: client
    icon: ion-android-laptop
    color: "#cf151e"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "mobile"
        state: "good"
      dbLabel: client
    icon: ion-iphone
    color: "#32a852"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "mobile"
        state: "warn"
      dbLabel: client
    icon: ion-iphone
    color: "#d68400"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "mobile"
        state: "alarm"
      dbLabel: client
    icon: ion-iphone
    color: "#cf151e"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "unknown"
        state: "good"
      dbLabel: client
    icon: ion-help
    color: "#32a852"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "unknown"
        state: "warn"
      dbLabel: client
    icon: ion-help
    color: "#d68400"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
  - predicate:
      propertyKeys:
        - device
        - state
      knownValues:
        device: "unknown"
        state: "alarm"
      dbLabel: client
    icon: ion-help
    color: "#cf151e"
    size:
    label:
      type: Property
      key: ipAddress
      prefix: "client: "
    # ASN  Appearance *******************
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asn
    icon: radio-waves
    color:
    size: 48
    label:
      type: Property
      key: id
      prefix: "asn: "
    # User (USER/ADMIN/GUEST/CONTRACTOR) Appearance *******************
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "User"
        state: "good"
      dbLabel: user
    icon: ion-ios-contact-outline
    color: "#32a852"
    size:
    label:
      type: Property
      key: id
      prefix: "user: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "User"
        state: "warn"
      dbLabel: user
    icon: ion-ios-contact-outline
    color: "#d68400"
    size:
    label:
      type: Property
      key: id
      prefix: "user: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "User"
        state: "alarm"
      dbLabel: user
    icon: ion-ios-contact-outline
    color: "#cf151e"
    size:
    label:
      type: Property
      key: id
      prefix: "user: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Admin"
        state: "good"
      dbLabel: user
    icon: ion-at
    color: "#32a852"
    size:
    label:
      type: Property
      key: id
      prefix: "admin: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Admin"
        state: "warn"
      dbLabel: user
    icon: ion-at
    color: "#d68400"
    size:
    label:
      type: Property
      key: id
      prefix: "admin: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Admin"
        state: "alarm"
      dbLabel: user
    icon: ion-at
    color: "#cf151e"
    size:
    label:
      type: Property
      key: id
      prefix: "admin: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Contractor"
        state: "good"
      dbLabel: user
    icon: ion-hammer
    color: "#32a852"
    size:
    label:
      type: Property
      key: id
      prefix: "contractor: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Contractor"
        state: "warn"
      dbLabel: user
    icon: ion-hammer
    color: "#d68400"
    size:
    label:
      type: Property
      key: id
      prefix: "contractor: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Contractor"
        state: "alarm"
      dbLabel: user
    icon: ion-hammer
    color: "#cf151e"
    size:
    label:
      type: Property
      key: id
      prefix: "contractor: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Guest"
        state: "good"
      dbLabel: user
    icon: ion-social-octocat
    color: "#32a852"
    size:
    label:
      type: Property
      key: id
      prefix: "guest: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Guest"
        state: "warn"
      dbLabel: user
    icon: ion-social-octocat
    color: "#d68400"
    size:
    label:
      type: Property
      key: id
      prefix: "guest: "
  - predicate:
      propertyKeys:
        - type
        - state
      knownValues:
        type: "Guest"
        state: "alarm"
      dbLabel: user
    icon: ion-social-octocat
    color: "#cf151e"
    size:
    label:
      type: Property
      key: id
      prefix: "guest: "
      # Zone (ONNET/OFFNET/VPN) Appearance *******************
  - predicate:
      propertyKeys:
        - id
      knownValues:
        id: "VPN"
      dbLabel: zone
    icon: ion-locked
    color:
    size:
    label:
      type: Property
      key: id
      prefix: "zone: "
  - predicate:
      propertyKeys:
        - id
      knownValues:
        id: "ONNET"
      dbLabel: zone
    icon: ion-network
    color:
    size:
    label:
      type: Property
      key: id
      prefix: "zone: "
  - predicate:
      propertyKeys:
        - id
      knownValues:
        id: "OFFNET"
      dbLabel: zone
    icon: ion-android-globe
    color:
    size:
    label:
      type: Property
      key: id
      prefix: "zone: "
    # Asset Appearance *******************
  - predicate:
      propertyKeys:
        - state
      knownValues:
        state: "good"
      dbLabel: asset
    icon: ion-ios-briefcase
    color: "#32a852"
    size:
    label:
      type: Property
      key: id
      prefix: "asset: "
  - predicate:
      propertyKeys:
        - state
      knownValues:
        state: "warn"
      dbLabel: asset
    icon: ion-ios-briefcase
    color: "#d68400"
    size:
    label:
      type: Property
      key: id
      prefix: "asset: "
  - predicate:
      propertyKeys:
        - state
      knownValues:
        state: "alarm"
      dbLabel: asset
    icon: ion-ios-briefcase
    color: "#cf151e"
    size:
    label:
      type: Property
      key: id
      prefix: "asset: "
    # Period (year/month/day/hour/minute/second) Appearance *******************
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "second"
      dbLabel:
    icon: ion-clock
    color:
    size: 22
    label:
      type: Property
      key: start
      prefix: "timestamp: "
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "minute"
      dbLabel:
    icon: ion-clock
    color:
    size: 24
    label:
      type: Property
      key: start
      prefix: "timestamp: "
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "hour"
      dbLabel:
    icon: ion-clock
    color:
    size: 32
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "day"
      dbLabel:
    icon: ion-android-calendar
    color:
    size: 24
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "month"
      dbLabel:
    icon: ion-android-calendar
    color:
    size: 32
  - predicate:
      propertyKeys:
        - period
      knownValues:
        period: "year"
      dbLabel:
    icon: ion-android-calendar
    color:
    size: 40
sampleQueries:
  # Provide easy access to node types in the Exploration UI
  - name: Last 10 Nodes
    query: CALL recentNodes(10)
  - name: Legend
    query: MATCH (n) WHERE labels(n) IS NOT NULL WITH labels(n) AS kind, collect(n) AS legend RETURN legend[0]
  - name: One User Node
    query: MATCH (user:user) RETURN user LIMIT 1
  - name: One Asset Node
    query: MATCH (asset:asset) RETURN asset LIMIT 1
  - name: One ASN Node
    query: MATCH (asn:asn) RETURN asn LIMIT 1
  - name: One Attempt Node
    query: MATCH (attempt:attempt) RETURN attempt LIMIT 1
  - name: One Client Node
    query: MATCH (client:client) RETURN client LIMIT 1
  - name: Table of logins showing spraying attack (run with SHIFT/RETURN)
    query: MATCH (n) WHERE id(n) = idFrom('user', '8b2d78e3-6d4d-42f4-9221-4d91111fe62d') MATCH (n)-[:ORIGINATED]->(m)-[:USING]->(o) MATCH (n)-[:ORIGINATED]->(m) RETURN m.timestamp AS Timestamp, m.eventId AS Attempt, o.ipAddress AS Source, m.zone AS Zone, m.entityId AS Entity, m.outcomeResult AS Outcome ORDER BY m.timestamp
  - name: Table of logins showing spraying attack with only attacker (run with SHIFT/RETURN)
    query: MATCH (n) WHERE id(n) = idFrom('user', '8b2d78e3-6d4d-42f4-9221-4d91111fe62d') MATCH (n)-[:ORIGINATED]->(m)-[:USING]->(o) MATCH (n)-[:ORIGINATED]->(m) WHERE o.ipAddress="217.21.4.61" RETURN m.timestamp AS Timestamp, m.userSequence AS Sequence, m.eventId AS Attempt, o.ipAddress AS Source, m.zone AS Zone, m.entityId AS Entity, m.outcomeResult AS Outcome ORDER BY m.timestamp
  - name: Find Incoming NEXT Loop
    query: MATCH (attempt1:attempt)-[:NEXT]->(attempt0:attempt)<-[:NEXT]-(attempt2:attempt) RETURN attempt0,attempt1,attempt2 LIMIT 1
  - name: Find Outgoing NEXT Loop
    query: MATCH (attempt1:attempt)<-[:NEXT]-(attempt0:attempt)-[:NEXT]->(attempt2:attempt) RETURN attempt0,attempt1,attempt2 LIMIT 1
  - name: Dirty Attempts (SHIFT-ENTER)
    query: MATCH (attempt:attempt)-[r]-() WITH attempt, count(r) AS edgeCount WHERE edgeCount>7 RETURN count(attempt) AS OOGIE_NODES
quickQueries:
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: "[Node] Adjacent Nodes"
      querySuffix: MATCH (n)--(m) RETURN DISTINCT m
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: "[Node] Refresh"
      querySuffix: RETURN n
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: "[Text] Local Properties"
      querySuffix: RETURN id(n), properties(n)
      sort: TEXT
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] All User Types that Targeted Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) RETURN user
      sort: NODE
      edgeLabel: TARGETED_BY
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] Admins that Targeted Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) WHERE user.type = "Admin" RETURN user
      sort: NODE
      edgeLabel: TARGETED_BY_ADMIN
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] Contractors that Targeted Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) WHERE user.type = "Contractor" RETURN user
      sort: NODE
      edgeLabel: TARGETED_BY_CONTRACTOR
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] Contractors that Failed Authentication for Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) WHERE user.type = "Contractor" AND attempt.outcomeResult = "FAILURE" RETURN user
      sort: NODE
      edgeLabel: FAILED_AUTH_BY_CONTRACTOR
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] Guests that Targeted Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) WHERE user.type = "Guest" RETURN user
      sort: NODE
      edgeLabel: TARGETED_BY_GUEST
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: asset
    quickQuery:
      name: "[Node] Users that Targeted Asset"
      querySuffix: MATCH (user)-[:ORIGINATED]->(attempt)-[:TARGETED]->(n) WHERE user.type = "User" RETURN user
      sort: NODE
      edgeLabel: TARGETED_BY_USER
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: attempt
    quickQuery:
      name: "[Node] Previous Attempt"
      querySuffix: MATCH (n)<-[:NEXT]-(attempt) RETURN attempt
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: attempt
    quickQuery:
      name: "[Node] Next Attempt"
      querySuffix: MATCH (n)-[:NEXT]->(attempt) RETURN attempt
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: attempt
    quickQuery:
      name: "[Node] Show Client and ASN"
      querySuffix: MATCH (n)-[:USING]->(m) MATCH (n)-[:OVER]->(o) RETURN DISTINCT n,m,o
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: client
    quickQuery:
      name: "[Node] Targeted Assets"
      querySuffix: MATCH (n)<-[:USING]-(attempt)-[:TARGETED]->(asset:asset) RETURN asset
      sort: NODE
      edgeLabel: TARGETED
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: client
    quickQuery:
      name: "[Text] Authentication attempts in chronological order"
      querySuffix: MATCH (n)<-[:USING]->(m) RETURN m.timestamp AS Timestamp, m.eventId AS Attempt, n.ipAddress AS Source, m.zone AS Zone, m.entityId AS Entity, m.outcomeResult AS Outcome ORDER BY m.timestamp
      sort: TEXT
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Failed Password Authentication Attempts"
      querySuffix: MATCH (n)-[:ORIGINATED]->(attempt {outcomeResult:"FAILURE"}) RETURN attempt
      sort: NODE
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Targeted Assets"
      querySuffix: MATCH (n)-[:ORIGINATED]->(attempt)-[:TARGETED]->(asset:asset) RETURN asset
      sort: NODE
      edgeLabel: TARGETED
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Text] Authentication attempts in chronological order"
      querySuffix: MATCH (n)-[:ORIGINATED]->(m)-[:USING]->(o) MATCH (n)-[:ORIGINATED]->(m) RETURN m.timestamp AS Timestamp, m.eventId AS Attempt, o.ipAddress AS Source, m.zone AS Zone, m.entityId AS Entity, m.outcomeResult AS Outcome ORDER BY m.timestamp
      sort: TEXT
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Attempts Timeline"
      querySuffix: MATCH (n)-[:ORIGINATED]->(event)-[:NEXT]->(m) RETURN DISTINCT m
      sort: NODE
