Skip Ribbon Commands
Skip to main content

Ondrej Sevecek's Blog

:

Engineering and troubleshooting by Directory Master!
Ondrej Sevecek's Blog > default
leden 17
Limit na počet členů ve skupině při použití příkazu Get-AdGroupMember

Já tyhle nesmyslné web-service cmdlety moc nepoužívám, a jak vidím, tak dobře vím proč :-) Dneska padla otázka, jestli to nemá limit na počet zobrazených členů. Zkusil jsem. A ejhle:

The size limit for this request was exceeded

První úvaha byla, že tam bude nějaký parametr, kterým se to zvětší, to je přece normální. Zjištění je, že tam žádný parametr není. Pokud to potřebujete zvýšit nad 5000! členů, tak musíte měnit nějaký konfigurační soubor na všech vašich řadičích domény :-)

Říkám to pořád, s tím web-servisním AD powershellem, to se někdo musel zkouřit.

leden 11
Jak se pohybovat v PowerShell ISE command pane

Konečně jsem na to přišel! Na vývoj skriptů používám PowerShell ISE. Má to dvě zobrazení - script pane a command pane. Ve script pane se normálně edituje skript (bílé pozadí, zapne se to CTRL-I). Když svůj kód spustíte, dostanete příkazovou řádku (command pane, modré pozadí) s výpisem výsledků. Sem se přepnete CTRL-D.

No a v tom command pane se dá myší umísťovat kurzor a potom dělat bloky a kopírovat do schránky (clipboard). Jenže to jsem až do teď musel dělat myší. Musel jsem vždycky kliknout do prostoru a teprve potom fungovaly šipky. Když zmáčknete jenom šipku nahoru, tak vám to nahodí předchozí příkaz, místo aby to posunulo kurzor.

A teď jsem se naučil, že pohybovat kurzorem po tom command pane, se dá pomocí CTRL-UP (šipka nahoru). Stačí stisknout jednou a potom už fungují šipky normálně!

leden 03
My country od Fredericka Creama

Naši šli na Nový rok na Mou vlast do brněnské filharmonie. A takto pěkně přeložený program dostali. Osobně oceňuji zvláště volbu překladů jako je "country", "woods" nebo "fields" a inovativní překlady místních názvů. Speciálně ten do němčiny, což pěkně vyzdvihuje vlastenecký náboj díla:

Škoda jenom, že to ten kvalitní překladatel nedotáhl pořádně do konce. Přitom se dalo přeložit i jméno autora i všechno ostatní. Třeba takto:

  • Frederick Cream, My country
    • The High Castle
    • The Moldau
    • Charlotte
    • From Bohemian Woods and Fields
    • Camp
    • Hymen hill

 

prosinec 21
Parádní UAC bypass

Včera mi Michal Meravý (díky!) poslal super UAC bypass, trošku jsem to upravil na powershell a je to tu:

reg add hkcu\software\classes\mscfile\shell\open\command /d "cmd /c start powershell -NoExit" & eventvwr

nebo rovnou příklad zneužití

reg add hkcu\software\classes\mscfile\shell\open\command /d "cmd /c start powershell { 'chachá' | Out-File c:\uac-bypass.txt }" & eventvwr

Vo co gou? O UAC jsem tu už jednou hanlivě psal, že se jedná o "bezpečnostní" fíčuru pro domáčí uživatele. V podnikovém prostředí tak maximálně na to, aby to snížilo míru a frekvenci toho, jak si BFUčka dodrbávají počítače a kdo to pak musí pořád opravovat. Takže z mého pohledu o nic nejde. Ale líbí se mi to. Zrovna tahle metoda je prostě krásná, to se musí uznat.

Jak to funguje?

Od Windows 7 se UAC ve výchozím nastavení chová tak, že některé programy nevyžadují explicitní potvrzování čudlíkem, a přitom je systém rovnou spustí "elevated" (neboli "jako správce", neboli "as administrator"). To je případ různých systémových správcovských programů jako je control panel, nebo většina MMC konzolí (soubory s příponou .msc). A eventvwr.exe.

Zrovna tenhle eventvwr.exe je evidentně kamenem úrazu. Spustí se elevated a přitom sám v podstatě nic nedělá. Jenom spustí rovnou eventvwr.msc konzoli. A ta se spouští jen jako asociace .msc souboru.

Tento uac bypass tedy jen změní v uživatelském profilu asociaci .msc souboru z původního mmc.exe na powershell. A je to. Jakmile potom někdy spustíte eventvwr.exe, tak rovnou naskočí jako správce a spustí následně powershell už také jako správce.

Krásná ukázka exploitu, kterých bude nekonečně mnoho, pokud navrhnete nějaký software blbě. Dá se to fixnout? Ano, stačí opravit eventvwr.exe. Ale stejně bude dalších nekonečně moc cest. Stačí trošku hledat.

Pokud se jedná o pěknou historii různých UAC bypassů, tak se mrkněte sem - stránka UACMe - nabízí milión různých cest. Když si to pročtete, nabízí také seznam miliónu MS fixů, které každou tu cestu postupně opravují :-)

Jak se bránit?

Buď uživatelé nesmí být členem místní skupiny Administrators. Nebo můžete přepnout UAC tak, aby se to ptalo vždycky, když je to potřeba. To se provede buď v Group Policy, nebo lokálně v Local Security Policy:

Security Settings
  Local Policies
    Security Options
      User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode

  = Prompt for consent

Jednoduše - musíte to přepnout z výchozího Prompt for consent for non-Windows binaries na bezpečnější (ale otravnější) Prompt for consent.

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
1 - 14Next
>
 

 Rychlovky lepší než tvítr

 
GoDaddy vydal 8951 certifikátů bez správného ověření - Root.cz 12.1.2017 14:40
https://www.root.cz/clanky/godaddy-vydal-8951-certifikatu-bez-spravneho-overeni
 
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 :-)
 
(More Announcements...)