Wp-Login Angreifer mit Crowdsec aussperren

Ich wollte crowdsec um eine einfache Regel erweitern. Alle Zugriffe auf die normale Wordpress Login url wp-login.php auf meinem Blog sind als Angriffe aufzufassen und die Absendeadresse soll von Crowdsec geblockt werden.

Die folgende “Dokumentation” dient nur als meine persönliche Gedächtnisstütze und hat keinerlei Anspruch auf Korrektheit. Bei mir hat es halt so geklappt und den nächsten Usecase würde ich wieder so angehen.

Ich beginne mir eine kleine Testdatei mit den relevanten Logdaten zu erstellen

 grep wp-login /usr/local/www/www-hb/logs/nx-error.log > wp-login.log

Dann erstelle ich einen Parser

vi /usr/local/etc/crowdsec/parsers/s01-parse/nginx-logs-hb.yaml

filter: "evt.Parsed.program startsWith 'nginx'"
onsuccess: next_stage
name: hagen-bauer/nginx-logs-hb
description: "Parse nginx error logs"
nodes:
  - grok:
      pattern: '(%{IPORHOST:target_fqdn} )?%{NGINXERRTIME:time} \[%{LOGLEVEL:loglevel}\] %{NONNEGINT:pid}#%{NONNEGINT:tid}: (\*%{NONNEGINT:cid} )?%{GREEDYDATA:message}, client: %{IPORHOST:remote_addr}, server: %{DATA:target_fqdn}, request: "%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}", host: "%{IPORHOST}(:%{NONNEGINT})?"'
      apply_on: message
      statics:
        - meta: log_type
          value: http_error-log
        - target: evt.StrTime
          expression: evt.Parsed.time
    pattern_syntax:
      NO_DOUBLE_QUOTE: '[^"]+'
    onsuccess: next_stage
    nodes:
      - filter: "evt.Parsed.message contains 'wp-login'"
        statics:
          - meta: log_type
            value: "nginx_wplogin"
statics:
  - meta: service
    value: http
  - meta: source_ip
    expression: "evt.Parsed.remote_addr"
  - meta: http_status
    expression: "evt.Parsed.status"
  - meta: http_path
    expression: "evt.Parsed.request"
  - meta: http_verb
    expression: "evt.Parsed.verb"
  - meta: http_user_agent
    expression: "evt.Parsed.http_user_agent"
  - meta: target_fqdn
    expression: "evt.Parsed.target_fqdn"
``

und das ganze teste ich zuerst

cscli explain --file wp-login.log --type nginx

line: 2021/11/19 02:01:26 [error] 71930#100755: *453882 open() "/usr/local/www/html/wp-login.php" failed (2: No such file or directory), client: 193.142.146.138, server: www.hagen-bauer.de, request: "GET /wp-login.php HTTP/1.1", host: "www.hagen-bauer.de"
        ├ s00-raw
        |       ├ 🟢 crowdsecurity/non-syslog (first_parser)
        |       └ 🔴 crowdsecurity/syslog-logs
        ├ s01-parse
        |       └ 🟢 hagen-bauer/nginx-logs-hb (+16 ~3)
        ├ s02-enrich
        |       ├ 🟢 crowdsecurity/dateparse-enrich (+2 ~1)
        |       ├ 🟢 crowdsecurity/geoip-enrich (+13)
        |       ├ 🔴 crowdsecurity/http-logs
        |       └ 🟢 crowdsecurity/whitelists (unchanged)
        ├-------- parser success 🟢
        ├ Scenarios

Wichtig ist das da was gefunden wird und kein “parser failed” am Ende steht. Jetzt finden wir was aber wir müssen noch sagen was mit den Ergebnissen getan werden soll. Das geschieht in einem “Scenario”

vi /usr/local/etc/crowdsec/scenarios/http-wordpress-login-xmlrpc.yaml

type: leaky
format: 2.0
#debug: true
name: hagen-bauer/http-wordpress-login-xmlrpc
description: "Detect attempt to access to wp-login "
filter: "evt.Meta.log_type == 'nginx_wplogin' "
groupby: "evt.Meta.source_ip"
#distinct: evt.Parsed.request
capacity: 4
leakspeed: 2m
blackhole: 5m
labels:
  service: nginx
  type: bruteforce
  remediation: true

Und noch mal prüfen ob was passiert

cscli explain --file wp-login.log --type nginx

line: 2021/11/19 02:01:26 [error] 71930#100755: *453882 open() "/usr/local/www/html/wp-login.php" failed (2: No such file or directory), client: 193.142.146.138, server: www.hagen-bauer.de, request: "GET /wp-login.php HTTP/1.1", host: "www.hagen-bauer.de"
        ├ s00-raw
        |       ├ 🟢 crowdsecurity/non-syslog (first_parser)
        |       └ 🔴 crowdsecurity/syslog-logs
        ├ s01-parse
        |       └ 🟢 hagen-bauer/nginx-logs-1bs3 (+16 ~3)
        ├ s02-enrich
        |       ├ 🟢 crowdsecurity/dateparse-enrich (+2 ~1)
        |       ├ 🟢 crowdsecurity/geoip-enrich (+13)
        |       ├ 🔴 crowdsecurity/http-logs
        |       └ 🟢 crowdsecurity/whitelists (unchanged)
        ├-------- parser success 🟢
        ├ Scenarios
                └ 🟢 hagen-bauer/http-wordpress-login-xmlrpc

Jetzt kann man von einem anderen Rechner durch wiederholten Aufruf der wp-login.php mal prüfen ob eine “Entscheidung” gefällt worden ist

 # cscli decision list
+---------+----------+--------------------+---------------------------------------+--------+---------+------------------------+--------+--------------------+----------+
|   ID    |  SOURCE  |    SCOPE:VALUE     |                REASON                 | ACTION | COUNTRY |           AS           | EVENTS |     EXPIRATION     | ALERT ID |
+---------+----------+--------------------+---------------------------------------+--------+---------+------------------------+--------+--------------------+----------+
| 95xxx81 | crowdsec | Ip:140.238.175.147 | hagen-bau/http-wordpress-login-xmlrpc | ban    | CH      | 31898 ORACLE-BMC-31898 |      5 | 2h10m34.812851056s |      951 |
+---------+----------+--------------------+---------------------------------------+--------+---------+------------------------+--------+--------------------+----------+