Skip Ribbon Commands
Skip to main content

Ondrej Sevecek's Blog

:

Engineering and troubleshooting by Directory Master!
Ondrej Sevecek's Blog > default
prosinec 07
Hitovka letošního roku aneb hvězdička v DNS

Nedávno jsme se shodli s kamarády, že tohle si zaslouží nominaci na IT hit roku 2016.

To jste věděli, že v DNS, už od Windows 2003 jde udělat hvězdičkový záznam, který potom bere všechny dotazy na jména, která nejsou explicitně vyjmenovaná? Může to být A i CNAME záznam. Podívejte se na obrázek:

Schválně jsem tam dal zvláštní IP adresu .55, aby to bylo vidět v nslookup. Nápříklad záznam anything.gopas.virtual explicitně neexistuje. Ale když se na něho zeptáte, tak vám to odpoví s IP adresou .55 jako kdyby existoval:

Funguje to i pro CNAME záznamy. Jenom nezapomeňte do jeho cílové hodnoty na konec dát tečku (.), ať si to tam samo nepřikládá primary DNS suffix:

Věděli jste o tom? Od kdy? Je to tam skoro od jakživa a přitom o tom není nikde žádná dokumentace. V podstatě okamžik, kdy zjistíte, že to existuje je, když po vás SharePoint App Catalog chce, abyste tohle v DNS použili.

Hezké ne?

listopad 26
Bleskový a ověřený postup opravy AD lingering objects a Kerberos autentizace

Dneska jsem na konferenci ms-fest prezentoval svoji léty vyzkoušenou, ověřenou a vyladěnou metodu, jak opravit Active Directory (AD) replikaci (replication) potom, co řadič(e) domény přešly tombstone lifetime a vznikly lingering objecty. O této situaci jste zřejmě už slyšeli. Jenže problém je prakticky vždycky kombinován s problémem rozhozených hesel počítačových účtů řadičů domén.

Každý řadič domény (DC - domain controller) si totiž mění heslo svého účtu (machine password) každých 30 dnů, stejně jako libovolný jiný členský stroj domény. Naproti tomu tombstone lifetime je obvykle 180 dnů. Pokud si řadič sám sobě změní heslo dvakrát a během té doby se to nedokáže zreplikovat, tak přestane fungovat Kerberos. Tzn. už po 30 dnech možná, po 60 dnech zaručeně. Takže tombstone lifetime a lingering objects je obvykle až druhý následek rozházeného času.

Důvody pro rozházený čas DC:

  • nějaká DC se prostě už dlouho neviděla - tento důvod není už dneska tak obvyklý, jako ten druhý
  • máte jedno, nebo více DC, na nějakém cloudu a oné virtualizaci se rozhodí čas a posune to čas na všech virtuálkách - to je dneska, jak pozoruji, velmi běžné (chyba obsluhy, chyba hardware, baterka, ...)

To co nastane mezi domain controllery je totální rozklad. Přestávají replikovat a ověřovat jak sebe, tak uživatele a počítače. V případě ověřování klientů to je chaticky podle toho, které DC si kdo vybere. Jsou to chyby jako třeba:

The directory service cannot replicate with this server because the time since the last replication with this server has exceeded the tombstone lifetime

Event Id: 2042
Message: It has been too long since this machine last replicated with the named source machine. The time between replications with this source has exceeded the tombstone lifetime. 

Kerberos error: The target principal name is incorrect
error: 2148074274 = 0x80090322 = SEC_E_WRONG_PRINCIPAL

The Kerberos client received a KRB_AP_ERR_MODIFIED error from the server.
The target name used was LDAP/._msdcs. This indicates that the target server
failed to decrypt the ticket provided by the client.

Na opravu těchto dvou problémů existují standardní postupy. Problém je v jejich praktickém využití. Pokud nevíte přesně jak, bude vám oprava trvat minimálně několik hodin, ne-li dnů, a navíc to ještě neuděláte správně.

Po zkušenostech jsem si vypracoval ideální postup, který je nejrychlejší možný, spolehlivý a obecně funkční. Dneska jsem takto opravil pět DC během cca 40 minut, a to jsme u toho ještě vysvětloval.

Celý postup je na screenshotech a s popisky v následující prezentaci:

Fastest ever steps to repair tombstone lifetime, lingering objects and Kerberos machine passwords

Základní vysokoúrovňové kroky

Detailní návod je v prezentaci. Pro shrnutí jen nejdůležitější body. Prostě nepřemýšlejte a udělejte všechno na všech řadičích:

  • nejprve opravte čas
  • vytvořte si nějaký samostatný DNS server - něco nainstalujte, nebo použijte existující, ale potřebujete DNS server, který není na žádném AD DC, aby na tom nefunkčním AD nebyl závislý. Do tohoto DNS serveru nasměrujte všechna DC. Budete mít jen jeden DNS server a nebudete záviset na replikaci, která nefunguje
  • vypněte všechny Kerberos Key Distribution Center (KDC) služby, a nechejte si jen jednu - každé KDC používá svůj místní AD. Je to stejné jako s tím DNS serverem. Pokud nefunguje AD a nereplikuje, každé KDC potom vydává jinak špatné Kerberos tikety a celé se to chaotizuje. Stejně jako jedinný DNS server budeme mít tedy jen jediný KDC.
  • vyresetujete hesla počítačových účtů pomocí netdom resetpwd - tohle rozjede ověřování
  • vyčistíte lingering objecty - musíte to ale udělat správně a úplně. V prezentaci je na to skript. Musíte čistit každé DC oproti každému a navíc po jednom všechny AD partition (naming context).
  • no a nakonec pustíte replikaci
  • a nezapomenete to zase uvést do původního stavu - vrátit DNS servery a vypnout registrovou hodnotu

Tak ať slouží!

listopad 22
Vylepšené zásady hesel, neboli 4of4

Rychlé odkazy:

O co jde

Kvalita hesel uživatelů Active Directory (AD) je dlouhodobě na relativně špatné úrovni. Jako každá hesla :-) Běžně se používají hesla typu SlovoČíslo, například Cervenec2016, což je krásný příklad hesla, které je imunní i proti časté změně :-) Obecně osmdesát procent hesel na světě je pouze alfanumerických.

Máme k dispozici zásady hesel, které by to mohly vylepšit. AD umožňuje vynutit minimální délku hesla. Délka hesla větší než 10 znaků pěkně odstaví krátká slova a krátká čísla. Co se týče možných znaků (komplexnost), AD umí vynutit pouze politiku tři ze čtyř (3of4). To znamená, že ze čtyř skupin znaků malá/velká/čísla/ostatní je potřeba mít v hesle alespoň tři skupiny.

Udělal jsem si tedy vlastní password filter, který vynutí v hesle zásadu 4of4. Heslo musí obsahovat jak alespoň jedno velké a jedno malé písmeno z US-ASCII, tak i jednu cifru a jeden speciální znak z US-ASCII. Ostatní znaky mohou být libovolné, klidně národní unicode.

Speciálních znaků je řekněme plus/mínus dvacet. Pokud musí být heslo dlouhé 8 znaků, zvýší to počet hesel 20^8 krát, což je 25 600 000 000 krát. I pokud bychom uvažovali pouze speciální znaky z numerické klávesnice, kterých je jen 5, bude to i tak lepší alespoň 390 000 krát. U hesla desetiznakového je vylepšení dokonce 9 765 625 krát.

Fungování password filter obecně

Password filter je DLL knihovna, která se zapíše do registrů a kterou si systémový proces LSASS načte při svém startu, tedy při startu počítače. LSA (local security authority) je známá součástka, ve které běží celé AD, nebo SAM (lokální security accounts manager). Password filter obsahuje dvě rutiny, které LSA zavolá v okamžiku, kdy přijímá změnu (change password) nebo i nastavení (reset password) hesla.

Úkolem této DLL knihovny je heslo zkontrolovat a říct, jestli je ok, nebo jeho podmínky nesplňuje. Password filter dostává pouze nové heslo a dostává ho v plné formě, tedy ne ve formě hash. Hash se spočítá a v okamžiku následného uložení do AD, nebo SAM databáze. Password filter se používá také pro identity integration, jako součástka, která kvalitu hesla v podstatě nemusí kontrolovat, ale odesílá heslo do nějakých integrovaných systémů, aby se zajistil alespoň reduced-sign-on (RSO), když už nelze použít rovnou single-sign-on (SSO).

Můj filter pouze kontroluje heslo na politiku 4of4 a nikam ho neodesílá :-)

Knihovnu filteru je nutno nakopírovat do adresáře System32 a zapsat do registrové hodnoty:

HKLM\System\CurrentControlSet\Control\LSA
Notification Packages = MULTI_SZ

Pokud to chcete pro Active Directory, tak tuto operaci musíte provést na každém DC (domain controller) řadiči domény. Každý klient využívá pro změnu hesla jen svoje nejbližší DC a politika by se tedy neuplatňovala rovnoměrně. Filtr je potřeba umístit ideálně hned jako první hodnotu. Kdyby byly v seznamu nějaké další filtry právě kvůli IdM, tak by mohly odeslat heslo k integraci ještě dříve, než by následně bylo odmítnuto kvůli nedostatečné složitosti.

Instalace mého SAPHA password filteru

Instalační ZIP soubor filtru pro 64bitové systémy je ke stažení zde. Obsahuje to jak jeho DLL, tak i install.bat a uninstall.bat. Někam si to vybalte, můžete i do síťového sdíleného adresáře a jenom spusťte INSTALL.BAT. Ono se to samo nainstaluje. UNINSTALL.BAT slouží celkem logicky k odinstalaci. Snažil jsem se to udělat extrémně odolné, takže pokud se objeví při instalaci jakákoliv nesrovnalost, instalace nedoběhne a zahlásí chybu.

Po instalaci musíte počítač restartovat, což vám prográmek sám nabídne.

Pro zavedení do AD musíte instalaci provést na všech řadičích domény a musíte je restartovat. Nemusíte to udělat všude naráz, ani nemusíte všechny naráz restartovat (to ani nedělejte). Jenom dokud nebude na všech DLL zavedena, tak některá DC budou vyžadovat 4of4, zatímco jiná ne a pojedou při starém 3of4.

Můžete to taky nasadit i na workgroupové počítače. Tam to funguje stejně, ale přirozeně jen pro lokální SAM účty. Takže to na ostatních doménových počítačích instalovat nemusíte, pokud vám ta politika stačí pro účty v Active Directory.

Dávka INSTALL.BAT má ještě parametr -silent, kdybyste si to náhodou chtěli instalovat nějak automaticky, například pomocí Group Policy Preferences, nebo System Center.

Upozorňuji, že to je jenom 64bitová verze, takže pokud máte ještě nějaká 32bitová DC, tak mi napiště, pošlu vám 32bitovou verzi.

Support je mailem, je zdarma. Pokud chcete nějaké úpravy nebo vylepšení, ozvěte se, domluvíme se na ceně. Nebojte se, je to docela robustní, pády LSA jsem nezaznamenal. Pokud se knihovna nezavede, ani to nepoznáte, LSASS jede prostě dál. Snažil jsem se použít co nejrychlejší algoritmus, takže by to nemělo nijak omezovat výkon.

 

Zvýšená ochrana LSA knihoven

Existuje ještě jeden problém při instalaci password filteru. Jmenuje se to Additional LSA Protection (https://technet.microsoft.com/en-us/library/dn408187(v=ws.11).aspx) - neboli LSASS running as a protected process. Pokud to máte v registrech zapnuté (což nejspíš nemáte), LSASS vyžaduje, aby byl password filter správně digitálně podepsaný.

Jestli máte ochranu zapnutou poznáte podle následující registrové hodnoty - pokud je tam hodnota 1 tak je LSASS chráněn:

HKLM\System\CurrentControlSet\Control\LSA
RunAsPPL = DWORD = 1

Můj filter prozatím s touto ochranou nefunguje, protože není digitálně podepsán. Levné code signing certifikáty od StartSSL prozatím zmizely a protože tuto ochranu nikdo moc nepoužívá, nechce se mi platit 180 EUR ročně a certifikát od jiné CA.

Ptal jsem se a StartSSL support tvrdí, že do 14 dnů by měli zase začít pracovat, tak uvidíme.

listopad 08
Zkoušení hesel na RDP

Moc pěkná otázka dorazila, rád odpovídám rovnou veřejně, protože to je velmi obecný problém:

Včera jsem si všiml, že jsme pod brutforcem nějakých hackerů z Ruska. Útočili nám na Remote Desktop Connection broker (3389). Dělali to pěkně pomalu a opatrně, už jsme to dost minimalizovali. Odkážete mě na nějaké dražší hardwarové/softwarové řešení nebo toto spíš navrhujete řešit jiným způsobem? Resp. nechci odkazovat na nějaký konkrétní produkt, spíš mě zajímá Váš postoj k tomuto. Třeba mi řeknete, že po dobrém nastavení hesel a oprávnění to řešit nemusíme vůbec.

Odpověď

Z toho, jak to popisujete, se dá vyhodnotit, že zřejmě máte otevřen přímý přístup přes TCP 3389 z internetu a někdo zkoušel online hesla na nějaké loginy.

Nejprve se zabývejme loginy. Byly to nějaké vaše skutečné existující loginy, nebo to vždycky selhalo na tom, že to nebyl správný login? To poznáte například na tom RDP serveru v logu z Failure Audit události typu Logon. Bude v tom chyba 0xC000006D (což je obecně STATUS_LOGON_FAILURE), ale také tam bude přesnější důvod - buď 0xC000006A (STATUS_WRONG_PASSWORD), nebo právě 0xC0000062 tedy STATUS_INVALID_ACCOUNT_NAME.

Pokud útočník nezná ani loginy, tak je jasné, že to je úplný cizinec. Pokud loginy zná, tak se ptejme, jestli to jsou nějaké známé loginy, jako třeba administrator a nestálo by za to tyto loginy buď přejmenovat, nebo určitě rovnou i zakázat.

Pokud by útočník znal všechny vaše loginy, mohl by vám je takto dokonce vzdáleně všechny pozamykat. Nemusí vůbec zkoušet hesla s cílem je uhádnout. Prostě jenom na každý účet bouchne několikrát, aby se zamknul. Takto by vám pozamykal i servisní účty a vyřadil tak celou vnitřní infrastrukturu, alespoň na nějakou dobu (viz. můj článeček zde).

Dále je vždycky žádoucí mít kvalitní hesla. Pokud by to jedno online ověření trvalo třeba 15 ms, což je dnes cca standard na rychlých systémech, tak vyzkoušet polovinu osmiznakových alfanumerických (alfanum8) hesel by trvalo 52 000 let. Píšete, že to navíc zkoušeli velmi pomalu, což indikuje, že se prostě snažili použít jen několik profláknutých hesel.

Proti tomu se dá obvykle chránit delšími hesly. Pokud si dáte politiku alespoň 10 znaků, tam už moc profláknutá hesla nejsou, protože to už není například jen jedno slovo a číslo.

Máte Windows AD prostředí, takže máte politiku 3of4. Až budu u vás, nasadíme vám moji politiku 4of4, takže si lidé budou muset dávat hesla obsahující alespoň jeden speciální znak, což kompletně odstraní problém profláknutých hesel a to i na intranetu.

To jsou ale spíše okrajové záležitosti.

Zabezpečení vzdáleného přístupu proti zkoušení hesel

Jak ale omezíme zkoušení hesel úplně? Musíte vyžadovat něco jiného než heslo už jen k tomu, aby se vůbec ustavilo vzdálené spojení. Na to se používají buď VPN připojení s ověřením pomocí buď klientského certifikátu (client certificate), nebo nějaká jiná multifaktorová autentizace (token, smart card, RSA SecureId kalkulátor, SMS, apoška).

Microsoft k tomuto má možnosti jako je SSTP (TLS HTTPS VPN), nebo IKEv2 a DirectAccess, všechno s čipovou kartou nebo jen certifikátem v počítači.

Pokud se jedná o prosté RDP, tak můžete využití také RD Gateway. RD Gateway může také vyžadovat klientský certifikát (nebo i čipovou kartu - smart card) k připojení. Takže neotevřete rovnou 3389, ale budete mít otevřen jenom TCP HTTPS 443 směrem na RD Gateway, která bude vyžadovat client certificate vůbec k vytvoření TLS spojení.

Webové aplikace, nebo i RD Gateway se dají dále publikovat přes WAP (Web Application Proxy), která může stejně tak vyžadovat klientský certifikát.

listopad 07
StartSSL už opravdu řeší ten problém s WoSign

Taková dobrá a levná autorita to byla (StartSSL) a oni se prodají do WoSignu a nechají se takhle kreténsky kompromitovat. Ach jo. Dneska má StartSSL na webu toto:

Doplněk informací od google: https://security.googleblog.com/2016/10/distrusting-wosign-and-startcom.html. Psal jsem tady o tom nedávno, ale konečně někdo vysvětlil, že nejde o přesun do Číny, ale o to, že to koupil WoSign. Sice si pořád myslím, že to jenom konkurenční boj let's encrypt (neboli "major sponsors" Mozilla a Chrome).

Tak uvidíme, jaké řešení na to borci vymyslí.

Nejhorší je, že WoSign si StartSSL (StartCom) koupil nejspíš proto, že byli skutečně dobří. Umí například code signing certifikáty za pár korun, kterými se dají podepisovat kernel mode ovladače. Jinde se to prodává za 180 EUR, zatímco StartCom je má za 60 USD na 3 roky.

StartCom/StartSSL založil maník, kterej chtěl dělat certifikáty dobře. Takhle to dopadá, když se prodáte do číny.

říjen 13
Vyhledávání AES klíčů v paměti procesů

Kvůli některým svým pokusům, pentestům a zkoumání volatile state jsem si napsal prográmek na vyhledávání AES klíčů v paměti. Vzhledem k tomu, že to je zajímavý koncept a přitom poměrně neznámý, tak jsem si říkal, že udělám kamarádovi radost a napíšu i něco techničtějšího než kalendář :-)

Je to součást mého nástroje BitColdKit, přesněji jeho funkce BitColdKit-FindAesKeys. Funkce hledá AES klíče buď v nějakém souboru na disku (nejspíš asi memory dump provedený procdumpem nebo celý RAM imidž - ten vytvářím třeba pomocí nástroje RamCapturer). Umí ale hledat i přímo v paměti nějakého běžícího procesu, stačí zadat jeho jméno, nebo process Id.

AES klíče jsou bezva :-) Cokoliv je dneska šifrované AESem. BitLocker například. Nebo webové aplikace a služby (web service) velmi rády využívají šifrované cookies, nebo view state. SQL databáze mohou být šifrované. ADFS i microsoft identity manager (FIM/MIM/MIIS/ILM) si šifrují nějaké tajné údaje v databázi. SharePoint má hesla managed accountů i jiné tajné údaje samozřejmě šifrovaná v databázi. Rozličné password managery (keepass) také šifrují svoje databáze hesel pomocí AES a mají tedy tyto šifrovací klíče v paměti po dobu svého běhu.

Jak to funguje

Nemusíte vůbec vědět, kde ty klíče v paměti jsou. AES klíče jsou sice jen 16B (AES 128) až 32B (AES 256) dlouhé. Takže jak je najdete aniž byste věděli, kde by v té paměti přibližně měly být? Mohli byste začít tím, že vyhledáte bloky paměti s vysokou entropií - klíče budou nejspíš hodně náhodné, aby byla šifra kvalitní. Ale tohle není efektivní, jak jsem prověřil. Paměť je plná různých entropických bloků. Tohle není cesta.

Lepší je využít key schedule. Na šifrování se nepoužívá přímo jen AES klíč. Nejprve se AES key rozšíří do tzv. key schedule, což je blok o velikosti 176B pro AES 128, délce 208B pro AES 192 a velikosti 240B pro AES 256. A teprve touto key schedule se projíždí a xorují vstupní data a vzniká výsledný šifrát. Generování key schedule je zbytečně pomalé na to, aby se to dělalo při každém požadavku na šifrování. Takže všechny AES knihovny vždycky při inicializaci klíče vytvoří rovnou key schedule a tu mají dlouhodobě v paměti místo toho klíče. Dělají to tak samozřejmě i netfx třídy RijndaelManaged a AesCryptoServiceProvider.

Není až tak důležité, jak taková AES key schedule vzniká z klíče, ale můžete se podívat na obrázky:


Části klíče se prostě nějak zaxorují do sebe a tak se postupuje až je to komplet tak dlouhé, jak je potřeba. No a máme key schedule.

Co je na tom parádní, že na začátku je přímo ten AES key. A za ním následuje mnoho bajtů, které jsou z něho přímo vypočítány. Takže se to krásně hledá.

Prostě jedete po paměti, u každého bajtu se zastavíte a zkusíte to od jeho pozice vzít jako potenciální AESový klíč. Podle těchto 16B/32B napočítáte, jak by vypadala šedula, kdyby to byl opravdu klíč. A porovnáte to s tím, co je ve stutečnosti v paměti. Pokud to sedí, tak to byl klíč. Pokud to nesedí, tak to klíč nebyl.

Napsal jsem si tedy svoji vlastní implementaci AESu do C# (viz. zdrojáky BitColdKitu) a používám svoji počítačku key schedule.

Jak to vyzkoušet

Pokud to chcete otestovat, prostě si stáhněte BitColdKit. Přímo z paměti procesu můžete klíče hledat buď pokud proces běží pod stejným účtem jako BitColdKit - to ani nemusíte být členem skupiny Administrators. Pokud proces běží pod jiným účtem, budete muset být členem skupiny Administrators, aby mu mohl BitColdKit přečíst paměť. Přesněji řečeno, pro čtení paměti cizích procesů potřebujete právo (user right) Debug programs (SeDebugPrivilege).

Nejjednodušší a nejhezčí je pokus na BitLocker. Jestli máte na počítači BitLockerem zašifrovaný, a aktuálně odemčený, nějaký oddíl, jeho AES klíč je v paměti RAM. Udělejte si memory dump pomocí RamCapturer a ve výsledku najdete AES klíče (neboli FVEK). Trvá to cca 20 minut pro 8 GB RAM memory image pro jednu délku klíče, pokud víte jakou hledáte.

Dá se to zkusit také na lsass, nebo nějaké w3wp procesy IISka. A cokoliv jiného, co šifruje AESem.

Krásně to funguje na keepass. Keepass si šifruje databázi hesel právě AES klíčem odvozeným z vašeho vstupního hesla. Stačí ho tedy spustit, zadat to vstupní heslo (master password) a potom pomocí BitColdKit-FindAesKeys -process keepass klíč vyndat z paměti.

Jen tak bokem, keepass má také v paměti všechna uložená hesla, která jste použili, v čisté formě, takže se také dají získat z memory dumpu. BitColdKit má na to funkci BitColdKit-FindString, která právě hledá v paměti procesů nějaké řetězce v čisté formě.

říjen 12
Školní kalendář v PowerShellu

Kamarád mě pořád otravuje, že prý mám něco konečně napsat, jinak ten můj blog přestane číst. A protože on tvrdí, že je jediným čtenářem, tak jsem mu musel jeho přání splnit. Bylo by mě tu smutno.

Otázka zůstává, jestli zrovna tohle využije. Udělal jsem si pro Janka školní kalendář v PowerShellu, aby se konečně naučil měsíce. Tak třeba se to někomu bude líbit. Výběr těch barev mě zabral tak tři hodiny. Hrůza.

Ve skriptu stačí hned na začátku změnit nadpis $title a seznam volných dnů v proměnné $specialDays2.

[string] $title = 'Školní kalendář pro mého Janíčka'

[string[]] $specialDays2 = @(
  '7.10.',
  '18.11.',
  '22.12.',
  '23.12.',
  '27.12.',
  '28.12.',
  '29.12.',
  '30.12.',
  '13.4.'
  )

[string[]] $specialDays = @(
  '28.9.',
  '28.10.',
  '17.11.',
  '24.12.',
  '25.12.',
  '26.12.',
  '1.1.',
  '14.4.',
  '17.4.',
  '1.5.',
  '8.5.',
  '5.7.',
  '6.7.'
  )

[string] $zeroSpring = '2016-03-20'
[string] $zeroSummer = '2016-06-21'
[string] $firstAutumn = '2016-09-22'
[string] $firstWinter = '2016-12-21'
[string] $firstSpring = '2017-03-20'
[string] $firstSummer = '2017-06-21'
[string] $zeroAutumn = '2017-09-22'
[string] $zeroWinter = '2017-12-21'

[int] $year = 2016
[int] $startMonth = 9
[string] $outFile = Join-Path $env:TEMP ('skolni-kalendar-{0}-{1}.htm' -f $year, ($year + 1))

[int[]] $monthSeparators = @(1, 6)

#
#

[hashtable] $monthNames = @{

   1 = 'Leden'
   2 = 'Únor'
   3 = 'Březen'
   4 = 'Duben'
   5 = 'Květen'
   6 = 'Červen'
   7 = 'Červenec'
   8 = 'Srpen'
   9 = 'Září'
  10 = 'Říjen'
  11 = 'Listopad'
  12 = 'Prosinec'

  }

[hashtable] $dayNames = @{

  1 = 'Pondělí'
  2 = 'Úterý'
  3 = 'Středa'
  4 = 'Čtvrtek'
  5 = 'Pátek'
  6 = 'Sobota'
  7 = 'Neděle'

  }

[hashtable] $dayNamesShort = @{

  1 = 'Po'
  2 = 'Út'
  3 = 'St'
  4 = 'Čt'
  5 = 'Pá'
  6 = 'So'
  7 = 'Ne'

  }

[hashtable] $dayNumbers = @{

  'Monday' = 1
  'Tuesday' = 2
  'Wednesday' = 3
  'Thursday' = 4
  'Friday' = 5
  'Saturday' = 6
  'Sunday' = 7

  }

[hashtable] $seasons = @{

  1 = 'zima'
  2 = 'jaro'
  3 = 'léto'
  4 = 'podzim'

  }

#
#

[DateTime] $start = [DateTime]::Parse(('{0}-{1:D2}-01' -f $year, $startMonth))

#
#

function NewDay ([int] $day, [int] $month, [int] $year, [bool] $weekend, [bool] $holiday, [int] $preDaysToMonday, [int] $postDaysToSunday, [int] $dayOfWeek, [int] $season)
{
  [PSObject] $newDay = New-Object PSObject

  Add-Member -Input $newDay -MemberType NoteProperty -Name day -Value $day
  Add-Member -Input $newDay -MemberType NoteProperty -Name month -Value $month
  Add-Member -Input $newDay -MemberType NoteProperty -Name year -Value $year
  Add-Member -Input $newDay -MemberType NoteProperty -Name weekend -Value $weekend
  Add-Member -Input $newDay -MemberType NoteProperty -Name holiday -Value $holiday
  Add-Member -Input $newDay -MemberType NoteProperty -Name preDays -Value $preDaysToMonday
  Add-Member -Input $newDay -MemberType NoteProperty -Name postDays -Value $postDaysToSunday
  Add-Member -Input $newDay -MemberType NoteProperty -Name dayOfWeek -Value $dayOfWeek
  Add-Member -Input $newDay -MemberType NoteProperty -Name season -Value $season

  return $newDay
}

function Season ([DateTime] $dayDate) {

  $date = $dayDate.Date

  if ($date -lt $zeroSpring) {

    return 1
  }

  if (($date -ge $zeroSpring) -and ($date -lt $zeroSummer)) {

    return 2
  }

  if (($date -ge $zeroSummer) -and ($date -lt $firstAutumn)) {

    return 3
  }

  if (($date -ge $firstAutumn) -and ($date -lt $firstWinter)) {

    return 4
  }

  if (($date -ge $firstWinter) -and ($date -lt $firstSpring)) {

    return 1
  }

  if (($date -ge $firstSpring) -and ($date -lt $firstSummer)) {

    return 2
  }

  if (($date -ge $firstSummer) -and ($date -lt $zeroAutumn)) {

    return 3
  }

  if (($date -ge $zeroAutumn) -and ($date -lt $zeroWinter)) {

    return 4
  }

  if ($date -ge $zeroWinter) {

    return 1
  }
}

function PreDays ([DateTime] $start) {

  [int] $preDays = 0

  if ($start.DayOfWeek -eq [System.DayOfWeek]::Saturday) { $preDays = 5 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Sunday) { $preDays = 6 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Monday) { $preDays = 0 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Tuesday) { $preDays = 1 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Wednesday) { $preDays = 2 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Thursday) { $preDays = 3 }
  if ($start.DayOfWeek -eq [System.DayOfWeek]::Friday) { $preDays = 4 }

  return $preDays
}

function PostDays ([DateTime] $end) {

  [int] $postDays = 0

  if ($end.DayOfWeek -eq [System.DayOfWeek]::Monday) { $postDays = 6 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Tuesday) { $postDays = 5 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Wednesday) { $postDays = 4 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Thursday) { $postDays = 3 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Friday) { $postDays = 2 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Saturday) { $postDays = 1 }
  if ($end.DayOfWeek -eq [System.DayOfWeek]::Sunday) { $postDays = 0 }

  return $postDays
}

#
#

[hashtable] $calendar = @{}

[DateTime] $pointer = $start
[int] $monthPointed = 0

while ($pointer -ne $start.AddYears(1)) {

  [int] $preDays = 0

  if ($pointer.Month -ne $monthPointed) {

    $monthPointed = $pointer.Month
    [void] $calendar.Add($monthPointed, (New-Object System.Collections.ArrayList))

    $preDays = PreDays $pointer
  
  }

  $nextDay = $pointer.AddDays(1)
  [int] $postDays = 0

  if ($nextDay.Day -eq 1) {

    $postDays = PostDays $pointer
  }

  [bool] $isWeekend = ($pointer.DayOfWeek -eq [System.DayOfWeek]::Saturday) -or ($pointer.DayOfWeek -eq [System.DayOfWeek]::Sunday)
  [bool] $isHoliday = ($specialDays -contains ('{0}.{1}.' -f $pointer.Day, $pointer.Month)) -or ($specialDays2 -contains ('{0}.{1}.' -f $pointer.Day, $pointer.Month))

  [void] $calendar[$monthPointed].Add((NewDay -day $pointer.Day -month $pointer.Month -year $pointer.Year -weekend $isWeekend -holiday $isHoliday -preDaysToMonday $preDays -postDaysToSunday $postDays -dayOfWeek $dayNumbers[$pointer.DayOfWeek.ToString()] -season (Season $pointer)))

  $pointer = $nextDay
}

#
#

#
#

[System.Text.StringBuilder] $outHtml = New-Object System.Text.StringBuilder

[void] $outHtml.AppendLine(('<html><head><meta http-equiv=Content-Type content="text/html; charset=UTF-8"><title>{0}</title>' -f $title))
[void] $outHtml.AppendLine('<style>')

[void] $outHtml.AppendLine('* { font-family: "Segoe UI",Arial,sans-serif; }')
[void] $outHtml.AppendLine('h1 { text-align: center; }')
[void] $outHtml.AppendLine('table { border-spacing: 0; margin: 0 auto; }')
[void] $outHtml.AppendLine('table.wrapper { table-layout: fixed; }')
[void] $outHtml.AppendLine('th { text-align: center; font-size: 120%; background-color: #E9E9E9; padding: 5px; }')
[void] $outHtml.AppendLine('td { padding: 5px; }')
[void] $outHtml.AppendLine('td.dayName { padding-left: 10px; }')
[void] $outHtml.AppendLine('td.dayDate { padding-right: 10px; }')
[void] $outHtml.AppendLine('td.day { text-align: center; }')
[void] $outHtml.AppendLine('td.dayWinter { background-color: #EAF4FF; }')
[void] $outHtml.AppendLine('td.daySpring { background-color: #EAFFD5; }')
[void] $outHtml.AppendLine('td.daySummer { background-color: #FFEAEA; }')
[void] $outHtml.AppendLine('td.dayAutumn { background-color: #FFFFD5; }')
[void] $outHtml.AppendLine('td.holidayWinter { font-weight: normal; background-color: #D5E9FF; }')
[void] $outHtml.AppendLine('td.holidaySpring { font-weight: normal; background-color: #DAFFB5; }')
[void] $outHtml.AppendLine('td.holidaySummer { font-weight: normal; background-color: #FFD5D5; }')
[void] $outHtml.AppendLine('td.holidayAutumn { font-weight: normal; background-color: #FFFFB5; }')
[void] $outHtml.AppendLine('td.weekendWinter { font-weight: bold; background-color: #D5E9FF; }')
[void] $outHtml.AppendLine('td.weekendSpring { font-weight: bold; background-color: #DAFFB5; }')
[void] $outHtml.AppendLine('td.weekendSummer { font-weight: bold; background-color: #FFD5D5; }')
[void] $outHtml.AppendLine('td.weekendAutumn { font-weight: bold; background-color: #FFFFB5; }')

[void] $outHtml.AppendLine('</style>')
[void] $outHtml.AppendLine('</head>')
[void] $outHtml.AppendLine(('<body><table class="wrapper"><tr><td colspan="{0}"><h1>{1}</h1></td></tr>' -f (12 + $monthSeparators.Length), $title))

#
#

function DayCell ([int] $i, [int] $row)
{
  [Text.StringBuilder] $dayCell = New-Object Text.StringBuilder

  #[void] $dayCell.Append('<td><table><tr>')

  $oneDay = $calendar[$i][$row]

  [string] $class = 'day'

  if ($oneDay.holiday) {

    $class = 'holiday'
  }

  if ($oneDay.weekend) {

    $class = 'weekend'
  }

  switch ($oneDay.season) {

    1 { $season = 'Winter' }
    2 { $season = 'Spring' }
    3 { $season = 'Summer' }
    4 { $season = 'Autumn' }
  }

  if (-not ([object]::Equals($oneDay, $null))) { 

    [void] $dayCell.Append(('<td class="day dayName {0}{1}">{2}</td><td class="day dayDate {0}{1}">{3}.{4}.</td>' -f $class, $season, $dayNamesShort[$oneDay.dayOfWeek], $oneDay.day, $oneDay.month))

  } else {

    [void] $dayCell.Append(('<td class="day dayName {0}{1}">&nbsp;</td><td class="day dayDate {0}{1}">&nbsp;</td>' -f $class, $season))
  }

  #[void] $dayCell.Append('</tr></table><td>')

  return $dayCell.ToString()
}

function MonthCells ([int] $i, [int] $maxDays) 
{
  [Text.StringBuilder] $cells = New-Object Text.StringBuilder

  [int] $dayCounter = 1
  for ($row = 0; $row -lt $maxDays; $row++) {

    [void] $cells.Append('<tr>')

    [void] $cells.Append((DayCell $i $row))

    [void] $cells.AppendLine('</tr>')

    if ($dayCounter -lt 7) {

      $dayCounter ++
  
    } else {

      $dayCounter = 1
    }
  }

  return $cells.ToString()
}

function MonthCell ([int] $i, [int] $maxDays, [int] $year)
{
  [Text.StringBuilder] $month = New-Object Text.StringBuilder

  [void] $month.Append('<td class="month">')

  [void] $month.Append(('<table class="month" id="month{0:D2}">' -f $i))
  [void] $month.Append('<tr>')
  [void] $month.Append(('<th colspan="2">{0}<br/>{1}</th>' -f $monthNames[$i], $year))
  [void] $month.AppendLine('</tr>')
  [void] $month.AppendLine('<tr><td colspan="2"></td></tr>')

  [void] $month.Append((MonthCells $i $maxDays))

  [void] $month.Append('</table>')

  [void] $month.AppendLine('</td>')

  return $month.ToString()
}

#
#

[int] $maxDays = 0
foreach ($oneMonth in $calendar.Keys) {

  $maxDays = [Math]::Max($maxDays, $calendar[$oneMonth].Count)
}

[void] $outHtml.Append('<tr>')

for ($i = $startMonth; $i -le 12; $i++) {

  [void] $outHtml.Append((MonthCell $i $maxDays $year))

  if ($monthSeparators -contains $i) {

    [void] $outHtml.AppendLine(('<td class="monthSept"></td>'))
  }
}

for ($i = 1; $i -lt $startMonth; $i ++) {

  [void] $outHtml.Append((MonthCell $i $maxDays ($year + 1)))

  if ($monthSeparators -contains $i) {

    [void] $outHtml.AppendLine(('<td class="monthSept"></td>'))
  }
} 

[void] $outHtml.AppendLine('</tr>')

#
#

[void] $outHtml.AppendLine('<tr>')


[void] $outHtml.AppendLine('</table></body></html>')
$outHtml.ToString() | Set-Content -LiteralPath $outFile -Encoding UTF8

No a to je pro dnešek všechno, přátelé.

září 16
Ponožka jako vhodný bezpečnostní doplněk k vašemu telefonu

Od jakživa používám Jankovu ponožku jako obal na mobil. No dobrá, tak už si je kupuju i sám, přecejenom už trošku vyrostl. Ale minulý týden jsme na, i pro mě velmi přínosném, CHFI kurzu, zjistili, že to má dokonce i bezpečnostní vlastnosti. Podle pana účastníka není problém z otisků prstů zjistit, alespoň přibližně, jaký kód na mobilu člověk má.

Na mém to nejde, protože je vždycky pěkně vyleštěný:

září 12
Show ohledně přesunu StartSSL do Číny

Dorazil mi mailem odkaz na článek o tom, že certifikační autorita StartSSL byla přesunuta do Číny. Pod článkem je změť histerických komentářů. Nešilte.

Co je na tom jako špatně? Zřejmě má někdo strach, že Číňani začnou dělat MITM a začnou odposlouchávat nějaké webové komunikace. A že začnou generovat podvžené digitální certifikáty pro podpis kódu (exe, powershell apod.). Tak to jsem se právě zbláznil strachem.

A měli by i všichni ostatní, kdo mají například notebook nebo mobil Lenovo. Firma se přímo chlubí tím, že je čínská. Na mém iPhone je zezadu hrdě napsáno assembled in China. Hlavní výrobce TPM čipů, firma Infineon, má v Číně oddělení R&D (research and development) i oddělení Production, jak je hrdě manifestováno tady. Například firma Home Credit provozuje v Česku jenom plivátko, v Číně je největším poskytovatelem osobních půjček, což bude o několik nul více, než v prostoru pro nás deset miliónů.

Právě se mi promočily gatě.

Jako od kdy je Čína víc zlá, než US například. Kdyby se udělala statistika, zvlášť na root.cz, tak předpokládám, že víc článků tam bude o tom, jak US špicluje všechny a všechno. Proč by vás nemohla odposlouchávat například německá rozvědka, čistě proto, že Mutti nařídila raději odposlechnout všechno, aby se předešlo nějakým dalším terorům?

Takže v klidu. Certifikační autority (CA) jsou například do Microsoft systémů vpouštěny na základě požadavkůch zvaných Microsoft Trusted Root Certificate Program. Je zde požadavek na nezávislý audit, a to jak pro vstup do programu (intake process) tak i opakovaně každoročně (continuing requirements).

I v tuto chvíli (April 2016) jsou v něm například China Financial Certification Authority (CFCA), China Internet Network Information Center (CNNIC) i WoSign CA Limited a Guang Dong Certificate Authority (GDCA) a Shanghai Electronic Certification Authority Co., Ltd. (SHECA).

Nebo například Government of Saudi Arabia, NCDC (Saudská Arábie), Chunghwa Telecom Corporation (Taiwan), Government of Taiwan, Government Root Certification Authority (GRCA) (Taiwan), Government of Hong Kong (SAR), Hongkong Post, Certizen (Hong Kong), Government of India, Ministry of Communications & Information Technology, Controller of Certifying Authorities (CCA) a také třeba Government of Turkey, Kamu Sertifikasyon Merkezi (Kamu SM) a TurkTrust.

Korejci tu svoji dokonce rovnou nazývají takto - Korea Information Security Agency (KISA).

A to jsem to vybral čistě pouze na základě jejich jmen.

Pokud si tedy nezrušíte všechny CA kromě našich českých a nevyhodíte veškeré hardware vybavení, které není české :-), tak si vyměňte spodky a v pohodě pokračujte.

září 08
PowerShellová bleskovečka - aneb jak najdu neaktivní počítače

Počítače si mění heslo automaticky samy každý 30 dnů. Takže pokud si už 95 dnů heslo nezměnily, tak jsou asi polomrtvé:

dsquery * domainRoot -filter "(&(sAMAccountType=805306369)(pwdLastSet<=$(([DateTime]::Now.AddDays(-95) - [DateTime]::Parse('1601-01-01')).Ticks)))" -limit 0 | % { $_.Trim('"') } | Sort
srpen 17
Jak vzniká pocit, že Windows je úplně zabagovanej

Třeba takto. Nějakej "výzkumník" objeví (dlouho známý) velmi těžko zneužitelný nedostatek a když o tom napíše dostatečné hrozivý článek tak je z toho hned poprask a lidi ládují do registrů bezhlavě hodnoty, které zablokují všechno. Ten článek je v podstatě komplet přehnané strašení. Upozorňuje to explicitně navíc na firemní oběti, protože to ani pro workgroup počítače ani pro doménové stroje mimo dosah DC nefunguje.

Zaprvé to využívá SMB přístup do internetu. Jestli má firma na svém firewallu povolen zevnitř přístup na SMB do internetu, tak to se pak nemá proč divit. Druhak jestli firma neimplementuje žádnou multifaktorovou autentizaci, tak se nemá čemu divit. Takže prostor pro útok je minimální.

Třeťak to vychytává NTLMv2 response. Což je zasolená MD5 hash hesla. Takže jestli má uživatel správně dlouhé a komplexní heslo, to znamená alespoň 12 znaků, tak při použití nejrychlejšího nástroje hashcat a jedné GPU za cca 5000 Kč to bude krekovat cca 76 000 let. Takže drama největší. Začal jsem se toho děsně obávat.

Pokud chce někdo vychytávat hesla uživatelů, tak garantuju, že pokud uděláte stránku s nápisem "přihlašte se pomocí svého firemního účtu", tak to do toho formuláře zadá každý druhý uživatel a nemusí se krekovat žádná hash.

Dále rada na konci článku ohledně použití hodnoty RestrictReceivingNTLMTraffic a RestrictSendingNTLMTraffic je už úplný nesmysl, to se na mě nezlobte. Blokovat "receiving NTLM" (neboli Network Security: Restrict NTLM: Incoming NTLM traffic) tzn. příjem NTLM na straně podnikového klienta nejspíš provozně nevadí, ale zaručeně to neřeší popisovanou vulnerabilitu.

Ano, zablokovat "sending NTLM" (neboli Network Security: Restrict NTLM: Outgoing NTLM traffic) by proti tomuto "útoku" ochránilo. Dobrá tak to udělejte a užijte si těch padesát věcí, které vám potom nepojedou. Tohle nastavení, tedy zablokovat NTLM se snažím dělat posledních pár let všude, kde dělám nějaké zabezpečení a ze zkušenosti můžu garantovat, že to udělat nemůžete. Protože vám potom x věcí prostě nepojede. První je RDP v 90% případů. Druhý je SQL klienti v 50% případů. NTLM prostě v podnikové síti vypnout nejde, protože to na to není připravené. Howgh.

Takže až zase budete číst nějaké strašáky, tak to berte jako stejnou propagandu, jako jsou libovolné jiné zaručeně "nejdůvěryhodnější" články o politice, válkách, společnosti, ekologii apod.

srpen 15
Narozen hackerem :-)

Jsem prostě rozenej hacker :-) Dojdu po měsíci školit do GOPASu v Praze a vidím, že máme na učebnách nové hypermoderní displeje zobrazující seznam účastníků a také heslo na WiFi a kód na dveře. Asi jsem se narodil hackerem, protože první co mě napadlo, bylo jít se podívat ven, jestli ten kód na dveře není vidět skrze naše skleněné dveře. Dveře jsou prosklené, s kódovou klávesničkou, a přímo za nimi jsou učebny s viditelnými displayi :-)

No trošku mě zklamalo, že to vidět není. Údajně to dřív vidět bylo, ale už si toho někdo všiml a panely na inkriminovaných místech zobrazují jen hvězdičky :-)

Škoda, těšil jsem se, že budu mít hezkou přednášku na HackerFest :-)

červenec 20
Vyhledání adresářů, které se už dlouho nezměnily

Problém - máme file server a potřebujeme najít všechny adresáře, jejichž obsah se už dlouho nezměnil. Použijeme k tomu samozřejmě PowerShell. Vyhledat dlouho nezměněné soubory není vůbec problém, stačí porovnat jejich atribut LastWriteTime. Jenže tohle nechceme, bude toho obvykle (sto)(deseti)tisíce, prostě mnoho. Já chci vidět jen adresář, jehož celý obsah má poslední modifikaci starší, než například 410 dnů.

Takže zde je funkce, která prohledá celou adresářovou strukturu a vrátí jen adresáře, jejichž žádný soubor se nezměnil nedávno, než před nějakým počtem dnů.

function Get-FsAgeMap (
  [Parameter(Position=0, Mandatory=$true)] [ValidateScript({ Test-Path $fsRoot })] [string] $fsRoot,
  [Parameter(Position=1)]                  [ValidateScript({ $_ -ge 0 })] [int] $ageDaysAtLeast = 0,
                                                                          [switch] $returnOnlyParent
  )
{
  if ($returnOnlyParent -and ($ageDaysAtLeast -eq 0)) {

    throw 'You must specify some minimum days of age to use the -returnOnlyParent parameter'
  }

  $fsRoot = $fsRoot.TrimEnd('\')

  [hashtable] $ageMap = @{}
  [bool] $uncFsRoot = $fsRoot -like '\\?*\?*'

  # Note: using -Force parameter in order to obtain System and Hidden files as well
  dir $fsRoot -Recurse -Force | ? { -not $_.PsIsContainer } | % { 

    [IO.FileInfo] $oneFile = $_
    [int] $ageDays = ([DateTime]::Now - $oneFile.LastWriteTime).TotalDays

    [string] $onePath = $oneFile.FullName
    while ($true) {

      [string] $onePath = Split-Path -Parent $onePath

      if ((-not ([string]::IsNullOrEmpty($onePath))) -and ($onePath.Length -ge $fsRoot.Length)) {

        if ($ageMap.ContainsKey($onePath)) {

          $ageMap[$onePath] = [Math]::Min($ageDays, $ageMap[$onePath])

        } else {

          [void] $ageMap.Add($onePath, $ageDays)
        }
      
      } else {

        break
      }
    }
  }

  if ($ageDaysAtLeast -gt 0) {

    [Collections.ArrayList] $ageMapKeys = $ageMap.Keys
    foreach ($oneAgeMapKey in $ageMapKeys) {

      if ($ageMap[$oneAgeMapKey] -lt $ageDaysAtLeast) {

        [void] $ageMap.Remove($oneAgeMapKey)
      }
    }

    if ($returnOnlyParent) {

      [Collections.ArrayList] $ageMapKeys = $ageMap.Keys
      $ageMapKeys.Sort()

      [int] $i = 0
      while ($i -lt $ageMapKeys.Count) {

        [string] $oneAgeMapKey = $ageMapKeys[$i]
        $i ++

        while (($i -lt $ageMapKeys.Count) -and ($ageMapKeys[$i] -like (Join-Path $oneAgeMapKey '?*'))) {

          [void] $ageMap.Remove($ageMapKeys[$i])
          $i ++
        }
      }
    }
  }

  return $ageMap
}

Zavoláte to jednoduše, buď s pomocí lokální cesty, nebo pomocí UNC síťové cesty, to je jedno. Parametr returnOnlyParent způsobí, že ve výsledku budou opravdu jen ty nejvrchnější adresáře. Pokud byste to tam nedali, tak by to vrátilo všechny adresáře, jejichž obsah je starší než zadaný počet dnů, takže by tam jedna podcesta byla zbytečně vícekrát. Například takto:

$ageMap = Get-FsAgeMap -fsRoot '\\fs1\profiles' -ageDaysAtLeast 210 -returnOnlyParent

 

červen 15
Všechno je tu boží!

Dvacet minut, padesát poslechů a ještě najít anglická slova jsme tu museli, abychom zjistili český text Všechno je tu boží z filmu Lego příběh. Ten čtvrtý verš není skoro rozumět.

Tak výsledek je tady:

všechno je tu boží
všechno jde ti líp když jsi týmový hráč
všechno je tu boží
tak teď žít chcem sen náš

Teď už si to z hlavy nikdy nevyndám :-)

1 - 14Next
>
 

 Rychlovky lepší než tvítr

 
start ssl už zase vydává! 1.12.2016 12:50
jupíí, StartSSL už zase vydává!
 
nejede google 22.11.2016 21:16
jak najít teď rychle jinej DNS server když nejede google? brýle se bez brýlí špatně hledají :-)
 
AES klíč wordu 22.11.2016 11:11
dívám se, že můj hledač AES klíčů funguje i na zašifrované Word dokumenty: https://www.sevecek.com/Lists/Posts/Post.aspx?ID=583
 
rtm windows 2016 7.11.2016 21:56
RTM Windows 2016 nabízí při instalaci DC hodnotu forest level "Windows Server Technical Preview". Tak to je opravdu luxus na RTM :-)
 
CD a wifi po vlaku 20.10.2016 20:43
čd opět předvádí, že mít WiFi ještě neznamená připojení do internetu. Ale jede hezky, jen co je pravda, nikdo moc nezatěžuje :-)
 
(More Announcements...)