crowdsecurity / hub

Main repository for crowdsec scenarios/parsers
https://hub.crowdsec.net
159 stars 147 forks source link

crowdsecurity/http-bf-wordpress_bf is bypassed when using NGINX Reverse Proxy #861

Open sanvu88 opened 12 months ago

sanvu88 commented 12 months ago

File: http-bf-wordpress_bf.yaml

When using NGINX Reverse Proxy, users only need to create a post method to a link like http://example.com/wp-login.php/ to bypass /http-bf-wordpress_bf. Because the link ends with /, file_name will be empty. Currently my solution is to create a filter like this:

filter: 'evt.Meta.log_type == "http_access-log" && evt.Meta.http_path contains "wp-login.php" && evt.Parsed.verb == "POST" && evt.Meta.http_status in ["200", "301", "302", "307"]'

LaurenceJJones commented 11 months ago

Are you directly just proxying passing it to the applications or doing any regex matching?

sanvu88 commented 11 months ago

Are you directly just proxying passing it to the applications or doing any regex matching?

I'm not. It seems Nginx will automatically remove the / at the end and do a 302 redirect to wp-login.php causing the filter to be bypassed.

LaurenceJJones commented 11 months ago

Just quick test (From my tests both dont care about trailing slash)

Compose

services:
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 80:80
  db:
    # We use a mariadb image which supports both amd64 & arm64 architecture
    image: mariadb:10.6.4-focal
    # If you really want to use MySQL, uncomment the following line
    #image: mysql:8.0.27
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=somewordpress
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
  wordpress:
    image: wordpress:latest
    volumes:
      - wp_data:/var/www/html
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress
volumes:
  db_data:
  wp_data:

nginx.conf

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream docker-app {
        server wordpress:80;
    }

    access_log /var/log/nginx/example.access.log;
    error_log /var/log/nginx/example.error.log;

    server {
        listen 80;

        location / {
            proxy_pass         http://docker-app;
            proxy_redirect     off;
        }
    }
}

Wordpress logs

root-wordpress-1  | 172.18.0.2 - - [31/Oct/2023:09:30:44 +0000] "GET /wp-login.php HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.2 - - [31/Oct/2023:09:30:44 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.0" 404 452 "http://docker-app/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.2 - - [31/Oct/2023:09:30:47 +0000] "GET /wp-login.php/ HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.2 - - [31/Oct/2023:09:30:47 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.0" 404 452 "http://docker-app/wp-login.php/" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"

Wordpress-apache

root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:12 +0000] "GET /wp-login.php HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:12 +0000] "GET /wp-includes/js/zxcvbn-async.min.js?ver=1.0 HTTP/1.0" 200 569 "http://docker-app/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:12 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.0" 404 452 "http://docker-app/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:12 +0000] "GET /favicon.ico HTTP/1.0" 404 452 "http://docker-app/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:14 +0000] "GET /wp-login.php/ HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:41:14 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.0" 404 452 "http://docker-app/wp-login.php/" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"

Nginx logs

192.168.121.1 - - [31/Oct/2023:09:30:44 +0000] "GET /wp-login.php HTTP/1.1" 200 1783 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.121.1 - - [31/Oct/2023:09:30:44 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.1" 404 272 "http://docker-app/wp-login.php" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.121.1 - - [31/Oct/2023:09:30:47 +0000] "GET /wp-login.php/ HTTP/1.1" 200 1783 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"
192.168.121.1 - - [31/Oct/2023:09:30:47 +0000] "GET /images/wordpress-logo.svg?ver=20131107 HTTP/1.1" 404 272 "http://docker-app/wp-login.php/" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"

Futher testing it seems anything starting with /wp-login.php will be treated as valid by the php file

root-wordpress-1  | 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"

So we should change the filter to be evt.Parsed.request startsWith '/wp-login.php'

Apache2

``` $ cscli explain --log '172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"' --type apache2 -v line: 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" ├ s00-raw | ├ 🟢 crowdsecurity/non-syslog (+5 ~8) | ├ update evt.ExpectMode : %!s(int=0) -> 1 | ├ update evt.Stage : -> s01-parse | ├ update evt.Line.Raw : -> 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" | ├ update evt.Line.Src : -> /tmp/cscli_explain2143585454/cscli_test_tmp.log | ├ update evt.Line.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-10-31 09:46:41.574811939 +0000 UTC | ├ create evt.Line.Labels.type : apache2 | ├ update evt.Line.Process : %!s(bool=false) -> true | ├ update evt.Line.Module : -> file | ├ create evt.Parsed.message : 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" | ├ create evt.Parsed.program : apache2 | ├ update evt.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-10-31 09:46:41.5748735 +0000 UTC | ├ create evt.Meta.datasource_type : file | ├ create evt.Meta.datasource_path : /tmp/cscli_explain2143585454/cscli_test_tmp.log ├ s01-parse | └ 🟢 crowdsecurity/apache2-logs (+21 ~2) | └ update evt.Stage : s01-parse -> s02-enrich | └ create evt.Parsed.bytes : 2229 | └ create evt.Parsed.rawrequest : | └ create evt.Parsed.referrer : - | └ create evt.Parsed.response : 200 | └ create evt.Parsed.target_fqdn : | └ create evt.Parsed.auth : - | └ create evt.Parsed.http_user_agent : Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0 | └ create evt.Parsed.port : | └ create evt.Parsed.request : /wp-login.php/12312331/123423132 | └ create evt.Parsed.timestamp : 31/Oct/2023:09:43:59 +0000 | └ create evt.Parsed.httpversion : 1.0 | └ create evt.Parsed.verb : GET | └ create evt.Parsed.clientip : 172.18.0.4 | └ create evt.Parsed.ident : - | └ update evt.StrTime : -> 31/Oct/2023:09:43:59 +0000 | └ create evt.Meta.http_path : /wp-login.php/12312331/123423132 | └ create evt.Meta.http_user_agent : Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0 | └ create evt.Meta.log_type : http_access-log | └ create evt.Meta.service : http | └ create evt.Meta.http_status : 200 | └ create evt.Meta.http_verb : GET | └ create evt.Meta.source_ip : 172.18.0.4 ├ s02-enrich | ├ 🟢 crowdsecurity/dateparse-enrich (+2 ~2) | ├ create evt.Enriched.MarshaledTime : 2023-10-31T09:43:59Z | ├ update evt.Time : 2023-10-31 09:46:41.5748735 +0000 UTC -> 2023-10-31 09:43:59 +0000 UTC | ├ update evt.MarshaledTime : -> 2023-10-31T09:43:59Z | ├ create evt.Meta.timestamp : 2023-10-31T09:43:59Z | ├ 🟢 crowdsecurity/geoip-enrich (+9) | ├ create evt.Enriched.Longitude : 0.000000 | ├ create evt.Enriched.ASNNumber : 0 | ├ create evt.Enriched.ASNOrg : | ├ create evt.Enriched.ASNumber : 0 | ├ create evt.Enriched.IsInEU : false | ├ create evt.Enriched.IsoCode : | ├ create evt.Enriched.Latitude : 0.000000 | ├ create evt.Meta.ASNNumber : 0 | ├ create evt.Meta.IsInEU : false | ├ 🟢 crowdsecurity/http-logs (+7) | ├ create evt.Parsed.file_dir : /wp-login.php/12312331/ | ├ create evt.Parsed.impact_completion : true | ├ create evt.Parsed.static_ressource : false | ├ create evt.Parsed.file_frag : 123423132 | ├ create evt.Parsed.file_name : 123423132 | ├ create evt.Parsed.file_ext : | ├ create evt.Meta.http_args_len : 0 | ├ 🟢 crowdsecurity/jellyfin-whitelist (unchanged) | └ 🟢 crowdsecurity/nextcloud-whitelist (unchanged) ├-------- parser success 🟢 ├ Scenarios └ 🟢 crowdsecurity/http-crawl-non_statics ```

Nginx logs

``` $ cscli explain --log '172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0"' --type nginx -v line: 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" ├ s00-raw | ├ 🟢 crowdsecurity/non-syslog (+5 ~8) | ├ update evt.ExpectMode : %!s(int=0) -> 1 | ├ update evt.Stage : -> s01-parse | ├ update evt.Line.Raw : -> 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" | ├ update evt.Line.Src : -> /tmp/cscli_explain3431606629/cscli_test_tmp.log | ├ update evt.Line.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-10-31 09:47:36.221585572 +0000 UTC | ├ create evt.Line.Labels.type : nginx | ├ update evt.Line.Process : %!s(bool=false) -> true | ├ update evt.Line.Module : -> file | ├ create evt.Parsed.message : 172.18.0.4 - - [31/Oct/2023:09:43:59 +0000] "GET /wp-login.php/12312331/123423132 HTTP/1.0" 200 2229 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" | ├ create evt.Parsed.program : nginx | ├ update evt.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-10-31 09:47:36.221609718 +0000 UTC | ├ create evt.Meta.datasource_path : /tmp/cscli_explain3431606629/cscli_test_tmp.log | ├ create evt.Meta.datasource_type : file ├ s01-parse | ├ 🟢 crowdsecurity/nginx-logs (+22 ~2) | ├ update evt.Stage : s01-parse -> s02-enrich | ├ create evt.Parsed.body_bytes_sent : 2229 | ├ create evt.Parsed.remote_addr : 172.18.0.4 | ├ create evt.Parsed.request : /wp-login.php/12312331/123423132 | ├ create evt.Parsed.request_length : | ├ create evt.Parsed.verb : GET | ├ create evt.Parsed.http_referer : - | ├ create evt.Parsed.proxy_upstream_name : | ├ create evt.Parsed.http_user_agent : Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0 | ├ create evt.Parsed.target_fqdn : | ├ create evt.Parsed.http_version : 1.0 | ├ create evt.Parsed.proxy_alternative_upstream_name : | ├ create evt.Parsed.remote_user : - | ├ create evt.Parsed.request_time : | ├ create evt.Parsed.status : 200 | ├ create evt.Parsed.time_local : 31/Oct/2023:09:43:59 +0000 | ├ update evt.StrTime : -> 31/Oct/2023:09:43:59 +0000 | ├ create evt.Meta.log_type : http_access-log | ├ create evt.Meta.service : http | ├ create evt.Meta.http_status : 200 | ├ create evt.Meta.http_user_agent : Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0 | ├ create evt.Meta.http_verb : GET | ├ create evt.Meta.source_ip : 172.18.0.4 | ├ create evt.Meta.http_path : /wp-login.php/12312331/123423132 ├ s02-enrich | ├ 🟢 crowdsecurity/dateparse-enrich (+2 ~2) | ├ create evt.Enriched.MarshaledTime : 2023-10-31T09:43:59Z | ├ update evt.Time : 2023-10-31 09:47:36.221609718 +0000 UTC -> 2023-10-31 09:43:59 +0000 UTC | ├ update evt.MarshaledTime : -> 2023-10-31T09:43:59Z | ├ create evt.Meta.timestamp : 2023-10-31T09:43:59Z | ├ 🟢 crowdsecurity/geoip-enrich (+9) | ├ create evt.Enriched.IsInEU : false | ├ create evt.Enriched.IsoCode : | ├ create evt.Enriched.Latitude : 0.000000 | ├ create evt.Enriched.Longitude : 0.000000 | ├ create evt.Enriched.ASNNumber : 0 | ├ create evt.Enriched.ASNOrg : | ├ create evt.Enriched.ASNumber : 0 | ├ create evt.Meta.ASNNumber : 0 | ├ create evt.Meta.IsInEU : false | ├ 🟢 crowdsecurity/http-logs (+7) | ├ create evt.Parsed.file_dir : /wp-login.php/12312331/ | ├ create evt.Parsed.file_frag : 123423132 | ├ create evt.Parsed.impact_completion : true | ├ create evt.Parsed.file_ext : | ├ create evt.Parsed.static_ressource : false | ├ create evt.Parsed.file_name : 123423132 | ├ create evt.Meta.http_args_len : 0 | ├ 🟢 crowdsecurity/jellyfin-whitelist (unchanged) | └ 🟢 crowdsecurity/nextcloud-whitelist (unchanged) ├-------- parser success 🟢 ├ Scenarios └ 🟢 crowdsecurity/http-crawl-non_statics ```