OpenSSH 7.4

Címkék

Az OpenSSH csapat bejelentette az OpenSSH 7.4 elérhetőségét. A kiadás bejelentésében felhívják a figyelmet arra, hogy a soron következő kiadásokban több elavult kripto funkció, feature is nyugdíjazásra kerül. Többek közt:

  • 2017 augusztusában környékén eltávolítják az SSH v.1 protokoll maradékát (csak kliens oldali, fordítási időben letiltott jelenleg) is
  • ugyanabban a kiadásban búcsúzik a Blowfish / RC4 / RIPE-MD160 HMAC
  • elutasításra kerülnek a 1024 bitnél kisebb RSA kulcsok
  • a következő kiadástól megszűnik a priv. sep. nélkül futó sshd támogatása
  • stb.

Részletek a bejelentésben.

Hozzászólások

Note To Self: el ne felejtsem kipróbálni, megy-e a legújabb OpenSSL-lel (1.1.0)

Szerk: valószínűleg jobban haladnék, ha le tudnám tölteni valahonnan az openssh-7.4p1.tar.gz-t

Régebben (évekkel ezelőtt) azt olvastam, hogy a blowfish jó.
Azóta mi változott?
Az a gond, hogy 64 bites a blokk mérete?

A blowfish eltávolítása meglepő.

Négy biztonsági hibát is javít, ajánlott frissíteni.

Ubuntu 16.04 frissülni fog erre vagy esélytelen?

3 honapja dolgozok egy az sshd-hoz hasonlo, de tovabbfejlesztett pivilage separation megoldason. erdekes munka.

Adott egy applikáció, ami kb 8 unix ( linux, aix, solaris, hpux, zlinux variánsok) platformra fordul, amelynek jelenleg van egy definiált listen portja. Ezen keresztül fogad bejővő file transfer kapcsolatokat és még egyéb adminisztációs program kapcsolatokat. Maga a program álltalában root jogosultsággal fut, hogy miért, azt nem részletezem. Egy ügyfél kérése volt, hogy az sshd-hez hasonló módon oldjuk meg, hogy a root jogokkal ellátott process sose olvasson a bejövő újjonnan nyitott kapcsolatról. Az sshd esetében ez úgy valósul meg, hogy az acceptált socketről egy forkolt nobody child process olvas és bonyolítja le az autentikációt. Minden bejővő kapcsolat esetén forkolja magát az sshd...
A mi megoldásunk annyiban tér el, hogy a root processnek nincs listen portja, induláskor indít egy nobody listener processt, ami fogadja a bejövő kéréseket. A GSS authentikáció egy IPC (internal process communication) keresztül történik.
A lényegi rész itt történik, ezzel nyertem egy láda sört egy kollégámtól, aki nem hitt a megvalósírásban! :-)
Amennyiben a bejövő partner authentikációja sikeres, a listener process átadja a socket descriptort a parent root processnek, tehát az már csak egy olyan csatornáról olvas, ami megbízható és titkosított. A sendmsg(), recvmsg() függvények segítségével control adatokat is lehet küldeni, aminek egyik ilyen szolgáltatása, hogy nyitott file descriptort tudunk átadni bármilyen másik processnek.
Valójában az eltérés lényege az sshd-hez képest, hogy a root joggal rendelkező processnek nincs nyitott listen portja. Amennyiben megfektetik a listener process-t, a nobody jogosultsággal nem tudnak sok mindent csinálni a rendszeren.
Kb. ennyi a történet!

Jó lehet kiindulásnak, de van néhány dolog amit érdemes figyelembe venni:

- egyrészt érdemes tisztázni az ügyféllel a threat modelt, milyen fajta támadások ellen szeretnének (és lehet) védekezni

- a root processz által listenelt port önmagában még nem okoz igazán biztonsági kockázatot... ha a bejövő kapcsolat esetén a processz egyből forkol és eldobja a root jogait, akkor nem lesz olyan input feldolgozó programkód, amelyik rootként hajtódik végre és ezáltal rootként kihasználható hibát tartalmaz: tehát önmagában nem az okozza a problémát, hogy rootként listenel a port, vagy nem

- ennek ellenére a nobodyként, vagy bármilyen más userként futó hibás kód továbbra is támadható marad ("mindegy hogy hová söpröd a szart, az szar marad"), csak a hatás (impact) lesz kisebb: a jelenlegi felállásban egy potenciális "remote pre-authentication root" sérülékenységből "csak" egy "remote pre-authentication" sérülékenység lesz, azonnali root nélkül, de továbbra is lehetőség lehet "remote post-authentication root" hiba kihasználására, ha utána - a hitelesítést követően - már rootként történik a további input feldolgozás... Ha ezt most az sshd esetére vetítjük ki, akkor gondolj egy olyan szituációra, ahol van egy sima legitim user, aki rendesen tudja magát hitelesíteni (és ez a hitelesítés így már ugyan nem rootként történik), de a későbbi sshd kód ha továbbra is rootként hajtódik végre, akkor a rosszindulatú felhasználó a sikeres autentikáció után rábírhatja arra a root sshd processzt, hogy az számára kedvező dolgot tegyen, így ez továbbra is sebezhetőség, csak nem "remote pre-authentication root", hanem "remote post-authentication root" és "privilege escalation" lesz.

- a fentebbi miatt érdemes ezért a támadási felületet minimalizálni és a lehető legkevesebb kódot rootként futtatni, de továbbra se fog segíteni ez abban, hogy a nem-root user nevében futó kódokat támadás érje...

- egy sshd jellegű program esetén sok esetben nem tud elég védelmet jelenteni az, ha privilégium szeparáció történik, mert nincs jelentősége hogy egy rootként futó processzt bír rá a támadó arra, hogy pl. sikeresen bejelentkeztesse, vagy egy privsep processzt bír rá... Az OpenSSH sshd esetén sokszor kizárólag egyetlen biten múlik, hogy az adott felhasználó be legyen jelentkeztetve: az "authenticated" nevű változó 0 vagy 1 értéket tartalmaz a megfelelő pillanatban, amikor annak ellenőrzése megtörténik. Mindegy, hogy ezt a változót egy rootként futó processz ellenőrzi-e, vagy egy korlátozott user nevében futó processz, ha a támadó képes ezt az egy bitet átbillentenie a megfelelő időben, akkor a program logikájánál fogva hiteles bejelentkezésnek fogja kezelni és belépteti. Nem fog segíteni ebben az esetben a privilege separation. Lásd: Data Flow Integrity, data-only attacks.

- a nobody user használata privsep felhasználónak nem javasolt, mert ezt a usert más daemon is használhatja, így kereszttámadás vitelezhető ki két program között. Konkrétan ez a nobody felhasználó eredetileg unixokon az NFS számára lett dedikálva. A nobody userrel futó privsep processz támadása esetén így lehetőség adódik az NFS megosztások manipulálására is. Hasonló problémát jelent a memcached, amelyik szintén a nobody felhasználót használja a kiszolgálásra. Ez egy rossz gyakorlat, amelyet érdemes elkerülni. Az OpenSSH nem véletlenül használ saját külön felhasználót a privilégium szeparációhoz, ahogy más privsep használó daemonok is (pl. OpenNTP, Postfix, stb.). Emiatt szörnyen hibás az Ubuntu Wiki nobody leírása is a témában...

- az OpenSSH sshd egyébként nem csupán forkolja magát, hanem execeli is. Ennek azért van jelentősége, mert ha a program PIE binárisként (Position-independent executable) van fordítva (és openssh esetén próbálnak törekedni erre) és az operációs rendszer képes ASLR címtér elrendezés randomizálásra, akkor a re-exec következtében a kód szekció is új offset címre kerül minden egyes alkalommal, így megnehezíti egy esetleges ROP (Return Oriented Programming) támadás sikerre vitelét, ahol a már létező programkódokat próbálja a támadó más sorrendben végrehajtani egy megfelelő memóriakorrupciós hiba segítségével (pl. stack vagy heap buffer overflow). Egy sima fork esetén ezek a program kódok, de még a betöltött függvénykönyvtárak is ugyanazon a memóriacímen maradnak, így könyebbé válik egy return-to-libc támadást kivitelezni.

- A fentebb írt re-exec ugyan jó hatással van az egyszerű ROP támadások kivédésére, de továbbra se nyújt védelmet egy szofisztikáltabb támadás ellen, ahol a támadó nem csak "vakon" az ismert, vagy brute-forceolt memóriacímekre alapozva próbál kódot végrehajtani, hanem előtte egy információ szivárgást lehetővé tevő programhibát kihasználva megszerzi az aktuális címtér elrendezettséghez szükséges offseteket és azok birtokában indít pontos, célirányos támadást még ugyanabban a folyamatban (manapság így hajtanak végre támadásokat a szintén privilegium szeparált webböngészők ellen a modern ASLR-képes oprendszereken).

- az OpenSSH sshd a fork + re-exec + root-drop mellett további korlátozásokat és sandbox-mechanizmusokat próbál bevezetni, hogy minimalizálni tudja a támadási felületet. Az egyik régóta jelenlévő megoldás az, hogy a privilégium szeparáció közben a root eldobása előtt még egy chroot("/var/empty") hívást is végrehajt a child processz, így a hitelesítést végző kódban lévő hibák kihasználása esetén sem tud egy támadó kapásból a privsep user nevében hozzáférni a teljes filerendszerhez. Másik trükk, amelyet akkor alkalmaz, ha semilyen fejlettebb sandbox megoldás nem áll rendelkezésre az adott rendszeren, hogy beállítja a child processz által megnyitható file descriptorok számát, file méretét és létrehozható processzek számát nullára, így csak a már nyitott file descriptorokkal tud játszani a támadó a továbbiakban a child processz nevében, de nem képes újabb fileokat és folyamatokat létrehozni kapásból. Ezt a módszert egyébként D.J. Bernstein is használja jóval régebb óta a saját szoftvereiben, sőt, további korlátozásokat is be szokott vetni, pl. az adott processz adatszegmensére vonatkozóan.

- Ezek a remeknek tűnő sandbox korlátozások is megkerülhetők azonban egy jól irányzott kernel támadással, vagy a parent processz IPC-n keresztüli összezavarásával.

- a fejlettebb sandbox mechanizmusokkal sokkal komolyabb korlátozásokat lehet bevezetni a privilégium szeparált processzekre, akár kernel rendszerhívások tiltásával (pl. Linux seccomp, Solaris Privileges, FreeBSD Capsicum, OpenBSD Pledge, stb.). Ennek ellenére garantált védelmet így sem lehet adni mindenfajta támadás ellen. Lásd korábban említett data-only attacks, vagy megfelelő kernel exploit.

- Érdemes a signal kezeléssel is óvatosan bánni, mert az is okozhat meglepetéseket, versenyhelyzeteket, ahogy az OpenSSH és Sendmail esetén meg is történt és a világ egyik legjobb kódauditorának jóvoltából tudunk csak róla (Mark Dowd).

Köszi az összefoglalót, hasznos olvasmány!
Az úgyfél elsődleges kérése az volt, hogy root processznek ne legyen listen portja. Hiába állítom neki, hogy nem jelent biztonsági kockázatot ha csak a fork-olt processz olvas a socketről, egyszerűen nem hajlandó elfogadni, ezért lett ez a megoldás. Az álltalad vázolt technikákat mi sajnos nem használjuk, de majd felvetem a fejlesztés későbbi szakaszában.