Ajattele, älä tottele – Disobey-ratkaisuja

Julkaisimme Skrollin numerossa 2018.1 Selviytymisoppaan Suomen johtavaan hakkeritapahtumaan, Disobeyhin. Disobey-oppaamme on nyt myös Skrollin verkkosivuilla ja kannattaa lukea pohjille, jos tapahtuma ei ole entuudestaan tuttu.

Yksi Disobey-tapahtuman elementti ovat tietomurtohaasteet, joita osallistujat ratkovat tapahtuman aikana. Kerroimme oppaassamme, ettei kukaan vuoden 2018 tapahtumassa ratkaissut Disobeyn virallista hakkerointihaastetta loppuun asti, joskin moni pääsi pitkälle. Tapahtumasponsoreiden tarjoamista lisähaasteista useat kuitenkin ratkaistiin ja vihjailimme artikkelissa kertovamme ratkaisuista myöhemmin – nyt on tullut aika läikyttää näitä papuja.

Disobey on hakkeritapahtumien perinteen mukaisesti toimittajan kannalta suhteellisen epäystävällinen paikka, vaikka muuten ystävällinen tapahtuma onkin. Valokuvata ei saa, joten käytännössä kaikki kuvat tapahtumasta ovat järjestäjien diskreetisti ottamia (myös tämän blogimerkinnän kuvat, kiitos Disobey). Juuri kukaan ei myöskään halua esiintyä omalla nimellään – jopa siinä määrin, että Skrolli myy tapahtumassa jonkin verran lehtitilauksia nimimerkeille (!) – ja tietoa jaetaan esitysten ulkopuolella kitsaasti.

Emme siis ihmetelleet, kun anonyymina pysyttelevä lähteemme – kutsutaan heitä vaikka nimellä paineita – avasi Skrollille tapahtuman haasteiden mysteereitä ja ryyditti paljastuksiaan ohjeistamalla ristiriitaisesti: ”Ei saa jakaa!” Onneksi Skrolli on kilttien hakkereidenkin kaveri, joten yhteisymmärrys jakamisen tavasta löytyi ja julkaisemme nyt viisi välähdystä siitä, millaisia haasteita Disobeyssa oikein ratkotaan ja miten.

Nämä haasteet ovat tapahtumaa sponsoroineiden yritysten järjestämiä, joten ne ovat sieltä hakkerikulttuurin valoisammasta päästä. Osa on helppoja, osa vähän haastavampia – vieläkin vaikeampia olisi. Kaikki ovat luonteeltaan myös enemmän tai vähemmän leikkiä. Esimerkkikirjo kuvastaa kuitenkin hyvin Disobeyn henkeä.

Ratkaisut alla.

Janne Sirén
Digipäällikkö, Skrolli


”Official Testing Company Level 1”

Kaikki Disobey-haasteet eivät suinkaan ole teknisiä tietoverkkomurtoja ja haavoittuvuuksien etsintää – osaa voisi luonnehtia ihan vain aivopähkinöiksi. ”Official Testing Company Level 1” -haaste oli esimerkiksi Facebook-botti. ”Juteltiin, mutta ei saatu auki. Saatiin kuitenkin linkit Level 2-4:ään [jatkohaasteisiin]”, kertoo lähteemme. Huhujen mukaan botti tarjoili Kraftwerk-yhteen sanoituksia, joita lähteemme ei tunnistanut. Taikasana olisi ollut ”Kraftwerk”, jolla haaste olisi ratkennut.

”Darkwater LLC, First flag – Fourth flag”

Toinen haastetyyppi ovat ns. operations security (opsec) -haasteet, jotka perustuivat inhimilliseen tiedonkeruuseen. Remod Oy:lla oli Disobeyssa opsec-haaste aloittelijoille, joka ei edellyttänyt edes teknistä taituruutta, vaan enemmänkin perinteistä salapoliisin silmää. Haasteeseen liittyivät web-sivut ja paikanpäällä ollut Macbook-tietokone, johon oli kytketty USB-muistitikku. Toimitusjohtajan salasana löytyi kotisivuilta ja sillä ratkesi neliosaisen haasteen ensimmäinen vaihe.

Haasteen neljäs osa koski mainittua USB-muistitikkua. ”Macbookissa ollut USB-tikku kopioitiin omalle koneelle lähempää tarkastelua varten, ja ettei DoSsattaisi kenenkään muun ratkomisia”, meille kuvailtiin. Tikulla oli .zip- ja .mp4-tiedostot, joista jälkimmäinen oli asiaan enimmäkseen liittymätön video. Zip taas oli salasanasuojattu. Salasana saatiin Darkwater-haasteen aikaisemmasta vaiheesta.

”Sisältä löytyy toinen video, joka olikin rickroll.” Haasteen ratkaisu oli siis Rickroll.

”Hack the Bill”

Kolmas esimerkkimme onkin sitten jo varsinainen tekninen tietomurto. Tehtävänkuvauksena oli palvelimen verkko-osoite ja kerrottiin, että haasteen ratkaisu – niin sanottu lippu – löytyy palvelimelta Admin-käyttäjän kotihakemistosta. nmap-skannaustyökalu kertoi kyseessä olevan Windows Server 2008 -käyttöjärjestelmää käyttävä palvelin, jossa verkkoportit 139 ja 445 vastasivat huutoon.

Metasploitista löytyi hakusanalla ’eternalblue’ sopiva exploit, joka osoitettiin oikeaan suuntaan ja sanottiin run -> shell. Lippu talteen ja shelli kiinni.” Mielenkiintoisena sivuvaikutuksena EternalBlue-exploit epävakauttaa kohdejärjestelmää, joten palvelin kaatuili myöhempien häkkääjien yrittäessä sen shelliin. ”Kippausongelmat kävivät mielessä jo ennen kuin sanoin ’run’, mutta koska luulin niiden rajoittuvan enimmäkseen XP/2003-sarjaan niin annoin mennä”, lähteemme pohdiskeli.

”Nixu Oyj: Lov^WBacon is in the air”

Hakkerointi ei ole pelkkää kaapelin päässä kyttäämistä, vaan myös radiosignaalit näyttelevät olennaista roolia (kuten Oona Räisänen syvällisemmin kirjoittaa Skrollin Disobey-selviytymisoppaassa). Nixun ”Lov^WBacon is in the air” -tehtävä oli radiotiedustelun kevyemmästä päästä, käytännössä WLANin kuuntelua.

”Ensin verkkolistasta löytyi hetkellisesti ’0xc lbhe fxvyyf yngre ng’, joka kääntyy rot13:sta ’0xc your skills later at’. On siis selvästi joku jatkoviesti.” Tässä vaiheessa ”teoriat oli 1) Etsi SSID:n mainostama kanava, kuuntele kanavaa Wiresharkilla. 2) ’0xc’ viittaa viestin osaan, loput viestit on myös SSID-beaconeita.”

”Ei koskaan saatu oikein tartuttua millä kanavalla tuo 0xc-viesti oli, joten piti tyytyä logittamaan kuullut beaconit ja toivoa parasta.” Kismet-työkalu tuotti pettymyksen, Wireshark jostain syystä taipui paremmin. ”Wiresharkilla voi filtteröidä ’wlan.ssid contains ”0x”’, joka listasi nätisti kaikki relevantit beaconit, ei tosin tietenkään järjestyksessä”, avasi lähteemme. Kun tulokset ajettiin rot13:n lävitse, näkymä oli jotakuinkin tällainen:

0x1 do you like the bacon?
0x2 Enjoy the first flag: DISOBE
0x3 Y{CrispyBaconInTheAirIsTheBe
0x4 stForProfessionals}. For the
0x5 0x6 0x7 0x8 next challenge visit [url]
0x9 Do not worry if you are not
0xa playing the Disobey CTF, you
0xb will have a chance to try
0xc your skills later at

”0xd:tä ei koskaan kuultu. Jäynä?”

Tehtävä paljasti samalla jotain hakkerihaasteiden käytännöistä: ”Toisin kuin muut, Nixun liput vastasivat paremmin tosielämää” – jossa yleensä tiedetään mitä etsitään – ”eivätkä ole mielivaltaisia ’Piilotettiin lippu obskuuristi. Siinähän kaivelet, höhö’.” Nixu noudatti ainoana kohtuullisen yleistä lippumerkintätapaa, joka on TUNNISTE{LipunSisältö}, tässä tapauksessa siis DISOBEY{CrispyBaconInTheAirIsTheBestForProfessionals}.

”Tämä helpottaa elämää, kun yrittää arpoa onko vahingossa kävellyt lipun ohitse.”

”Nixu Oyj: SSObey”

Haasteessa saatiin linkki kotikutoiseen Single Sign-On (SSO) -kirjautumissivuun. Sivulta löytyi latausnappi, jonka kautta sai binääritiedoston. Binääri paljastui käännetyksi Python-ohjelmaksi, jossa oli yhdessä exe-tiedostossa enemmän tai vähemmän kaikki tarpeellinen zip-pakattuna. ”Binwalkilla paketti auki ja seikkailemaan.” Yhdestä tiedostosta löytyi kiinnostava osuus, mutta sekin oli mälsästi binääriä. Onneksi ”löytyi ’uncompyle’, jolla sai pyöräytettyä .pyc:eistä oikein ihmiskelpoista koodia…”

@app.route('/.blacklist')
def blacklist():
    return jsonify(app.config['BLACKLIST'])

@app.route('/test')
def test():
    uid = session.get('uid')
    if uid is None:
        tok = request.args.get('ssobey_token')
        if tok is None:
            return redirect(url_for('.login', returnTo=url_for('.test', _external=True)))
        authinfo = verify_sso_token(tok)
        session['uid'] = authinfo['user']
        return redirect(url_for('.test'))
    return render_template('test.html', uid=uid, flag=app.config['FLAG'])

app.config.from_envvar('APP_SETTINGS', silent=False)
if not len(app.config['SSO_SECRET']) > 24:
    raise AssertionError('The SSO shared secret must be long.')
if not all((chr(c) in string.hexdigits for c in app.config['SSO_SECRET'])):
    raise AssertionError('The SSO shared secret must be hex-encoded because of these stupid UnicodeErrors in Python 3...')

web.pyc

import base64, hashlib, binascii
SCRAMBLE_KEY = ('jB31Jq2mvx1g').encode()
def xor(plaintext, key):
    r = []
    for x, y in zip(plaintext, key):
        r.append(x ^ y)

    return bytes(r)

def obey_the_signature(data):
    secret = xor(binascii.hexlify(app.config['SSO_SECRET']), SCRAMBLE_KEY)
    proof = hashlib.sha256(secret + data).hexdigest()
    return proof

def calc_sso_token(user):
    proof = obey_the_signature(user.encode())
    tok = json.dumps({'user': user,'proof': proof})
    return base64.urlsafe_b64encode(tok.encode())

util.pyc

Koodista paljastuu, että on selvitettävä ”SSO_SECRET”, jotta voidaan luoda token, jota tarjota SSO-kirjautumissivun test-alasivulle GET-parametrina. ”SSO_SECRET on annettujen vihjeiden perusteella heksoja, mutta alustavissa testeissä myös esimerkkikonffissa annettu normaalien ihmisten lause kelpasi token-generaattorille”, pohdiskeli lähteemme. Väsytyshyökkäys (brute-force attack) eli tokenin etsiminen kaikkia vaihtoehtoja ohjelmallisesti kokeilemalla olisi vaihtoehto, mutta liian hidasta ilman lisätietoja.

Lopulta jekku löytyi util.pyc-tiedoston xor-funktiosta – funktio pysähtyy, kun lyhyempi sen parametrina saamista merkkijonoista päättyy. Toinen näistä merkkijonoista, ”SCRAMBLE_KEY”, löytyy koodista ja sen pituudeksi tiedettiin täten 12 merkkiä, mikä olisi merkkijonopituuden yläraja.

Lisäksi koodista selviää, että SSO_SECRET annetaan xor:lle parametriksi hexlify-funktion kautta eli muunnettuna tekstinä esitetyiksi kaksimerkkisiksi heksanumeroiksi. Tämä käytännössä puolittaa SSO_SECRETin merkitsevien merkkien määrän, koska 6-merkkinen SSO_SECRET paisuu hexlifyllä SCRAMBLE_KEYn mittaiseksi eli 12-merkkiseksi.

”Ainoastaan 6 ensimmäistä merkkiä SSO_SECRETistä on merkittäviä ja näiden voidaan olettaa olevan välillä 0-9A-F tai 0-9a-f. Avainavaruus on hyvin siedettävä bruteforcen kannalta”, Skrollille kerrotaan. Väsytyshyökkäyksellä saatu lippu oli DISOBEY{WhenRidingTheElevatorObeyTheExclusiveShaftReservation}.

Luikerteleeko kuvan vasemmassa alakulmassa mekaaninen lohkoketju (blockchain)? Yksi Disobey-tapahtuman vakiohaasteista on myös lukkojen tiirikoiminen.