MS Remote Desktop Gateway, HAProxy és jelszó: brute force

Barátaim, sziasztok!

Számos módja van annak, hogy otthonról kapcsolódjon az irodai munkaterülethez. Az egyik a Microsoft Remote Desktop Gateway használata. Ez az RDP HTTP-n keresztül. Magát az RDGW beállítását itt nem akarom érinteni, nem akarom taglalni, hogy miért jó vagy rossz, tekintsük a távoli elérési eszközök egyikeként. Arról szeretnék beszélni, hogy megvédje RDGW-kiszolgálóját a gonosz internettől. Amikor beállítottam az RDGW szervert, azonnal aggódni kezdtem a biztonság miatt, különösen a jelszóval való nyers erőszak elleni védelem miatt. Meglepődtem, hogy az interneten egyetlen cikket sem találtam ennek módjáról. Nos, magának kell megtennie.

Az RDGW önmagában nem rendelkezik védelemmel. Igen, csupasz interfésszel ki lehet tenni fehér hálózatra, és remekül fog működni. Ez azonban nyugtalanítja a megfelelő rendszergazdát vagy információbiztonsági szakembert. Ezenkívül lehetővé teszi a fiók blokkolásának elkerülését, amikor egy gondatlan alkalmazott megjegyezte a vállalati fiók jelszavát az otthoni számítógépén, majd megváltoztatta a jelszavát.

A belső erőforrások külső környezettől való védelmének jó módja a különféle proxy-k, közzétételi rendszerek és egyéb WAF-ok. Emlékezzünk arra, hogy az RDGW továbbra is http, akkor már csak egy speciális megoldást kell csatlakoztatni a belső szerverek és az internet közé.

Tudom, hogy vannak menő F5, A10, Netscaler(ADC). Az egyik ilyen rendszer adminisztrátoraként elmondom, hogy ezeken a rendszereken is be lehet állítani védelmet a nyers erő ellen. És igen, ezek a rendszerek megvédenek minden szin áradattól.

De nem minden cég engedheti meg magának, hogy ilyen megoldást vásároljon (és keressen rendszergazdát egy ilyen rendszerhez :), de ugyanakkor a biztonságról is gondoskodhat!

Teljesen lehetséges a HAProxy ingyenes verziójának telepítése ingyenes operációs rendszerre. A Debian 10, haproxy 1.8.19-es verzióját teszteltem a stabil tárolóban. A tesztelési tárhely 2.0.xx verzióján is teszteltem.

Magát a debian beállítását a cikk keretein kívül hagyjuk. Röviden: a fehér felületen zárjon be mindent, kivéve a 443-as portot, a szürke felületen - például a házirendje szerint zárjon be mindent, kivéve a 22-es portot. Csak azt nyissa meg, ami a munkához szükséges (VRRP például lebegő ip-hez).

Először is beállítottam a haproxyt SSL áthidaló módban (más néven http módban), és bekapcsoltam a naplózást, hogy megnézzem, mi történik az RDP-n belül. Úgymond középre kerültem. Tehát az RDGateway beállításáról szóló „minden” cikkben megadott /RDWeb elérési út hiányzik. Csak az /rpc/rpcproxy.dll és a /remoteDesktopGateway/ található. Ebben az esetben a szabványos GET/POST kérések nem használatosak, hanem a saját kéréstípusuk (RDG_IN_DATA, RDG_OUT_DATA) használatos.

Nem sok, de legalább valami.

Teszteljük.

Elindítom az mstsc-t, felmegyek a szerverre, négy 401-es (illetéktelen) hibát látok a naplókban, majd beírom a felhasználónevemet/jelszavamat és látom a 200-as választ.

Kikapcsolom, újraindítom, és a naplókban ugyanazt a négy 401-es hibát látom. Rossz bejelentkezési/jelszót írok be, és újra négy 401-es hibát látok. Erre van szükségem. Ezt fogjuk kifogni.

Mivel nem lehetett meghatározni a bejelentkezési url-t, és emellett nem tudom, hogyan lehet elkapni a 401-es hibát a haproxyban, ezért fogom (valójában nem fogom, hanem számolom) az összes 4xx hibát. A probléma megoldására is alkalmas.

A védelem lényege az lesz, hogy időegységenként megszámoljuk a 4xx hibák számát (a háttérben) és ha ez meghaladja a megadott határt, akkor elutasítunk (a frontenden) minden további kapcsolatot erről az IP-ről a megadott ideig. .

Technikailag ez nem a jelszó brute force elleni védelem lesz, hanem a 4xx hibák ellen. Például, ha gyakran kér egy nem létező url-t (404), akkor a védelem is működni fog.

A legegyszerűbb és leghatékonyabb módja, ha számítunk a háttérre, és jelentünk, ha bármi extra megjelenik:

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

Nem a legjobb megoldás, bonyolítsuk le. A háttérben számolunk, a frontenden pedig a blokkolással.

Durván bánunk a támadóval, és megszakítjuk a TCP-kapcsolatát.

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

ugyanaz, de udvariasan visszaadjuk a http 429-es hibát (Túl sok kérés)

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

Ellenőrzöm: elindítom az mstsc-t és elkezdem véletlenszerűen beírni a jelszavakat. A harmadik próbálkozás után 10 másodpercen belül visszarúg, és az mstsc hibát ad. Ahogy a naplókban is látszik.

Magyarázatok. Messze vagyok a haproxy mestertől. Nem értem miért pl
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
lehetővé teszi, hogy körülbelül 10 hibát kövessen el, mielőtt működne.

Zavarban vagyok a számlálók számozásával kapcsolatban. Haproxy mesterei, örülök, ha kiegészítenek, javítanak, jobbá tesznek.

A megjegyzésekben javasolhat más módszereket az RD Gateway védelmére, érdekes lesz tanulmányozni.

A Windows Remote Desktop Client (mstsc) kapcsán érdemes megjegyezni, hogy az nem támogatja a TLS1.2-t (legalábbis Windows 7-ben), így el kellett hagynom a TLS1-et; nem támogatja a jelenlegi titkosítást, ezért a régieket is elhagynom kellett.

Azok számára, akik nem értenek semmit, csak tanulnak, és már jól akarnak járni, azoknak megadom a teljes konfigurációt.

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

        # 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

Miért van két szerver a háttérben? Mert így lehet hibatűrést kialakítani. A Haproxy lebegő fehér ip-vel is tud kettőt csinálni.

Számítástechnikai erőforrások: kezdheti a „két koncert, két mag, játék PC”-vel. Alapján wikipedia ez elég lesz a tartaléknak.

referenciák:

Az rdp-gateway beállítása a HAProxy-ból
Az egyetlen olyan cikk, amit találtam, ahol a jelszó brutális erőltetésével foglalkoztak

Forrás: will.com

Hozzászólás