MS Remote Desktop Gateway, HAProxy și atacurile de tip bruteforce

Salut, prieteni!

Există multiple metode de conectare de acasă la locul de muncă din birou. Una dintre ele este utilizarea Microsoft Remote Desktop Gateway. Acesta este RDP peste HTTP. Nu vreau să discut despre configurarea RDGW, nici să analizez de ce este bun sau rău, să-l considerăm uneltele de acces la distanță. Vreau să vorbesc despre protecția serverului dumneavoastră RDGW împotriva internetului rău. Când am configurat serverele RDGW, m-am preocupat imediat de protecție, în special de protecția împotriva atacurilor de tip bruteforce. Am fost surprins că nu am găsit articole pe internet despre cum se poate face acest lucru. Ei bine, va trebui să facem singuri.

RDGW, în sine, nu are nici o protecție. Da, poate fi expus cu o interfață golită în rețeaua publică și va funcționa perfect. Dar unui administrator corect sau unui specialist în securitate îi va fi greu cu acest lucru. De asemenea, va evita situația de blocare a contului atunci când un angajat neglijent a memorat parola contului de companie pe computerul de acasă și apoi a schimbat parola.

O bună metodă de protejare a resurselor interne împotriva mediului extern sunt diversele proxy-uri, sisteme de publicare și alte WAF. Să ne amintim că RDGW este, totuși, HTTP, deci se sugerează să utilizăm o soluție specializată între serverele interne și internet.

Știu că există soluții avansate precum F5, A10, Netscaler (ADC). Ca administrator al uneia dintre aceste sisteme, pot spune că este, de asemenea, posibil să configurezi protecția împotriva atacurilor de tip bruteforce pe aceste sisteme. Și da, aceste sisteme vor proteja în plus împotriva oricăror inundații de tip SYN.

Dar nu orice companie își poate permite să achiziționeze o astfel de soluție (sau să găsească un administrator pentru un astfel de sistem :), dar poate avea grijă de securitate!

Este posibil să instalați o versiune gratuită HAProxy pe un sistem de operare gratuit. Am testat pe Debian 10, în repositoriul stabil având versiunea haproxy 1.8.19. De asemenea, am verificat versiunile 2.0.xx din repositorul testing.

Configurarea Debian-ului în sine o lăsăm în afara acestui articol. Pe scurt: în interfața publică, blocați totul cu excepția portului 443; în interfața gri – conform politicii dumneavoastră, de exemplu, blocați totul cu excepția portului 22. Deschideți doar ceea ce este necesar pentru funcționare (cum ar fi VRRP pentru IP-ul flotant).

În primul rând, am configurat haproxy în modul SSL bridging (cunoscut și ca modul http) și am activat logging-ul pentru a observa ce se întâmplă în interiorul RDP. Adică, am intervenit în mijloc. Astfel, calea specificată în „toate” articolele despre configurarea RDGateway, /RDWeb, lipsește. Tot ce există acolo sunt /rpc/rpcproxy.dll și /remoteDesktopGateway/. În plus, nu sunt folosite cereri standard GET/POST, ci un tip propriu de cerere RDG_IN_DATA, RDG_OUT_DATA.

Nu mult, dar măcar ceva.

Hai să testăm.

Deschid mstsc, merg pe server, în loguri văd patru erori 401 (neautorizat), apoi introduc utilizator/parolă și primesc răspuns 200.

Dezactivez, repornesc, în loguri văd aceleași patru erori 401. Introduc utilizator/parolă greșită și văd din nou patru erori 401. Exact ce este necesar. Asta vom monitoriza.

Deoarece nu am reușit să determin URL-ul de login și, în plus, nu știu cum să monitorizez în haproxy exact eroarea 401, voi monitoriza (de fapt, voi număra) toate erorile 4xx. Asta este acceptabil pentru a rezolva problema.

Ideea protecției va consta în numărarea erorilor 4xx (pe backend) într-o unitate de timp și, dacă acestea depășesc limita specificată, se vor respinge (pe frontend) toate conexiunile ulterioare de la acel IP pentru o perioadă specificată.

Tehnic, aceasta nu va fi o protecție împotriva atacurilor de tip brute-force, ci o protecție împotriva erorilor 4xx. De exemplu, dacă se solicită frecvent un URL inexistent (404), protecția va funcționa și aici.

Cel mai simplu și funcțional mod de a face asta este să contorizăm pe backend și să respingem dacă apar neobișnuite:

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/desktop.example.com.pem
    mode http
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...

    #creați o tabelă, tip string, 1000 de elemente, expiră după 15 secunde, salvați numărul de erori din ultimele 10 secunde
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    #salvați ip-ul
    http-request track-sc0 src
    #interziceți cu eroare http 429, dacă în ultimele 10 secunde sunt mai mult de 4 erori
    http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
	
	...
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02

Nu este cea mai bună opțiune, să complicăm. Voi contoriza pe backend și voi bloca pe frontend.

Vom trata atacatorul cu brutalitate, ne vom desface conexiunea TCP.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    #creează o tabelă de adrese IP, 1000 de elemente, va expira după 15 secunde, păstrează din contorul global
    stick-table type ip size 1k expire 15s store gpc0
    #ia sursa
    tcp-request connection track-sc0 src
    #respinge conexiunea tcp dacă contorul global >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    #creează o tabelă de adrese IP, 1000 de elemente, va expira după 15 secunde, păstrează numărul de erori timp de 10 secunde
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    #multe erori dacă numărul de erori în 10 secunde depășește 8
    acl errors_too_fast sc1_http_err_rate gt 8
    #marchează atacul în contorul global (crește contorul)
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    #resetează contorul global
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    #ia sursa
    tcp-request content track-sc1 src
    #respinge, marchează ca atac
    tcp-request content reject if errors_too_fast mark_as_abuser
    #permite, resetează semnul atacului
    tcp-request content accept if !errors_too_fast clear_as_abuser
	
    ...
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02

aceleași lucruri, dar politicos, vom returna eroarea http 429 (Prea multe cereri)

frontend fe_rdp_tsc
    ...
    stick-table type ip size 1k expire 15s store gpc0
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc0_get_gpc0 gt 0 }
    ...
    default_backend be_rdp_tsc

backend be_rdp_tsc
    ...
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    acl errors_too_fast sc1_http_err_rate gt 8
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    http-request track-sc1 src
    http-request allow if !errors_too_fast clear_as_abuser
    http-request deny deny_status 429 if errors_too_fast mark_as_abuser
    ...

Verific: pornesc mstsc și încep să tastez parolele în mod aleatoriu. După a treia încercare în 10 secunde sunt respins, iar mstsc arată o eroare. Ce se vede în jurnale.

Explicații. Nu sunt un expert în haproxy. Nu înțeleg de ce, de exemplu
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
permite să facă aproximativ 10 erori înainte de a se activa.

Mă pierd în numerotarea contorilor. Experții haproxy, aș fi bucuros dacă m-ați completa, corecta, îmbunătăți.

În comentarii, puteți sugera alte metode de protecție a RD Gateway, ar fi interesant de studiat.

Referitor la clientul desktop de la distanță Windows (mstsc), merită menționat că acesta nu suportă TLS1.2 (cel puțin în Windows 7), așa că a trebuit să las TLS1; nu suportă cipherele actuale, așa că a trebuit să păstrez vechile.

Pentru cei care nu au înțeles nimic și abia începe să învețe, voi prezenta întreaga configurație.

haproxy.conf

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Locațiile implicite pentru materialele SSL
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Vezi: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        #ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        #ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
        ssl-default-bind-options no-sslv3
        ssl-server-verify none


defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  15m
        timeout server  15m
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http


frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/dektop.example.com.pem
    mode http
    capture request header Host len 32
    log global
    option httplog
    timeout client 300s
    maxconn 1000

    stick-table type ip size 1k expire 15s store gpc0
    tcp-request connection track-sc0 src
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }

    acl rdweb_domain hdr(host) -i beg dektop.example.com
    http-request deny deny_status 400 if !rdweb_domain
    default_backend be_rdp_tsc


backend be_rdp_tsc
    balance source
    mode http
    log global

    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    acl errors_too_fast sc1_http_err_rate gt 8
    acl mark_as_abuser sc0_inc_gpc0(fe_rdp_tsc) gt 0
    acl clear_as_abuser sc0_clr_gpc0(fe_rdp_tsc) ge 0
    tcp-request content track-sc1 src
    tcp-request content reject if errors_too_fast mark_as_abuser
    tcp-request content accept if !errors_too_fast clear_as_abuser

    option forwardfor
    http-request add-header X-CLIENT-IP %[src]

    option httpchk GET /
    cookie RDPWEB insert nocache
    default-server inter 3s    rise 2  fall 3
    server rdgw01 192.168.1.33:443 maxconn 1000 weight 10 ssl check cookie rdgw01
    server rdgw02 192.168.2.33:443 maxconn 1000 weight 10 ssl check cookie rdgw02


frontend fe_stats
    mode http
    bind *:8080
    acl ip_allow_admin src 192.168.66.66
    stats enable
    stats uri /stats
    stats refresh 30s
    #stats admin if LOCALHOST
    stats admin if ip_allow_admin

De ce două servere pe backend? Pentru că astfel se poate asigura redundanța. Puteți configura HAProxy cu două IP-uri publice.

Resursele de calcul: puteți începe cu „două gigabytes, două nuclee, PC de gaming”. Conform wikipedia acesta va fi suficient cu un surplus.

Linkuri:

Configurarea rdp-gateway de la HAProxy
Singura articol pe care am găsit-o, care se ocupă cu forțarea parolelor

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster