Pahavara: üks huvitav veebiproksi

Käes on Locked Shields küberõppuse nädal … aga kui sa ei ole LSil, siis on sul kindlasti aega tegeleda päris pahavaraga.

Puhastasin nädalavahetusel järjekordset veebi, kuhu oli sisse pääsetud nõrga admin-kasutaja salasõnaga. Kuna rünne oli toimunud hiljuti, oli mugav võtta ette varukoopia ning sellega võrreldes tuvastada muutunud failid. Ilmnes, et katalooge mööda oli laiali puistatud 35 erinevat tagaust või pahavaralise funktsionaalsusega faili.

Et küberkurjategijate tööd veidi laiemal tutvustada panin välja auhinnad… ja otsustasin koodi Facebookis huvilistele lammutada anda. See blogipost ongi kokku pandud osalt Zone FB-lehe postituse ja Vabakutseliste gruppi jagatu kommentaarides olevatest lahendustest – ning osalt Zone maja-sisestest tähelepanekutest.

Kui tahad enne lahenduse lugemist (või selle kõrvale) koodi vaadata, siis:

Niisiis saab kõik alguse sogastatud koodist:

Kristjan muutis (kasutatud) funktsioonide ja muutujate nimed arusaadavaks ja lisas kommentaarid – saamaks aru, mida koodijupp teeb ja kas seda on üldse ohutu oma arvutis või serveris käivitada:

Kuna muutujas $payLoad olev laeng on URL-kodeeringus mida pahavara-tuvastajatel lihtne läbi näha, on kasutataud täiendavat teisendustabelit selle sogastamiseks:

Olgu lisatud, et algses koodis näeb see eval ehk stringis olevat PHP koodi käivitav funktsioon välja alljärgnev, kurja eval() peitmiseks lihtsamate otsingute eest on funktsiooni nimi eraldi real ning lisaks üks kommentaar:

Kui asendada funktsion eval() näiteks print()’iga saame väljundiks laengu koodi – aga mulle meeldib rohkem tulemus faili kirjutada:

file_put_contents ( '_payload.php', decodePayload( $payLoad, $characterMap ) );

Ja saamegi asuda laengut uurima. Väga levinud tava on keelata ära veateadete väljastamine, aga siin on igaks juhuks võetud maha ka skripti täitmise ajapiirang (mis sisuliselt võimaldab seda kasutada ka teenustõkestusründeks andes kõigile lubatud PHP-protsessidele midagi aegavõtvat teha):

Edasi, otsime päringust maagilist küpsist – AGA Kristjani kommentaar on väikse näpukaga, nimelt väljutakse skriptist kui leitakse mõni küpsis mille väärtus EI VASTA soovitud nimele (küpsise nimi pole seejuures oluline).

Lisaks on koodis aga üks väga tore loogikaviga mille avastas Kurt Moser meie arendustiimist, vaata kas märkad:

Nojah, seda tsüklit ei täideta ja skriptist väljumist ei toimu, kui päringuga pole kaasas ühtki küpsist. Ehk sisuliselt on kogu piirang mõttetu – funktsionaalsele osale saab ligi igaüks.

Edasi läheb huvitavamaks:

Selle jupi juures tasub kindlasti “kastist välja” mõelda – php://input on tavapäraselt kasutusel POST päringute puhul, mitte miski ei keela selle kasutamist ka GET päringus. Sellisel kujul GETi kasutamise võlu seisneb aga selles, et veebiserverid päringu keha harilikult ei logi. Aga olgu siinkohal ka tõestus Elar Langilt:

<?php
var_dump(file_get_contents('php://input'));
?>

$ curl -X GET -s --data "töötab ju" http://localhost/z-server.php
string(11) "töötab ju"

$ curl -X POST -s --data "töötab ju" http://localhost/z-server.php
string(11) "töötab ju"

Järgmise jupi võtan ma aga oma koodiredaktorist, sest see oskab vigu tuvastada:

Jahaa – funktsioon split() kuulutati iganenuks juba PHP versioonis 5.3 ja alates 7.0 on see eemaldatud (tx veelkord Kurtile!). Ehk PHP uuemate versioonide puhul see kood ei tööta ja annab viga – võinoh… kuna vigade väljastamine on ülalpool ära keelatud, siis on tulemuseks Error 500, nagu ilmselt avastas ka ründaja:

94.130.35.51 - - [27/Mar/2019:10:02:21 +0200] "GET /wp-admin/css/colors/sunrise/lngzdhkh.php HTTP/1.1" 500 3527 "" "Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G935F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36" (F9F8DACB-0.001)

Pannes split() asemele explode() saab sellest murest kergesti üle.

decrypt() on sisuliselt XOR krüpteering ehk sama võtmega saab lahti ja kinni – nagu Karl-Sander Erss FBs kommentaaris demos:

> var_dump(decrypt(decrypt('asd'))==='asd');
bool(true)

Kuivõrd võtmeks on $_SERVER[‘HTTP_HOST’]  ja $_SERVER[‘REQUEST_URI’] on ka see kergesti ära-arvatav ehk nagu eelnevalt öeldud saab sellele koodi ära kasutada kesiganes tema olemasolu teab. Nii küpsis kui krüpto on täiesti mõttetud 🙂

Aga … mida see send_data1() siis teeb? Võtab saadud parameetrid (näidisandmed ülalolevas ekraanilaksus) ja teeb nendega HTTP POST või GET-päringu… ja tagastab tulemuse ehk tegu on veebiproksiga:

function send_data1( $data ) {
   $head = "";
   foreach ( $data["headers"] as $key => $value ) {
      $head .= $key . ": " . $value . "\r\n";
   }
   $params = array(
      'http' => array(
         'method'  => $data["method"],
         'header'  => $head,
         'content' => $data["body"],
         'timeout' => $data["timeout"],
      )
   );
   $ctx = stream_context_create( $params );
   $result = @file_get_contents( $data["url"], false, $ctx );
   if ( $http_response_header ) {
      if ( strpos( $http_response_header[0], "200" ) === false ) {
         $result = "HTTP_ERROR\t" . $http_response_header[0];
      }
   } else {
      $result = "CONNECTION_ERROR";
   }
   return $result;
}

Tuleb tunnistada, et file_get_contents() kasutamine koos kontekstiga HTTP-päringu parameetrite määramiseks pole minule kunagi varem ette jäänud, aga väga huvitav meetod. Lisaks: kuna paika pannakse ka timeout ning alguses on skripti täitmise ajapiirang maha võetud… siis võib see väääääga kaua aega võtta ehk olla kasutatav DOS ründeks.

Huvitavat jagub aga veelgi: nimelt EI MÄÄRATA ära HTTP protokolli versiooni ning vaikimisi on selleks HTTP/1.0 – ning minu oletuse kohaselt kasutatakse ülevõetud servereid järgmiste serverite ründamiseks. Hmm, vaatame korraks rünnatud saidi logi, filtreerides õnnestunud HTTP/1.0 päringuid:

Ja tõepoolest, need IPd pärinevad erinevatest pilve- või veebimajutusteenustest. Võib oletada, et ründajatel on oma veebiproksi, mis kasutab ülevõetud serverites olevaid veebiproksisid. See muudab võimatuks lihtsad lahendused nagu IP-põhised piirangud – mh kaitse WordPressi wp-login.php vastu suunatud paroolide jõurünnete tõrjumise “lubame vaid kolm katset samalt IPlt” meetodil.

Tänud kõigile kaasa mõtlemast, loodetavasti saime koos targemaks 🙂

Ära seda soovitust jälgi: täna on rahvusvaheline paroolivahetamispäev

Tõepoolest, väidetavasti on 1. veebruar “change your password day” ning seega äärmiselt sobilik hetk rääkida … tõsiselt halbadest infoturbe-nõuannetest.

Me oleme paroolidega seotust kirjutanud korduvalt: nt Hasso pikem lugu Veel kord paroolidest, siis täpselt seal välja toodud halva parooli-nõuande kasutamise tulemusest Nõrgast paroolist häkitud WordPressini 42 katsega ja loomulikult rahvusvahelise mõõtmega “Paha Panda” varastab postkaste.

Häid nõuandeid jagab aga NIST, mille “Special Publication 800-63B” võtab arvesse seniste parooli-reeglite tekitatud ebaturvalised käitumismustrid. Võid selle spikriks kõrvale võtta ja proovida vastata küsimusele:

Millised järgnevatest EI OLE NIST’i nõuded turvalisele paroolile?

(õiged vastused postituse lõpus)

  1. peab olema vähemalt 10 märki pikk
  2. peab olema vähemalt 8 märki pikk
  3. peab olema vähemalt 6 märki pikk, võib koosneda ainult numbritest
  4. peab sisaldama suur- ja väiketähte, numbrit, märki
  5. peab vähemalt X kuu järel vahetama
  6. ei tohi olla sama, mis viimased X parooli
  7. parooli või kasutajanime sisestamise lahtris ei tohi toimida copy ja paste
  8. ei tohi sisaldada nime, järjestikuseid märke, levinud salasõnu

Kasutasin seda nimekirja just turva-teemalisel esinemisel ja tuleb tunnistada, et paljud kuulajad noogutasid täpselt valede kohtade peal. Põhjuseks see, et IT-osakonnad jõustavad endiselt iganenud reegleid ning neid tirazeeritakse endiselt nii paberil kui digitaalses meedias.

Milline on siis hea ja milline halb reegel?

Hea parooli-nõue on selline, mis ei koorma liigselt kasutajat ning ei sunni teda ebaturvalisele optimeerimisele – inimene pole rumal, küll aga evolutsiooniliselt harjunud leidma kõige tõhustamat teed seatud eesmärgi saavutamiseni. Samas peaks see reegel aitama infosüsteemide arendajatel ja haldajatel meid kaitsta.

Näiteks – selleks, et parooli ei saaks N korda proovides ära arvata, peaks rakendus suutma sellist rünnet tuvastada ja takistada (konto ajutiselt lukustama), ent samas vältida teenustõkestusrünnet läbi kontode lukku ajamise.

Levinud tavad nagu sage vahetamine ja keerukusnõuded annavad aga soovitule vastupidise tulemuse: kasutaja lisab sõnale mõne numbri ja märgi ning siis suurendab sammhaaval numbrit või kasutab selleks aasta-arvu.

Kaks reeglit, mille puhul tasub viidata sellel blogipostile:

  • märgi-põhised keerukusnõuded
  • parooli kohustuslik vahetamine iga X ajavahemiku järel (aga: NIST nõuab vahetamist juhul kui  on kahtlus, et see on lekkinud)

3+2 lihtsat reeglit, mida järgida

  • salasõna asemel räägime sala-fraasist – veidi pikem isekomponeeritud liitsõna või sõnamäng/kalamburism on väga hea (jalgpallisupikulp, lumehangumine, vt insipratsiooni twitter.com/keitivilms)
  • ära kasuta sala-fraasis enda või kasutuskoha nime, levinud (sala)sõnu (Passw0rd!, kalamaja) ja järjestikuseid märgijadasid (q1w2e3r4)
  • oluliste teenuste jaoks (töökoha kasutajakonto, e-post, sotsmeedia) kasuta unikaalset sala-fraasi

Sellega on 98% tööd tehtud, kaks veidi suuremat pühendumist nõudvat soovitust on aga veel:

  • lülita olulistes teenustes (e-post, sotsmeedia) sisse kaheastmeline autentimine (2fa ehk two-factor authentication – nt SMSiga saadetav turvakood)
  • kasuta parooliseifi (LastPass, 1Password, KeepAss) unikaalsete paroolide loomiseks ja haldamiseks, pea meeles ainult üks tugev ehk seifi sala-fraas

Viimase punkti juurde vihjeks, et KeePass on täiesti tasuta ja LastPassis saab erakonto teha tasuta (ja kui su tööandja peaks LastPassi ametlikult kasutusele võtma saad era- ja töökontole ligi ühe sisselogimisega – seejuures mõistagi nii, et tööandja EI SAA sinna ligi 🙂

Ja lõpuks…

See paroolivahetuspäev on Gizmodo klikimeelitustrikk aastast 2012, seeria viimane lugu kannab muideks pealkirja Don’t Change Your Password ning jõuab umbes samade soovitusteni ehk “ära vali lolli parooli”.

Kui su IT-osakond Gizmodot adekvaatseks allikaks ei pea, siis olgu öeldud, et maikuus on tulemas World Password Day ning TheRegister on selle kokku võtnud pealkirjaga It’s World (Terrible) Password (Advice) Day!


Õige vastus – NISTi nõuded EI OLE 1, 4, 5, 6, ja 7.

Nõrgast paroolist häkitud WordPressini 42 katsega

Sattusin puhastama ühte WordPressi, mille puhul oli ilmseks sissetungi-viisiks halduri-õigustes kasutaja parooli ära-arvamine.

Vaatamata sellele, et iga turva-nõuanne hakkab pihta tugeva parooliga ja enamus kasutajaid sedaküsimise peale ka esimese meetmena nimetavad… on nõrga parooli kasutamine endiselt reaalne probleem.

Esimene samm: kasutajanimede kogumine

Seekordne näide põhineb ühe reaalse ründe logifailidel, alustuseks otsitakse WordPressi kasutajanimesid lapates läbi autorite arhiivi:

163.172.xxx.xxx [10/Dec/2018:20:39:22] "GET /?author=1 HTTP/1.1" 301
163.172.xxx.xxx [10/Dec/2018:20:39:22] "GET /?author=2 HTTP/1.1" 301
163.172.xxx.xxx [10/Dec/2018:20:39:22] "GET /?author=3 HTTP/1.1" 404

Kood 301 on edasisuunamine: kui proovida minna lehele https://blog.zone.ee/?author=16, siis on selleks https://blog.zone.ee/author/petskratt/ … ja minu kasutajanimi petskratt on selles kenasti näha.

Teine samm: parooli mõistatamine:

Kui kasutajanimi teada, võib hakata proovima sisselogimist:

163.172.xxx.xxx [10/Dec/2018:20:39:25 +0200] "POST /wp-login.php HTTP/1.1" 200 

Selliseid ridu on logis 41 ning kõigi nende puhul on WordPress väljastanud teate, et parool ja/või kasutajanimi ei klapi.

Ma olen selliseid katseid kogunud ja tüüpiliselt on proovitavad mustrid kasutaja petskratt ja veebilehe zone puhul sellised:

petskratt2017
petskratt2018
P@ssw0rd
petskratt1234
petskratt12345
petskratt@123456
12345678
petskratt@zone
zone2018

Siia sobib kenasti Hasso postitus Veel kord paroolidest ehk kuidas aegunud parooli-nõuded on õpetanud kasutajad justnimelt selliseid lihtsalt ära-arvatavaid paroole kasutama.

Ja niimoodi nad siis huupi lappavad, kuni mõne veebi puhul mõne kasutajaga näkkab. Sedapuhku on juba  42. rida on veidi erinev:

163.172.xxx.xxx [10/Dec/2018:20:39:54 +0200] "POST /wp-login.php HTTP/1.1" 302

302 on jällegi edasi-suunamine ning selle sihiks on /wp-admin … Rohkem IP-aadress 163.172.xxx.xxx logis ei esine, sest töö sai tehtud. 32 sekundiga.

Mõni päev hiljem:

103.76.xxx.xxx [12/Dec/2018:06:06:07 +0200] "POST /wp-login.php HTTP/1.1" 302

Kui 163.172.xxx.xxx oli pärit Prantsusmaa internetiteenusepakkuja võrgust, siis 103.76.xxx.xxx külastab meid Indiast, Tamil Nadu osariigist. Kontrollib logini toimimist ja rohkem teda näha pole.

Kolmas samm: parooli ärakasutamine

Ilmselt läheb toimiv kasutajanimi+parool müüki ning uus omanik asub selle abil saiti lammutama:

46.148.xxx.xxx [17/Dec/2018:22:29:54 +0200] "POST /wp-login.php HTTP/1.1" 302
46.148.xxx.xxx [17/Dec/2018:22:29:54 +0200] "GET /wp-admin/ HTTP/1.1" 200
46.148.xxx.xxx [17/Dec/2018:22:39:29 +0200] "POST /wp-admin/update.php?action=upload-theme
46.148.xxx.xxx [17/Dec/2018:22:39:32 +0200] "GET /wp-content/themes/[...]/db.php?u
46.148.xxx.xxx [17/Dec/2018:23:00:32 +0200] "POST /wp-content/themes/[...]/db.php?u

Ehk siis login, oma teema üleslaadimine, selles oleva db.php tagaukse toimivuse kontroll ja ärakasutamine. Kui huvi, siis see näeb välja selline:

IP 46.148.xxx.xxx viitab Kiievile, aga kuulub sealsele veebimajutusteenusele ja kuigi tegemist võib olla ka VPNi või veebi-proksi kasutajaga viitavad mõned logiread skriptitud tegevusele, näiteks selle üleslaadimis-käsu puhul on logis referrer-väljal http://$st2/wp-admin/theme-install.php?browse=featured.

Mida sellise jama vältimiseks teha?

  • ära kunagi kasuta lihtsat parooli – ei enda kontol ega kolleegile / kliendile konto tegemisel (isegi, kui on plaanis see hiljem ära muuta – tõenäoliselt ei muuda seda keegi)
  • vaata üle veebirakenduse admin’i / halduri õigustes kasutajate nimekiri ja kustuta lahkunud töötajad / endised arendajad jms kasutajad, keda enam vaja ei ole (või pane nende rollik subscriber / lugeja)

Lisalugu: aga kelle parool lekkis?

Kuna selles veebis oli mitu haldurit, siis pakkus mulle suurt huvi konkreetse kasutaja tuvastamine. See ei pruugi alati võimalik olla, aga tasub vaatada wp_user_meta tabelis olevaid session_token kirjeid. Sedapuhku oli kasutaja 2 kohta kirjas järgnev:

a:2:{s:64:"bff45f4a[...]";a:4:{s:10:"expiration";i:1547071064;s:2:"ip";s:11:"77.72.xxx.xxx";s:2:"ua";s:124:"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3198.0 Safari/537.36 OPR/49.0.2711.0";s:5:"login";i:1545861464;}s:64:"a3c7456[...]";a:4:{s:10:"expiration";i:1548253873;s:2:"ip";s:11:"91.90.xxx.xxx";s:2:"ua";s:124:"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3198.0 Safari/537.36 OPR/49.0.2711.0";s:5:"login";i:1547044273;}}

Ülalpool näha oleva 46.148.xxx.xxx loginit ei ole enam näha (sest ta ei ole välja loginud? liiga ammu?), küll aga on näha 2 teist IPd, esimese asukohaks annab MaxMind Stoke-on-Trent’i ja teise omaks Odessa. Huvilised võivad tuvastada ka sisselogimise aja… ning logist on kenasti näha nende askeldamised 🙂

WP GDPR Compliance turvaprobleem

WordPress ja pluginad vajavad igapäevast ja soovitavalt automaatset uuendamist, sest nagu näitab järjekordne juhtum, suurendab uuenduse avaldamine probleemi ärakasutamist.

Sedapuhku on süüdlaseks WP GDPR Compliance plugin, mille koodis ei kontrollita kasutajaõiguseid ning nii saab kestahes WordPressi admin-ajax.php kaudu save_setting funktsiooni poole pöördudes muuta suvalisi seadeid (lisaks saab do_action() abil ligi WP muudele funktsionaalsustele).

Haavatavad on WP GDPR Compliance versioonid kuni 1.4.2 (kaasa arvatud), paigatud versioon on 1.4.3

Tüüpilised ründeviisid näivad hetkel olema sellised:

  • lubatakse kasutajate registreerimine users_can_register ja pannakse uue kasutaja rolliks (default_role) administrator … misjärel saab lisada meelepärase tagaukse muutes mõnda teema või pluginafaili – või enda tööriistakomplekti üles laadida
  • muudetakse ära site_url seaded (veebi baas-url), misjärel hakkab veeb laadima kõiki skripte ründaja poolt antud aadressilt… ning need skriptid suunavad kasutaja edasi kuhu-vaja (veebi aadress ja lehesisu udustatud):


Näites kasutatud veebilehe koodist on näha, kuidas skriptid ja CSS tulevad wtools[.]io pealt:

Varem on kasutusel olnud ka pastebin.com ja erealitatea[.]net

Kuidas puhastada?

Kui veebi home ja siteurl on muudetud, siis on esimeseks probleemiks haldusliidesele ligi pääsemine. Seega on vaja saada ligipääs, uuendada pluginad ja rookida välja kõik probleemne.

  • taasta phpMyAdmin’i kasutades andmebaasis siteurl ja home väärtused – selleks on sinu veebi URL (teataval puhul võivad need ka erinevad olla):

  • logi WP haldusliidesesse sisse ja uuenda WP GDPR Compliance plugin
  • kontrolli üle kasutajate registreerimine – Seaded > Üldine valikust: kasutajate registreerimine võiks mitte olla lubatud (kui see ei ole veebi jaoks oluline funktsionaalsus) ja roll võiks olla Lugeja ehk Subscriber:

  • kontrolli üle kasutajate nimekiri, eemalda tundmatud tegelased
  • ja seejärel kontrolli, ega külalised veel midagi maha ole poetanud – selleks on hea abivahend minu ctimer.php skript, mille kasutamise näite leiab blogipostitusest CSI küber – kas keegi on mu serveris faile muutnud?

Sellest probleemist kirjutavad ka:

pluginvulnerabilities.com – Unlike Wordfence and Other Security Providers We Warned About WP GDPR Compliance Before Websites Started to Get Hacked (08.11.2018)

Wordfence – Privilege Escalation Flaw In WP GDPR Compliance Plugin Exploited In The Wild (08.11.2018)

Sucuri – Erealitatea[.]net Hack Corrupts Websites with WP GDPR Compliance Plugin Vulnerability (09.11.2018) ja Hackers Change WordPress Siteurl to Pastebin (13.11.2018)

Kuidas e-smaspäevaks e-pood kiireks saada?

Kaks korda aastas toimuv e-smaspäeva poodlemis-kampaania on Zone jaoks erilise tähendusega: kuna me oleme aastaid olnud eelistatuim valik e-poe veebimajutuse valikul, jooksevad umbes pooled e-poed meie serverite peal.

Zone 74
Elkdata 16
Cloudflare, Inc. 6
Telia Eesti AS 5
Microsoft Corporation (Azure) 4
Hetzner Online GmbH 3
GleSYS Internet Services AB 3
Radicenter 3
OU cloud.ee 3
Wavecom 3
CITIC Telecom CPC Netherlands B.V. 3
kõik ülejäänud 29

Targa majutuse-valiku kõrval mängib olulist rolli ka veebi-poe kood: kasutusel olev Magento, WordPress vms rakendus võiks suuta lehekülje serveerida vähem kui sekundiga, selle kuvamiseks võiks olla vaja maksimaalselt 100-150 faili ning esilehe suurus peaks jääma alla 1-2 megabaidi.

Testisin 12. novembri kampaanias osalevaid poode ning panin tulemuste põhjal kokku lühikese video peamiste soovitustega:

Lisaks video ülevaatele leiad siitsamast blogist ühe varasema postituse, mis annab veel mitu head nippi, kuidas e-pood paremini tööle panna, kui klikid järgmist linki: https://blog.zone.ee/2018/05/13/seitse-nippi-kiire-e-poeni/