MS Remote Desktop Gateway, HAProxy и брутфорс атака

Приятели, здравейте!

Съществуват множество начини за свързване от вкъщи към работното място в офиса. Един от тях е да се използва Microsoft Remote Desktop Gateway. Това е RDP върху HTTP. Не искам тук да разглеждам настройката на самия RDGW, нито да обсъждам защо е добър или лош, нека го разглеждаме като един от инструментите за отдалечен достъп. Искам да говоря за защитата на вашия RDGW сървър от злонамерен интернет. Когато настроих RDGW сървърите, веднага се притесних за защитата, особено за защитата срещу подбора на пароли. Изненадах се, че не намерих статии в интернет за това как може да се направи. Е, ще трябва да се справя сам.

Самият RDGW няма никаква защита. Да, може да бъде изложен с гол интерфейс в откритата мрежа и ще работи прекрасно. Но на добрия администратор или специалист по информационна безопасност това ще му създаде неудобство. Освен това ще избегнете ситуацията с блокирането на акаунта, когато небрежен служител е запомнил паролата на корпоративния акаунт на домашния си компютър и след това е сменил паролата си.

Добър начин за защита на вътрешните ресурси от външната среда са различни проксита, системи за публикуване и други WAF. Нека си припомним, че RDGW все пак е http, така че направо извиква да се включи специализирано решение между вътрешните сървъри и интернет.

Знам, че има страхотни F5, A10, Netscaler(ADC). Като администратор на една от тези системи, ще кажа, че е възможно да се настрои защита от подбор и на тези системи. И да, тези системи ще ви защитят и от всякакъв син флуд.

Но не всяка компания може да си позволи да закупи подобно решение (и да намери администратор за такава система :), а за безопасността може да се погрижи!

Напълно възможно е да се инсталира безплатна версия на HAProxy на безплатна операционна система. Тествах на Debian 10, в стабилния репозиторий версия haproxy 1.8.19. Също така проверявах на версии 2.0.xx от testing репозитория.

Настройката на самия Debian оставяме извън обсъждането. Накратко: на белия интерфейс да закрием всичко, освен порта 443, на сивия интерфейс — според вашата политика, например също да закрием всичко, освен порта 22. Да отворим само това, което е необходимо за работа (например VRRP за плаващ ip).

На първо място конфигурирах haproxy в режим SSL bridging (т.е. режим http) и активирах логовете, за да видя какво става в RDP. С други думи, влезнах в средата. Но посоченият в „всички“ статии за настройка на RDGateway път /RDWeb липсва. Всичко, което има, е /rpc/rpcproxy.dll и /remoteDesktopGateway/. При това не се използват стандартни GET/POST заявки, а свой тип заявка RDG_IN_DATA, RDG_OUT_DATA.

Не много, но поне нещо.

Нека тестваме.

Стартирам mstsc, отивам на сървъра, в логовете виждам четири грешки 401 (неупълномощен), след което въвеждам логин/парола и виждам отговор 200.

Изключвам, стартирам отново, в логовете виждам същите четири грешки 401. Ввеждам грешен логин/парола и отново виждам четири грешки 401. Това е, което ни трябва. Ще го улавяме.

Тъй като не успях да определя URL адреса за вход, и освен това не знам как да улавям точно грешка 401 в haproxy, ще улавям (всъщност няма да улавям, а ще броя) всички грешки 4xx. И това ще е приемливо за решаване на задачата.

Същността на защитата ще се състои в това, че ще броим броя на грешки 4xx (на backend) за единица време и ако той надхвърли зададения праг, то ще отказваме (на frontend) всички по-нататъшни връзки от този IP за указаното време.

Технически, това няма да бъде защита от подбиране на паролата, а защита от грешки 4xx. Например, ако често се запитва не съществуващ URL (404), то защитата също ще сработи.

Най-простият и работещ начин е на backend да се броят и отблъскват, ако нещо излишно се е появило:

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

Не е най-добрият вариант, ще усложним. Ще броим на backend, а блокираме на frontend.

С атакуващия ще постъпим грубо, ще прекъсваме tcp връзката.

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

същата работа, но учтиво, ще връщаме грешка http 429 (Too Many Requests)

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

Проверявам: стартирам mstsc и започвам произволно да въвеждам пароли. След третия опит за 10 сек. ме изритва, а mstsc издава грешка. Както се вижда в логовете.

Пояснения. Аз не съм експерт по haproxy. Не разбирам, например, защо
http-request deny deny_status 429 if { sc_http_err_rate(0) gt 4 }
позволява около 10 грешки, преди да сработи.

Заплетен съм в номерирането на броячите. Мастери на haproxy, ще се радвам, ако ме допълните, поправите или направите по-добре.

В коментарите можете да предложите други методи за защита на RD Gateway, ще бъде интересно да се проучат.

Що се отнася до клиента за отдалечен работен плот на Windows (mstsc), трябва да се отбележи, че той не поддържа TLS1.2 (поне в Windows 7), затова трябваше да оставя TLS1; не поддържа актуални шифри, затова също така трябваше да оставя старите.

За тези, които нищо не разбираха и вече искат да правят добре, ще представя цялата конфигурация.

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

        # Локации на SSL материали по подразбиране
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Вижте: 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

Защо два сървъра на бекенда? Защото така може да се направи отказоустойчивост. Haproxy може да настроите и два с плаващ бял IP.

Изчислителните ресурси: може да започнете с "две гига, два ядра, игрови ПК". Според уикипедия това ще бъде повече от достатъчно.

Линкове:

Настройка на rdp-gateway от HAProxy
Единствената статия, която успях да намеря, която се занимава с опити за разбиване на паролата

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster