version: 1
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 attemps.json file is in the same directory as Quine and issue the following command to begin:
  java -jar {{ quine }} -r password_spraying.yml
  where x.x.x represents the version (e.g., quine-1.3.2.jar represents Quine version 1.3.2).

ingestStreams:
  - type: FileIngest
    path: attempts.json
    format:
      type: CypherJson
      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:
  - 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: DistinctId
    outputs:
      sequence:
        type: CypherQuery
        query: |-
          MATCH (client2)<-[:USING]-(attempt2)<-[:ORIGINATED]-(user)-[:ORIGINATED]->(attempt1 {clientSequence: (attempt2.clientSequence-1)})-[:USING]->(client1)
          WHERE id(attempt2) = $that.data.attempt2
            AND id(client1) = id(client2)
          CREATE (attempt2)<-[:NEXT]-(attempt1)
        shouldRetry: false
  - 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: DistinctId
    outputs:
      alert:
        type: CypherQuery
        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.data.attempt1
          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
        andThen:
          type: PrintToStandardOut
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
      queryLanguage: Cypher
      sort: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: "[Node] Refresh"
      querySuffix: RETURN n
      queryLanguage: Cypher
      sort: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
    quickQuery:
      name: "[Text] Local Properties"
      querySuffix: RETURN id(n), properties(n)
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      sort: Node
      edgeLabel: TARGETED_BY_USER
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: attempt
    quickQuery:
      name: "[Node] Previous Attempt"
      querySuffix: MATCH (n)<-[:NEXT]-(attempt) RETURN attempt
      queryLanguage: Cypher
      sort: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: attempt
    quickQuery:
      name: "[Node] Next Attempt"
      querySuffix: MATCH (n)-[:NEXT]->(attempt) RETURN attempt
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      sort: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: client
    quickQuery:
      name: "[Node] Targeted Assets"
      querySuffix: MATCH (n)<-[:USING]-(attempt)-[:TARGETED]->(asset:asset) RETURN asset
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      sort: Text
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Failed Password Authentication Attempts"
      querySuffix: MATCH (n)-[:ORIGINATED]->(attempt {outcomeResult:"FAILURE"}) RETURN attempt
      queryLanguage: Cypher
      sort: Node
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Targeted Assets"
      querySuffix: MATCH (n)-[:ORIGINATED]->(attempt)-[:TARGETED]->(asset:asset) RETURN asset
      queryLanguage: Cypher
      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
      queryLanguage: Cypher
      sort: Text
  - predicate:
      propertyKeys: []
      knownValues: {}
      dbLabel: user
    quickQuery:
      name: "[Node] Attempts Timeline"
      querySuffix: MATCH (n)-[:ORIGINATED]->(event)-[:NEXT]->(m) RETURN DISTINCT m
      queryLanguage: Cypher
      sort: Node