Ներածություն
Առաջին մասում քարտեզում ներկայացրեցինք encrypted SNI (eSNI) մեխանիզմի ընդհանուր նկարագիրը։ Ցուցադրեցինք, թե ինչպես հնարավոր է խուսափել ժամանակակից DPI համակարգերի հայտնաբերումից (բիլայնի DPI-ի և արգելված ՌԿՆ ռուտրեկերի օրինակով), ինչպես նաև հետազոտեցինք այս մեխանիզմի հիման վրա կառուցված նոր տարբերակ՝ դոմեն-ֆրոնթինգը։
Հետաքրքրասիրության համար երկրորդ մասում կանցնենք ավելի գործնական բաների, որոնք օգտակար կլինեն RedTeam մասնագետներին նրանց դժվար աշխատանքում։ Ոգեշնչողականորեն մեր նպատակն այն չէ, որ հասնենք արգելված ռեսուրսների հասանելիության (այսպիսի երանավետ բաների համար ունենք հին, բայց վստահելի VPN)։ Իհարկե, VPN մատուցողներ շատ են, ասած, ցանկացած ճաշակի, գույնի և բյուջեի համար։
Փորձելու ենք կիրառել դոմեն-ֆրոնթինգ մեխանիզմը ժամանակակից RedTeam.tools-ում, ինչպես Cobalt Strike, Empire և այլն, և տալ նրանց լրացուցիչ հնարավորություններ միմիկրելու և ժամանակակից բովանդակության ֆիլտրացման համակարգերից խուսափելու համար։
Վերջին անգամ eSNI մեխանիզմը ներդրելով OpenSSL գրադարանում, հաջողությամբ օգտագործեցինք այն բոլորիս հայտնի curl գործիքի միջոցով։ Բայց միայն curl-ով, ինչպես ասում են, լցոնված չես լինի։ Իհարկե, ցանկանում ենք նման բան իրականացնել բարձր մակարդակի լեզուներով։ Սակայն ցավոք սրտի, արագ որոնումը ցանցում մեզ հիասթափեցրեց, քանի որ eSNI-ի լիարժեք ներդրումը իրականացել է միայն GOLANG-ում։ Դրա արդյունքում, մեր ընտրությունը չի շատ մեծ՝ կամ մենք գրում ենք մաքուր C կամ C++ օգտագործելով վերաձևակերպված OpenSSL գրադարանը, կամ օգտագործում ենք CloudFlare-ից GOLANG-ի առանձին ֆորկ և փորձում ենք մեր գործիքակազմը այնտեղ անցկացնել։ Կարող ենք ևս մեկ տարբերակ առաջարկել, ավելի դասական, բայց միաժամանակ էլ դժվար, այսինքն՝ Python-ում eSNI աջակցություն իրականացնել։ Վերջին հաշվով, Python-ն էլ օգտագործում է OpenSSL https-ի հետ աշխատելու նպատակով։ Բայց այս տարբերակը թողնենք ուրիշի զարգացման համար, իսկ մենք կբավարարվենք GOLANG-ում իրականացմամբ, առավել ևս, որ մեր սիրելի Cobalt Strike-ը լավ գիտի աշխատել երրորդ կողմերի գործիքների միջոցով կառուցված կապի (External C2 channel) հետ՝ այս մասին կներկայացնենք հոդվածի վերջում։
Փորձեք ավելի դժվար…
Go-ում իրականացված գործիքներից մեկը՝ մեր մշակումը, որը ներխուժում է ներքին ցանց՝ տունելլեր , որը, ընդ որում, այս օրերին Microsoft-ի և Symantec-ի կողմից ճանաչվում է որպես շատ վնասակար ծրագրային ապահովում, ուղղված համաշխարհային կայունության խախտմանը…

Շնորհակալություն նորացված վերածնված ծրագրի օգտագործման համար: Բայց այստեղ առաջանում է մի փոքր խնդիր: Ավելորդ չէ նշել, որ rsockstun-ը սկզբում նախատեսում է օգտագործել սինխրոն SSL կապի ալիքը սերվերի հետ: Սա նշանակում է, որ կապը միացնելուց հետո այն պահպանվում է ամբողջ ժամանակային ընթացքում, երբ աշխատում է թունելագործը: Եվ ինչպես հասկանում եք, https պրոտոկոլը մի քիչ չի նախատեսված այսպես աշխատելու համար՝ այն աշխատում է հարց-պատասխան ռեժիմում, որտեղ յուրաքանչյուր նոր http հարցումը գոյություն ունի նոր tcp կապի շրջանակում:
Այդ սխեմայի հիմնական թերությունն այն է, որ սերվերը չի կարող փոխանցել տվյալները հաճախորդին, քանի դեռ հաճախորդը նոր http հարցում չի ուղարկել: Բայց, բարեբախտաբար, այդ խնդիրը լուծելու բազմաթիվ տարբերակներ կան - տվյալների հոսքի միջոցով http պրոտոկոլի միջոցով (ապրանքապես, մենք ինչպես որ կարողանում ենք նայել մեր սիրելի սերիաները և լսել երաժշտությունը https լինակում աշխատող պորտալներից, իսկ տեսանյութ և ձայն փոխանցումը ոչ այլ ինչ է, քան տվյալների հոսք): Մի տեխնոլոգիա, որը էմուլյացնում է լիարժեք tcp կապի աշխատանքը http պրոտոկոլի վրա, կոչվում է վեբ-սոկետներ (WebSockets), որում հիմնական գաղափարը կայանում է լիարժեք ցանցային կապի կազմակերպման մեջ հաճախորդի և վեբ սերվերի միջև:
Մեր հաջողության համար (ուրա!), այս տեխնոլոգիան ակտիվ է բոլոր CloudFlare-ի պլանների վրա և լավ աշխատում է eSNI-ի հետ: Հենց այս տեխնոլոգիան կիրառում ենք, որպեսզի սովորեցնենք մեր թունելագործին օգտագործել դոմեն-ֆրոնտինգ և թաքնվել ժամանակակից DPI-ներից:
Մի փոքր WebSockets-ի մասին
Առաջին հերթին, մենք կարճ և պարզ բառերով կպատմենք վեբ-սոկետների մասին, որպեսզի բոլորը պատրաստակամ լինեն այն, ինչի վրա կաշխատենք:
Վեբ-սոկետների տեխնոլոգիան թույլ է տալիս ժամանակավորապես անցնել http կապից դեպի սովորական տվյալների հոսք ցանցային սոկետի միջոցով, նախորդ tcp կապը չկոտրելով: Երբ հաճախորդը ցանկանում է անցնել վեբ-սոկետին, նա իր http հարցման մեջ ցույց է տալիս մի քանի http գլխիկներ: Ինքնին երկու պարտադիր գլխիկներ՝ Connection: Upgrade և Upgrade: websocket. Նա կարող է նաև պարտադիր նշել վեբ-սոկետի պրոտոկոլի տարբերակը (Sec-Websockset-Version: 13) և նման մի բան, ինչպիսին է base64 կայանա վեբ-սոկետի (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Սերվերը պատասխանեց նրան http 101 Switching Protocols կոդով և նաև սահմանեց Connection, Upgrade և Sec-WebSocket-Accept. Չունենալով տեսանելի գործընթացն անցնելու վրա, ցուցադրված է ներքևում:

Այդպիսով WebSocket կապի ստեղծումը կարելի է համարել ավարտված: Տվյալները ինչպես հաճախորդից, այնպես էլ սերվերից այժմ տրամադրվում են ոչ http, այլ WebSocket գլխիկներով (նրանք սկսվում են 0x82 բիտով): Այժմ սերվերին հարկ չկա սպասել հաճախորդի հարցմանը, որպեսզի փոխանցի տվյալները, քանի որ tcp կապը չի կոտրվում.
Գոլանգում վեբ-սոկետների հետ աշխատելու համար մի քանի գրադարաններ կան։ Դրանցից ամենահայտնիներն են և ստանդարտ ։ Մենք կօգտվենք վերջինից, քանի որ այն ավելի պարզ է, ավելի փոքր և աշխատում է, ինչպես ասում են, մի փոքր ավելի արագ։
Հ Pe լ մ Client rsockstun-ում մենք պետք է փոխենք net.dial կամ tls.dial զանգվածները համապատասխան WebSocket զանգվածներով:


Մենք ցանկանում ենք մեր թունելերը օգնել, որպեսզի նրաի Client մասը կարողանա աշխատել ինչպես ուղիղ ssl միացման, այնպես էլ WebSocket արձանագրության միջոցով։ Այս համար մենք կստեղծենք առանձին ֆունկցիա func connectForWsSocks(address string, proxy string) error {…} հին аналогյան connectForSocks() և այդկերպ այն կօգտագործվի WebSocket-ների հետ աշխատելու համար այն դեպքում, եթե սերվարի հասցեն, որը նշվել է Client-ը գործարկելու ժամանակ, սկսվում է ws: կամ wss: (Secure WebSocket-ի դեպքում):
Թունելի սերվերային մասի համար նույնպես մենք կստեղծենք առանձին ֆունկցիա WebSocket-ների հետ աշխատելու համար։ Այդ մեջ կստեղծվի http դասի օրինակ և կհիշվի http միացման մշակում (wsHandler ֆունկցիան):

Պաշտպանության ողջ տրամաբանությունը (հաճուսականությունը, հաճախականությունը և yamux նստաշրջանի ավարտը) մենք տեղադրելու ենք WebSocket միացման մշակումում:

Հավաքում ենք նախագծումը, սկսում ենք սերվերային մասը:
.\/rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rdԵվ հետո Client մասում:
.\/rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rdԵվ ստուգում ենք աշխատելու ընդհանուր հաշիվը:


Մոտեցանք անվան-ջերմացման
Վեբ-սոկետների մասին կարծես թե հասկացանք։ Այժմ անցնենք անմիջապես eSNI ու անվան-ջերմացման կերպով։ Ինչպես արդեն նշվեց նախկինում, DoH-ի և eSNI-ի աշխատանքների համար անհրաժեշտ է ունենալ հատուկ գոլանգի ճյուղ ։ Մենք մի ճյուղ ենք որոնում eSNI (pwu/eSNI) աջակցությամբ։
Կլոնում ենք այն մեր տեղական համակարգին կամ ներբեռնում և բաշխում համապատասխան zip-ը:
git clone -b pwu/eSNI https://github.com/cloudflare/tls-tris.gitԵվ ապա պետք է փոխելու GOROOT կատալոգը, փոխելով համապատասխան ֆայլերը ամբողջացած ճյուղից և այն տեղադրելով որպես անպատասխանատու։ Դրանից ազատելու համար CloudFlare-ի տղերքը պատրաստել են հատուկ սցենար – _dev/go.sh։ Պարզապես գործարկեք այն։ Սցենարը միասին makefile-ի հետ բոլորը ինքնին կանեն։ Հետաքրքրության համար կարող եք մտնել makefile-ի մեջ ավելի մանրամասների համար։
Սցենարի ավարտից հետո, նախագծի կոմպիլացիայի ժամանակ, մենք պետք է GOROOT-ի համար նշված լինելուց է տեղական կատալոգ, որ պատրաստվել է սցենարով։ Մեր դեպքում դա այսպես է երևում՝
GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….ԵՎ հետո, մենք պետք է իրականացնել թունելում eSNI հանրային բանալիների հարցման և պարսիկման ֆունկցոնալությունը անհրաժեշտ «անգամ» համար։ Մեր դեպքում դրանք CloudFlare-ի առջևի սերվերի eSNI հանրային բանալիներն են։ Այս համար մենք կստեղծենք երեք ֆունկցիաներ՝
func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)Ֆունկցիաների անվանումները, իրապես, ինքնին խոսում են իրենց մասին: Մեզ հետաքրքրող բովանդակությունը կստանանք esni_query.go ֆայլից՝ tls-tris-ի կազմում: Առաջին ֆունկցիան ստեղծում է ցանցային փաթեթ DNS սերվերին CloudFlare հետ հարցման համար, օգտագործելով DoH (DNS-over-HTTPS) պրոտոկոլը, երկրորդը՝ վերլուծում է հարցման արդյունքները և ստանում է ձեզ հետաքրքրող հանրային բանալիի արժեքները, իսկ երրորդը առաջին երկուի համար տարողություն է:
Հաջորդ քայլով, մենք ավելացնում ենք մեր նոր ստեղծված WebSocket միացման ֆունկցիան connectForWsSocks eSNI բանալու հարցման ֆունկցիոնալությունը դոմենի համար: Ստորև դիրքավորվող սերվերի մասում մենք կարգավորում ենք TLS պարամետրերը, և սահմանում ենք անապատ «փախցրած դոմենի» անունը:

Արժե նշել, որ սկզբնական շրջանում tls-tris ճյուղը նախատեսված չի եղել դոմեն-ֆրոնտինգի օգտագործման համար: Ուստի, այստեղ ուշադրություն չի դարձվել սերվերի կեղծ անունին (client-hello փաթեթում ուղարկվում է դատարկ serverName դաշտ): Սա շտկելու համար हमें TlsConfig կառուցվածքին լրացնել համապատասխան FakeServerName դաշտը: Ստանդարտ ServerName դաշտը չենք կարող օգտագործել, քանի որ դա օգտագործում է tls-ի ներքին մեխանիզմները, և եթե այն տարբերվի սկզբնականից, tls-ծառայությունը կավարտվի սխալով: TlsConfig կառուցվածքի նկարագրությունը պարունակվում է tls/common.go – հենց այն ֆայլն է, որը պետք է շտկել:


Ավելին, մենք պետք է փոփոխություններ կատարենք tls/handshake_client.go, որպեսզի օգտագործենք մեր FakeServerName դաշտը TLS ձեռնարկման ձևավորման ժամանակ:

Այստեղ ամեն ինչ ավարտվել է! Կարող ենք կոմպիլացնել նախագիծը և ստուգել աշխատանքը: Բայց նախքան ստուգումը, անհրաժեշտ է կարգավորել CloudFlare հաշիվը: Ինչպես ասել, կարգավորել – պարզապես ստեղծել հաշիվ CloudFlare-ում և կապել ձեր դոմենը դրան: բոլոր գործառույթները, որոնք կապված են DoH, WebSocket և ESNI, CloudFlare-ում ներառված են ըստ ենթադրյալի: Ինչ-որ բան DNS ռեկորդները թարմացվեն, կարող եք ստուգել դոմենի աշխատանքը՝ կատարելով eSNI բանալու հարցումը:
dig +short txt _esni.df13tester.info 
Եթե դուք տեսնում եք իր այսպիսին ձեր դոմենի համար՝ նշանակում է ամեն ինչ պատրաստ է և կարող եք շարունակել թեստավորումը:
Ներկայացնում ենք Ubuntu VPS, օրինակ, DigitalOcean-ում: P.S. Մեր դեպքում նորակոչված պրովայդերի IP-հասցեն հայտնվել է ՌԿՆ-ի սև ցուցակում: Ուստի, մի զարմանաք, եթե ձեզ նման բան պատահի: مجبور եղանք օգտագործել VPN, որպեսզի մուտք գործենք մեր VPS:
Նկուղում սեղմեք արդեն կոմպիլացված rsockstun-ը VPS-ում (այսպիսով, այս մեկ այլ գեղեցկություն է Golang-ի, – դուք կարող եք կոմպիլացնել ծրագիրը ձեր մոտ և գործարկել ցանկացած Linux-ում, պահպանելով միայն համակարգի գծայինությունը) և մեկնարկեք սերվերի մասը:

Ապա գործարկեք հաճախորդի մասը:

Ինչպես տեսնում ենք, հաճախորդը հաջողությամբ միացել է սերվերին CloudFlare-ի առաջատիպի միջոցով՝ օգտագործելով WebSocket: Թունելի աշխատանքի ստուգելու համար կարող եք կատարել curl հարցում՝ локальное socks5-ի միջոցով, որը բացվել է սերվերում:

Այժմ посмотрим, что же DPI видит в канале связи:

Նախ туннельը, օգտագործելով DoH մեխանիզմը, դիմում է Cloudflare-ի DNS սերվերին eSNI ստանձնման բանալու համար նշանակման դոմենի (փաթեթներ №1-19) վրա, ապա դիմում է ֆրոնտենդ սերվերին և ստեղծում TLS կապ, որի ժամանակ փակվում է ինքնուրույն դոմենի մասին։ (այս արժեքը պdefault է, երբ հաճախորդը запускается без указанного фейкового домена). Ձեր մձեն կեղծ դոմենքը հաստատելու համար անհրաժեշտ է օգտագործել -fronfDomain պարամետրը:
![]()

Այժմ ևս մեկ պահ։ Փոխանցման կարգավորումների տակ CloudFalre-ում, ըստ բաշխված կարգուկանոն, կարող է լինել ներդրված Flexibile SSL-համակարգ։ Սա նշանակում է, որ HTTPS դիմումները Cloudflare-ի ֆրոնտենդ սերվերներին հաճախորդներից կուղղորդվեն առանցEncrypt, כלומר HTTP բովանդակությանը։ Այդ իսկ պատճառով մենք տալով ալգորիթմի սերվերային մասը non-ssl ռեժիմում ( -listen ws:0.0.0.0) փոխանակ ( -listen wss:0.0.0.0)։

Լրիվ Encrypt ռեժիմի անցնելու համար ձեզ անհրաժեշտ է ընտրել Full, կամ Full (strict) նման դեպք, երբ սերվերի վրա կա իրական վկայական։ Ռեժիմը փոխելուց հետո մենք կարող ենք ընդունել զանգվածներից օգտվող CloudFlare HTTPS՝ պրոտոկոլի միջոցով։ Մի забудьте սեղմել self-signed վկայական սերվերի համար tunneler-ի մասում:

Հետաքրքրասեր ընթերցողը հարցնելու է. «Ինչպես է Windows-ից հաճախորդը? Նրա համար հիմնականում туннելերը կիրառվում են корпоративные մեքենաներում և սերվերներում, իսկ այնտեղ, սովորաբար, միշտ է Windows։ Ինչպես կարող եմ compile tunneler-ը Windows-ի համար, և ավելի քան այդ специфիկ TLS стекով?» Եվ այժմ մեզ ներկայացնենք ևս մեկ հատկությունը, որը ցույց է տալիս, թե որքան հարմար է Go լեզուն։ Compile-ով Windows-ի համար ուղղակի Ուղարկել է Kali-ից, պարզապես ավելացնելով GOOS=windows պարամետրը:
GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Կամ 32-bit տարբերակը:
GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Այո՛: Եվ ավելին ոչ մի խնդիր չկա։ Սա իրականում աշխատում է!

Compile գործիքները –w և –s պահանջվում են, որպեսզի հեռացվեն ավելորդ Կապտուկներից, ինչի արդյունքում հասնում է փաթեթապատկի կես ավելիկ մետաղների: Դրանք ավելի հետո կարող են նվազվել UPX-ի միջոցով, որպեսզի նվազեցնելու համար չափի:
Վերջում
Այս հոդվածում մենք, ցուցադրելով Go լեզվով պաշտպանության մանրադրամով, տեսակարար ընդգրկում ենք նոր տեխնոլոգիան՝ դոմեն-ֆրոնտինգ, որը կիրառվում է TLS 1.3 պրոտոկոլի հիմնաբառերի հաճելի առանձնահատկությունների վրա՝ համապատասխանաբար հարակից գործիքավորման որակաբառ։ Դա կարելի է երեք Անցկացնել Go ծրագրերի ներդնողներին պարզել CloudFlare սերվերներով, ինչպես օրինակ — հայտնի C2, կամ ստիպել CobaltStrike Beacon-ին օգտագործել eSNI դոմեն-ֆրոնտինգ՝ Teamserver-ի միջոցով , որ ներառում է Go լեզվով, կամ էլ սովորական C++ վրա՝ պրոպատչած OpenSSL տարբերակի օգտագործմամբ, որի մասին խոսել ենք հոդվածի նախորդ մասում։ Ընդհանուր առմամբ, երևակայության սահմանները գոյություն չունեն։
Տունելային և CloudFlare-ով իրականացվող օրինակն առկա է որպես կոնցեպտ, և դեռևս դժվար է ասել նման կառուցվածքի հեռանկարների մասին։ Այս պահին eSNI-ի աջակցություն ունի միայն CloudFlare, և ըստ գաղափարի, ոչինչ չի խանգարում նրանց հանել նման ֆրոնտինգը և, օրինակ, խզել tls-հարցումները, եթե SNI-ն և eSNI-ն չեն համընկնում։ Ընդհանուր առմամբ, ապագան ցույց կտա։ Բայց այս պահին «kremlin.ru» իր ապաստան ազգի տակ աշխատելու հեռանկարը բավականին գրավիչ է թվում։ Ոչ թե այնպես չէ՞։
Թարմացված միջավայրային կոդը, ինչպես նաև հավաքված executable exe-ֆայլերը տեղադրված են նախագծի առանձին ճյուղում . Տունելային բոլոր հնարավոր խնդիրների վերաբերյալ լավ կլինի գրեք issue GitHub-ի նախագծի էջում։
Ընտանիք: habr.com
