MS Remote Desktop Gateway, HAProxy i brute force lozinke

Prijatelji, pozdrav!

Postoji mnogo načina povezivanja od kuće s radnim prostorom u uredu. Jedan od njih je korištenje Microsoft Remote Desktop Gatewaya. Ovo je RDP preko HTTP-a. Ne želim se ovdje doticati samog postavljanja RDGW-a, ne želim raspravljati zašto je dobar ili loš, tretirajmo ga kao jedan od alata za daljinski pristup. Želim razgovarati o zaštiti vašeg RDGW poslužitelja od zlog Interneta. Kad sam postavio RDGW poslužitelj, odmah sam se zabrinuo za sigurnost, posebno zaštitu od grube upotrebe lozinke. Iznenadilo me što na Internetu nisam našao nijedan članak o tome kako to učiniti. Pa, morat ćete to učiniti sami.

Sam RDGW nema nikakve zaštite. Da, može se izložiti golim sučeljem bijeloj mreži i radit će odlično. Ali to će učiniti pravog administratora ili stručnjaka za informacijsku sigurnost nemirnim. Osim toga, omogućit će vam da izbjegnete situaciju blokiranja računa, kada je nemarni zaposlenik zapamtio lozinku za korporativni račun na svom kućnom računalu, a zatim promijenio lozinku.

Dobar način za zaštitu internih resursa od vanjskog okruženja je kroz različite proxyje, sustave za objavljivanje i druge WAF-ove. Prisjetimo se da je RDGW još uvijek http, a onda samo moli za priključivanje specijaliziranog rješenja između internih poslužitelja i Interneta.

Znam da postoje cool F5, A10, Netscaler(ADC). Kao administrator jednog od ovih sustava, reći ću da je i na ovim sustavima moguće postaviti zaštitu od brute force-a. I da, ovi sustavi će vas također zaštititi od bilo kakvog syn flooda.

No, ne može si svaka tvrtka priuštiti kupnju takvog rješenja (i pronaći administratora za takav sustav :), ali u isto vrijeme može se pobrinuti za sigurnost!

Posve je moguće instalirati besplatnu verziju HAProxyja na besplatni operativni sustav. Testirao sam na Debianu 10, haproxy verziji 1.8.19 u stabilnom repozitoriju. Također sam ga testirao na verziji 2.0.xx iz repozitorija za testiranje.

Samo postavljanje debiana ostavit ćemo izvan opsega ovog članka. Ukratko: na bijelom sučelju zatvorite sve osim porta 443, na sivom sučelju - prema vašoj politici, na primjer, također zatvorite sve osim porta 22. Otvorite samo ono što je potrebno za rad (VRRP npr. za floating ip).

Prije svega, konfigurirao sam haproxy u načinu SSL premošćivanja (aka http način) i uključio bilježenje da vidim što se događa unutar RDP-a. Da tako kažem, ušao sam u sredinu. Dakle, /RDWeb staza navedena u “svim” člancima o postavljanju RDGatewaya nedostaje. Sve što je tamo je /rpc/rpcproxy.dll i /remoteDesktopGateway/. U ovom slučaju se ne koriste standardni GET/POST zahtjevi, već se koristi vlastiti tip zahtjeva RDG_IN_DATA, RDG_OUT_DATA.

Ne puno, ali barem nešto.

Idemo testirati.

Pokrenem mstsc, idem na poslužitelj, vidim četiri greške 401 (neovlašteno) u zapisima, zatim unesem svoje korisničko ime/lozinku i vidim odgovor 200.

Isključim ga, ponovo pokrenem i u zapisima vidim iste četiri pogreške 401. Unesem pogrešnu prijavu/lozinku i ponovno vidim četiri pogreške 401. To je ono što mi treba. Ovo ćemo uhvatiti.

Budući da nije bilo moguće odrediti url za prijavu, a osim toga, ne znam kako uhvatiti pogrešku 401 u haproxyju, uhvatit ću (ne zapravo uhvatiti, već prebrojati) sve 4xx pogreške. Također pogodan za rješavanje problema.

Suština zaštite će biti da ćemo brojati broj 4xx grešaka (na backendu) po jedinici vremena i ako prekorači navedeno ograničenje, tada ćemo odbiti (na frontendu) sve daljnje veze s ove ip adrese na navedeno vrijeme .

Tehnički, to neće biti zaštita od brute force lozinke, to će biti zaštita od 4xx pogreške. Na primjer, ako često tražite nepostojeći url (404), zaštita će također raditi.

Najjednostavniji i najučinkovitiji način je računati na backend i prijaviti ako se pojavi bilo što dodatno:

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
    ...

    #создать таблицу, строковую, 1000 элементов, протухает через 15 сек, записать кол-во ошибок за последние 10 сек
    stick-table type string len 128 size 1k expire 15s store http_err_rate(10s)
    #запомнить ip
    http-request track-sc0 src
    #запретить с http ошибкой 429, если за последние 10 сек больше 4 ошибок
    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

Nije najbolja opcija, zakomplicirajmo. Računat ćemo na backend, a blok na frontend.

Prema napadaču ćemo postupiti grubo i prekinuti njegovu TCP vezu.

frontend fe_rdp_tsc
    bind *:443 ssl crt /etc/haproxy/cert/ertelecom_ru_2020_06_11.pem
    mode http
    ...
    #создать таблицу ip адресов, 1000 элементов, протухнет через 15 сек, сохрянять из глобального счётчика
    stick-table type ip size 1k expire 15s store gpc0
    #взять источник
    tcp-request connection track-sc0 src
    #отклонить tcp соединение, если глобальный счётчик >0
    tcp-request connection reject if { sc0_get_gpc0 gt 0 }
	
    ...
    default_backend be_rdp_tsc


backend be_rdp_tsc
    ...
    mode http
    ...
	
    #создать таблицу ip адресов, 1000 элементов, протухнет через 15 сек, сохранять кол-во ошибок за 10 сек
    stick-table type ip size 1k expire 15s store http_err_rate(10s)
    #много ошибок, если кол-во ошибок за 10 сек превысило 8
    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
	
    ...
    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

ista stvar, ali ljubazno, vratit ćemo grešku http 429 (Previše zahtjeva)

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
    ...

Provjeravam: pokrećem mstsc i počinjem nasumično unositi lozinke. Nakon trećeg pokušaja, unutar 10 sekundi me izbaci natrag, a mstsc daje grešku. Kao što se vidi u dnevnicima.

Objašnjenja. Daleko sam ja od haproxy majstora. Ne razumijem zašto npr
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
omogućuje vam da napravite oko 10 pogrešaka prije nego počne raditi.

Zbunjen sam oko numeriranja brojača. Majstori haproxyja, bit će mi drago ako me nadopunite, ispravite, učinite boljim.

U komentarima možete predložiti druge načine zaštite RD Gatewaya, bit će zanimljivo proučavati.

Što se tiče Windows Remote Desktop Client (mstsc), vrijedi napomenuti da ne podržava TLS1.2 (barem u Windows 7), pa sam morao napustiti TLS1; ne podržava trenutnu šifru, pa sam također morao ostaviti stare.

Za one koji ništa ne razumiju, tek uče, a već žele biti dobri, dat ću vam cijelu konfiguraciju.

haproksi.konf

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

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # See: 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

Zašto dva poslužitelja na pozadini? Jer tako možete napraviti toleranciju na greške. Haproxy također može napraviti dva s plutajućim bijelim IP-om.

Računalni resursi: možete početi s "dva koncerta, dvije jezgre, računalo za igranje." Prema Wikipedia ovo će biti dovoljno za rezervu.

reference:

Postavljanje rdp-gatewaya iz HAProxy
Jedini članak koji sam pronašao u kojem su se potrudili nametnuti lozinku

Izvor: www.habr.com

Dodajte komentar