
Egy évvel az előző bejegyzés után újabb autóipari zászlórablós játékról érkezik az újabb (vendég-) poszt: a tavalyi esemény sikerén felbuzdulva a VicOne és a Block Harbor idén is megszervezte a Vehicle Cybersecurity Competition névre keresztelt versenyüket. A tavalyi utolsó pillanatos észbekapással szemben erre már nagyon régóta készültünk — tulajdonképpen annak a versenynek a vége óta. Több szintnyi menedzsmentet bevonva céges pénzt, paripát, fegyvert allokáltunk az eseményre, csapatot kezdtünk szervezni (sőt, több csapat indulása is felmerült), elkezdtük kidolgozni a részletszabályokat, az együttműködés módját, satöbbi.
Így hát érthető, hogy amint megjelent az idei kiírás, azonnal izgatottan kattintottunk — hogy aztán hidegzuhanyként érjen minket az egyik legfontosabb változás: az idei nem csapatverseny lesz, hanem egyéni.
Megvan az a rész az Éhezők Viadalából, amikor a küzdelem végén a Kapitólium bejelenti, hogy mégsem nyerhet két játékos egyazon körzetből, csak egy győztes (értsd: túlélő) maradhat? Na, valami ilyesmi hangulat szállt a teremre (annak ellenére, hogy itt a szervezők igazából soha nem ígértek csapatversenyt). Ja, meg persze most nem a túlélésünkről volt szó, mint abban a viadalban. Ettől függetlenül ez az egyetlen változás sajnos érezhetően visszavetette a jelentkezési kedvet, de végül így is beneveztünk hárman.

Ezen felül volt még egy váratlan változás: egy folyamatos időszak helyett két hétvégére (péntektől hétfőig tartó időszakra) írták ki a versenyt. Ez meg ugye azért volt fontos, mert innentől kezdve egy egészen másfajta menedzsmenttel kellett egyeztetni a versenyre allokálható kapacitásról, ráadásul ebben a menedzsmentben az egyik board-tag kétéves volt, így nem igazán hatottak rá a professzionálisan prezentált cost-benefit analízisek.
Mindenesetre ha döcögősen is, de eljutottunk augusztus 22-ig, a verseny első napjáig. Én pedig a reggeli kávé lefőzése után elfoglaltra állítottam magam Teamsen, és belevetettem magam a kihívásokba.
A szervezők idén két kategóriába osztották a feladatokat: pirosba és kékbe. Ez megfelel a kiberbiztonságban bevett csoportosításnak, ahol a piros csapat a támadó, a kék pedig a védekező. A kékek feladata a biztonsági rendszerek megfelelő megtervezése, megépítése és üzemeltetése, míg a pirosaké a biztonsági rések felfedezése és kíméletlen kihasználása.
Miután gyorsan átfutottam a feladatokat, a kék kategóriának álltam neki, és ez a nekiállás végül olyan jól sikerült, hogy két héttel később, a verseny lezárulta után kaptam egy e-mailt a szervezőktől, miszerint én nyertem meg a kék kategóriát. 😮

A tavalyi posztomhoz hasonlóan idén is terveztem egy technikai jellegű leírást azokról a feladatokról, amelyeket sikerült megoldanom, így innentől ezek a részletek következnek. Akiket ez nem hoz lázba, azok itt nyugodtan befejezhetik az olvasást, köszönöm az eddigi megtisztelő figyelmüket. Az itt maradóknak pedig következzen a kék feladatok rövid leírása.
A pirosakról most nem írok, mert nálam sokkal hozzáértőbbek már megtették — ezúton is ajánlom a verseny összesített győztese, Willem Melching által közzétett blogposztot!
TARA Challenge
Ez a feladat a kék kategória negyedikje volt, mégis ezzel kezdtem, mert láttam, hogy már kora reggel jónéhányan megoldották, tehát nem lehetett annyira szörnyen nehéz. A tippem bejött, tényleg nem volt az, bár azért egy könnyed, reggeli agytornánál többnek bizonyult. Hatalmas előnnyel állhattam viszont neki, ugyanis még ha én személyesen nem is, de néhány kollégám konkrétan TARÁ-kkal kel és fekszik, és azért az ő tudásukból (na meg a közös ISO tréningekből) azért rám is ragadt valami.
A TARA a Threat Analysis and Risk Assessment rövidítése, ezt a dokumentumot az ISO/SAE 21434-es szabvány definiálja. Nagyon okos emberek (például a fent említett kollégáim) ebbe a dokumentumba írják össze a vizsgált autóipari elektronikai rendszerre leselkedő veszélyeket, a lehetséges támadások lépéseit, a köztük lévő összefüggéseket, az általuk okozható kárt, és még sok-sok mindent, majd osztanak-szoroznak, és megbecsülik tudományosan kiszámolják az egyes fenyegetések kockázatának mértékét. Ezután pedig a döntéshozók ezen számítások alapján dönthetnek az egyes kockázatok elfogadásáról vagy csökkentéséről (a többi lehetőséget most hagyjuk).
A feladatban ráadásul egy elképzelt fékrendszer TARÁ-ját kellett elkészítenünk (befejeznünk), és hát mit ad az ég, én meg pont egy fékrendszerekkel foglalkozó cégnél dolgozom. Szóval nem volt olyan lehetőség, hogy én ezt a kihívást ne teljesítsem. 🙂
Természetesen egy TARA elkészítése nagyon bonyolult feladat, amit számos kifejezetten erre a célra készült program támogat. Mi a legelterjedtebb formátumot kaptuk, egy Excel táblát. (Ez itt egy ilyen vicc akart lenni… Nekem legalábbis tetszett, amikor Cosimo Senni, a Stellantis vezető kiberbiztonsági menedzsere az egyik előadásdiájára felírta megjegyzésként, hogy gratulál a Microsoftnak a legnépszerűbb TARA program létrehozásához.)
A tábla amúgy meglepően jól össze volt rakva, és természetesen jó sok minden megfelelően ki is volt töltve, hogy nekünk már csak össze kelljen kattintgatnunk a megfelelő elemeket — ha a nulláról kellett volna elkészítenünk egy TARÁ-t, akkor vagy még most is ott ülnénk, vagy jóval kevesebben (értsd: senki) nem csinálta volna meg azt a feladatot. Erre azért is mernék egy nagyobb összegben fogadni, mert a CTF-ek célközönsége hagyományosan inkább a piros csapatból kerül ki, a TARA-készítés pedig tipikus (és remélem, nem bántok meg senkit, ha azt mondom, hogy) meglehetősen unalmas kék feladat.

Mindenesetre elszöttyögtem vele vagy egy órát, majd miután feltöltöttem a kész Excelt, jöhetett a feladat másik része. Ehhez egy elég hosszú és alapos tesztet állítottak össze a szervezők, amin el kellett érni egy bizonyos ponthatárt, és ha az megvolt, akkor a rendszer megmutatta a hőn áhított flaget.
Bár ezt a kihívást nem éreztem „igazi” CTF-feladatnak, a létjogosultságát egyértelműen aláírom, hiszen valódi kék csapatos feladatról van szó. Tanúsíthatom, hogy a felhasznált példa teljesen életszerű volt, a teszt kitöltéséhez tényleg szükség volt némi előzetes ismeretre (vagy szabványlapozgatásra) a józan ész mellett, úgyhogy szerintem ügyesen passzírozták be a szervezők ezt a témát a verseny keretei közé, jó feladat kerekedett belőle.
Közös kezdetek: Red Alert és SAE EAS
A címben említett két nevet a kék kategória első két feladata viselte. Az elsőről rögtön a ’90-es évek ikonikus játéka jutott eszembe (bár soha nem játszottam vele), a másodikról pedig az ISO „amerikai párja”, a Society of Automotive Engineers. Mint utóbb kiderült, az egyik megérzésem nagyon is helyes volt, a másik pedig egyáltalán nem — de hogy melyik volt melyik, azt még nem árulom el.
Mindkét feladat a szponzor VicOne saját termékében, az xNexusban indult. A kiírás szerint ebben kellett megtalálnunk bizonyos gyanús CAN üzeneteket, amelyekkel aztán továbbléphettünk. Na most itt szeretnék hangot adni azon értetlenségemnek, hogy vajon ezt ki és mikor gondolta jó ötletnek.
Az xNexus a VicOne VSOC (Vehicle Security Operations Center) platformja. Ez az a falméretű képernyő a filmekben, amelyet gondterhelt egyenruhások vagy headsetes-inges statiszták figyelnek, aztán ahogy egyre több helyen bukkannak fel rajta az aggasztó alertek (leginkább: gombostűk egy világtérképen és/vagy felugró ablakok pirosan villogó fejléccel), előbb-utóbb elhangzik a mondat: „Hívjátok az elnököt!”

Egy ilyen rendszer alapvető feladata ugye egyrészt az, hogy segítsen átlátni a dolgokat, átfogó képet nyújtson, de egyúttal lehetővé tegye az adatok részletes vizsgálatát is, le lehessen fúrni a logok legmélyére, az egyes alertek részleteiig.
Egy CTF-nél az ember általában nem tudja pontosan, hogy mit keres. Nézelődik, megpróbál mintákat találni, megérteni, hogy vajon mire utalhat a feladatkiírás szándékosan homályos, rejtélyes megfogalmazása, és reménykedik, hogy valami egyszer csak kirajzolódik a zajból, mintha egy sztereogram kelne hirtelen életre előtte.
Igen ám, de az xNexusban semmilyen módot nem találtunk arra, hogy a rengeteg (több ezres nagyságrendű), gyanúsként megjelölt CAN üzenetet valahogyan kiexportáljuk, vagy legalább egyszerre lássuk akár egy táblázatban, akár egy idővonalon, bárhogyan. (A többes szám annak szól, hogy a verseny Discordján többen adtak hangot az enyémhez hasonló frusztrációjuknak.) Pedig a feladat megoldásának elkezdéséhez ez egy kritikus lépés volt, enélkül nem volt semmink.
Azt hiszem, valamelyik szervező később mintha említette volna, hogy amúgy van export funkció az xNexusban, csak azt a verseny idejére kikapcsolták, mert úgy nem jelentett volna elég nagy kihívást a feladat — na de álljunk már meg egy pillanatra! Vajon milyen fényt vet az — egyébként teljesen természetesen és érthetően — reklámozni kívánt eszközre az, ha kihívás az, hogy a benne lévő adatokat megfelelően elérjük és használhassuk?
Képzeld el, hogy 1983 van, a Microsoft épp piacra dobta a forradalmi szövegszerkesztő programját, a Wordöt. Egy gépíróversenyen, amit a Microsoft szponzorál, az egyik feladat az, hogy a versenyzőknek írógép helyett Wordben kell középre zárniuk egy verset, de mivel ez így még nem lenne elég nehéz, a programban letiltják középre zárás funkciót, így a versenyzőknek addig kell a szóközökkel és tabulátorokkal ügyeskedniük, amíg el nem érik a sor megfelelő karakterpozícióját. Kétség kívül jó feladat lehet az ügyesség, rátermettség mérésére — na de ezek után vajon hányan fognak jó szívvel visszagondolni a Wordre? Egyáltalán — mennyire okos dolog kihívássá tenni a terméked használatát?

Nna, ezennel befejeztem az xNexus rantet, fókuszáljunk a megoldásra. Mivel a rendszer webes alapú, tudtam írni egy olyan Javascript függvényt, ami a Chrome DevTools konzoljában futtatva úgy tett, mintha én a saját kis kezemmel végigkattintgattam volna az oldal összes eseményét, megnyitottam volna a részletes nézetüket, és kimásoltam volna belőlük a számomra szükséges tartalmat egy szövegfájlba. Ja, a slusszpoén, hogy még ezt a függvényt is tizenháromszor kellett lefuttatnom (és alkalmanként kb. egy percig futott), mert ez a remek xNexus egy oldalon legfeljebb 100 eseményt volt hajlandó megjeleníteni. És én még szerencsés voltam, mert aki később állt neki a feladatnak, az még több lappal találkozott — úgy tűnt, hogy a verseny során is folyamatosan generálódnak új, szimulált események. Mondtam már, hogy nem értem, hogyan akarták így népszerűsíteni a terméket?
Egyetlen dolgot azért meg kell hagynom neki: legalább a detektált esetekre egészen jól rá lehetett szűrni az oldal saját szűrőjével. Erre minden bizonnyal rá is kellett jönnünk a megoldáshoz, mert szűrés nélkül egy nagyságrenddel több eseményt kellett volna kiimádkoznunk a rendszerből.
Mindenesetre a botcsinálta web scraping után a kezemben volt egy teljeskörű, minden részletet tartalmazó JSON fájl, amivel már elkezdhettem dolgozni. Ezen a ponton vált szét egymástól a két feladat, az egyikben az xNexus szerint gyanús CAN üzenetek egyik csoportjával kellett dolgozni, a másikban pedig egy másik csoportjukkal.
Red Alert
A feladat szövege így hangzott: „During a routine inspection of in-vehicle traffic, the agency has intercepted suspicious messages on the CAN bus. The frames include a broadcast labeled BEGIN and a data sequence. At first glance, this appears to be a backdoor, but perhaps it isn’t. The key seems to be weak. Our sources say it’s a hint to a broadcast message for our comrades. Determine the frequency in MHz and submit it as a flag.”
„Red Alert”, azaz vörös riasztás és „comrades”, azaz elvtársak? Persze, a comrade fordítható bajtársnak vagy simán csak társnak is, de ez a két szó elég volt ahhoz, hogy elültesse a szovjet bogarat a fülemben. Viszont még egy ilyen bogár sem oldja meg helyettem a feladatot, úgyhogy munkához láttam.
A feladat szövege szerint egy BEGIN tartalmú broadcast, azaz mindenkinek sugárzott üzenetet kellett keresnem. CAN-en minden üzenet broadcast, szóval ezzel nem foglalkoztam, inkább írtam gyorsan egy olyan scriptet, amely az xNexusból kinyert üzenetek tartalmát ASCII karakterekként értelmezte. Így elég gyorsan meglett, hogy a 0x100-as ID-jú üzenetet keresem: ez kétszer szerepelt a logban, az adattartalma pedig mindkétszer 424547494e, azaz BEGIN volt.
Ezután elég hamar rátaláltam a 0x1FF ID-jú másik ASCII üzenetre is, amely szintén többször fordult elő, és az adattartalma mindig 454e44464c414731, azaz ENDFLAG1 volt. Innentől viszonylag gyorsan meglett az a két üzenet közötti adattartalom, amiről a feladat szólt; a szűrt log végül ilyen lett:

Tehát az első és az utolsó előtti üzenetek ASCII karaktereket tartalmaznak, de a közöttük lévőek, a 0x110 és 0x119 közötti ID-júak nem. Nyilván ez volt a rejtjelezett szöveg, amit az elvtársak küldhettek!
Viszont ezen a ponton megállt a tudományom. Ha titkosított szövegről van szó, akkor innentől lehetőségek végtelen skálája nyílik meg. Milyen algoritmust használhattak? AES? DES? 3DES? XOR? Sok algoritmusnak többféle módja is van, melyik lehet a megfelelő? Mi lehetett a jelszó? Honnan vehetem majd észre, ha véletlenül ráhibáztam, és megtaláltam valamelyik paramétert a sokból?
Próbáltam elindítani a ChatGPT-t a szovjet vonalon, és kérdezgettem, hogy vajon milyen rejtjelezési eljárásokat használtak a Szovjetunióban. Adott egy tippet, a GOST-ot, amit meg is találtam a CyberChefen, de mivel annak is szüksége van egy csomó paraméterre, egy idő után eredménytelenül hagytam abba a vaktában lövöldözést, és inkább másik feladatok felé fordítottam a figyelmemet.
Telt-múlt az idő, aztán egy szép hétfői kora délutánon pittyent a telefonom: a szervezők új hintet tettek közzé a Red Alert feladathoz! Megerősítették benne a korábbi gyanúmat, hogy AES titkosításról van szó, de a kulcsról továbbra sem árultak el többet annál, hogy gyenge. De mit jelent ez?! Persze, kipróbáltam a weak szót, de hiába. Ráadásul azt sem tudtam, hogy az AES melyik módját használták, úgyhogy végül írtam egy scriptet, ami a CAN üzenetekből kinyert adaton végigpróbálgatta az AES módjait (IV = 0-val, ahol kellett, mert nem volt jobb ötletem), és akkor ítélt egy dekódolást sikeresnek, ha az eredmény csupa érvényes UTF-8 karaktert, azaz olvasható szöveget eredményezett.
Itt nyisd ki, ha érdekel a Python script!
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import binascii
import os
hex_data = (
'9722a9b59f5e9eca'
'90469c61dab4cb7b'
'568bf34238d88e5c'
'1e8cec17b1b3f31f'
'9a3c0e07738cca40'
'c2589a060f248ab6'
'e2bb72592abd6b61'
'3da429614f3507e3'
'634465b4b52b6c63'
'e41e8b6249a6a769'
)
ciphertext = binascii.unhexlify(hex_data)
iv = bytes([0] * 16)
modes_to_try = {
'ECB': modes.ECB(),
'CBC': modes.CBC(iv),
'CFB': modes.CFB(iv),
'OFB': modes.OFB(iv),
'CTR': modes.CTR(iv),
}
with open('weak_keys.txt', 'r', encoding='latin-1') as f:
for line in f:
key = binascii.unhexlify(line[:32].strip())
for name, mode in modes_to_try.items():
try:
cipher = Cipher(algorithms.AES(key), mode, backend=default_backend())
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
try:
utf8 = plaintext.decode("utf-8")
print(f'{key} {name}: {utf8}')
except UnicodeDecodeError:
pass
except Exception as e:
print(f"{name} error: {e}")
A jelszótippeket a script egy külső fájlból olvasta be, szóval innentől az volt a feladat, hogy 16 bájtos, gyenge jelszavakat állítsak elő. Ezt egy másik scripttel tettem meg, a következő módszerekkel:
- Ismétlődő nibble, azaz hexadecimális karakter: 0000…00, 1111…11, …, FFFF…FF-ig
- Ismétlődő bájt: 0000…00 (igen, jól látod, redundáns az előzővel, de ez akkor egyáltalán nem érdekelt 🙂 ), 0101…01, …, FEFE…FE, FFFF…FF
- Növekvő bájtok (overflow-val): 0001…0F, 0102…10, …, F9FAFBFCFDFEFF00, …, FF00010203040506
- Csökkenő bájtok (underflow-val): az előző alapján könnyen elképzelhető
Ezek közül viszont egyik sem bizonyult megfelelőnek, úgyhogy más irányba indultam. Eszembe jutott, hogy a rockyou.txt rengeteg gyakori jelszót tartalmaz, de ezek közül a legtöbb nem olyan hosszú, mint egy AES kulcs. Ezért kiegészítettem az előző scriptemet egy résszel, amely egy tetszőleges hosszúságú stringből kivág (vagy szükség szerinti ismétléssel készít) AES kulcsként használható szekvenciákat, majd ezeket is beleírja a weak_keys.txt-be. Viszont mielőtt még odaadtam volna a teljes rockyou.txt-t a scriptnek, valamiért rápróbáltam néhány 8 karakteres szóra a feladatkiírásból:
words = [b"comrades", b"redalert", b"backdoor"]
for seq_len in range(2, 9):
for word in words:
weak_keys.append((word[:seq_len] * math.ceil(16 / seq_len))[:16])
Ezzel legeneráltam egy új weak_keys.txt-t, lefuttattam az AES próbálgatós scriptet, és mi jelent meg a képernyőn???
b'backdoorbackdoor' ECB: aHR0cHM6Ly94LmNvbS9zaG9ydHdhdmU3OC9zdGF0dXMvMTkzNTU1NTAxNTUyNDk2NjYyOA==
ASCII eredmény a dekódolás után! A „backdoorbackdoor” jelszó az AES ECB módjával (tehát IV sem kellett) olvasható eredményt adott! A string végén álló két egyenlőségjel pedig egyértelműen elárulta, hogy egy base64-kódolt stringgel álltam szemben. Ezt gyorsan dekódolva egy Twitter (bocsánat, X) bejegyzést URL-jét kaptam, ami bebizonyította, hogy helyes volt a vörös témával kapcsolatos megérzésem!

A tweet egy elfogott rádióüzenet spektrogramját mutatta, a leírásban pedig ott volt a keresett frekvencia, a flag: 11,616 MHz! Sikerült!

SAE EAS
Ez a feladatkiírás sokkal szűkszavúbb volt az előzőnél: „The CAN messages in the traffic seem to be encrypted. Can you help me decrypt it using my key and IV? IV: 3809168903286241, KEY: 9170207685066913″. Itt teljesen egyértelműen kaptunk egy dekódoló kulcsot és egy IV-t, de az algoritmus nem volt meg. És igen, be kell vallanom, hogy nagyon sokáig nem esett le, hogy a cím az AES titkosításra utal, annyira elvitte a figyelmemet
- a SAE → ISO/SAE 21434 vonal,
- az EAS, aminél nem tudtam elvonatkoztatni a cégünk egyik projektjének nevétől, és
- a tény, hogy a két szó egymás tükörképe (palindrom).
Az xNexus-ban talált CAN üzenetek közül szinte mindegyiket felhasználtam a Red Alertben, csak a 0x123 ID-júak maradtak. Ezek az üzenetek sokszor jelentek meg a logban, az adattartalmuk pedig mindig a következő 8 ASCII string közül került ki: GxPUTaeG, msjgDNpr, lXDKFJZe, 9zOdUKa2, rer1vCCg, LN4UCW13, 4KOojhPU, 6LO5hHrj.
Mivel sorrendiséget nem tudtam megállapítani közöttük, ismét csak az eddig is jól bevált brute force módszerhez nyúltam: egy fájlba legeneráltam ezeknek a darabkáknak az összes lehetséges permutációját, soronként egyet.

Azt hiszem, ezen a ponton már erősen gyanakodtam az AES-re mint titkosító algoritmusra, az pedig nem ASCII kimenetet ad, hanem mindenféle barátságtalan bájtokat, úgyhogy egy zsigeri megérzésre hallgatva rádobtam még a permutált adatokra egy base64-dekódolást, és ahol az sikeres volt, ott az így előálló bájtláncokat megpróbáltam dekódolni az AES összes szóba jöhető módjával (CBC, CFB, OFB, CTR). Az előző feladathoz hasonlóan itt is akkor fogadtam el egy kimenetet hihetőnek, ha az ASCII karakterekből állt, és így nemsokára meg is lett a megfejtés: a keresett permutáció a msjgDNprrer1vCCg9zOdUKa26LO5hHrjlXDKFJZeGxPUTaeG4KOojhPULN4UCW13 volt, ez base64-dekódolva mindenféle krikszkrakszokat ad, de azokat az AES CBC-jével visszafejtve, a kiírásban szereplő kulcsot és IV-t felhasználva előállt az eltéveszthetetlen flag: bh{y0u_d3crypt3d_AES_c0rr3ctly}!
Firmware Reveal
A feladatkiírás a következő volt: „One of these firmware files received a surprise injection from a rogue agent. It’s super stealth (sic!), like buried under a mountain of hex. Your job? Dive into the GUI, break the binary open, and reverse engineer the sucker. Somewhere in there is a function dropping a CAN message with a custom arbitration ID. Decode it, trace it, snatch the payload, and show that spy who’s boss.” Ezen felül egy kiterjesztés nélküli, spy_decoder nevű fájl, hat tömörített (*.zip) fájl és két hint is járt a feladathoz: „James Bond” és „ASCII kódok”. A zip fájlok nevei egy jármű különböző vezérlőegységeire utaltak: headlight_firmware, hvac_control_firmware, infotainment_firmware, stb.
Az első, ami feltűnt, hogy amikor a csatolmányok letöltésekor a headlight_firmware-hez értem, akkor a Chrome figyelmeztetést adott, hogy ez rosszindulatú fájlnak tűnik. A többinél ilyet nem tapasztaltam, és arra is hamar rájöttem, hogy miért: az összes többi zip jelszóval volt védve, csak ez az egy nem, ezért a Chrome csak ebbe tudott belenézni. A jelszóval védett zipek feltörése nem tartozik a rutintevékenységeim közé, ezért a könnyebb ellenállás irányába haladva megnéztem, mivel tudok kezdeni valamit: a spy_decoderhez és a headlight_firmware-hez fértem hozzá, és a Notepad++ gyorsan elárulta mindkettőről, hogy Linuxon futtatható ELF fájlok. Nosza, üzemeljük be gyorsan a tavalyi versenyre összerakott virtuális Linuxot, és nézzük, mit látunk ezekben a fájlokban!
Itt most elegánsan átugrom azt a szíváshalmazt, ami a tavaly még minden gond nélkül működő környezet idei életre keltéséhez szükségeltetett. Fájlokat akarsz átküldeni a host rendszerről a virtuális gépre? Sok szerencsét! Frissítenéd a VirtualBox kiegészítőit, hogy hátha az megoldja a problémákat? Hát nem fogod! Ráadásul sietnél az egésszel, mert szorít az idő?

Maradjunk annyiban, hogy a Linuxot pihentettem inkább egy kicsit, és magát a futtatást feladva inkább Binary Ninjával néztem bele a fájlokba, először a headlight_firmware-be. A Binary Ninja egy úgynevezett disassembler és decompiler, ami a gép által értelmezhető programokat megpróbálja ember által értelmezhető formára visszaalakítani. Az egyik nagyon hasznos funkciója a stringek megkeresése, azaz ha a programba be van égetve valamilyen szöveg, akkor azt megtalálja és kilistázza. Ezzel a funkcióval nagyon ígéretes stringeket találtam:
- Sending CAN message wit
- h arb
- itration ID 0x1C0:
- %02X
- ASCII
Ez épp megfelelt egy printf format stringnek, tehát biztosra vehettem, hogy a program ki tudja írni az általam keresett információt. A kérdés csak az volt, hogy vajon ki is akarja-e majd írni azt.
Ha már nyitva volt a Binary Ninja, ránéztem a spy_decoderre is. Abban rengeteg stringet találtam, de egyik sem tűnt számomra relevánsnak, ellenben nagy részük a Python nyelvre utalt. Ez eléggé összezavart, hiszen a Python egy interpretált nyelv, előttem pedig egy lefordított, futtatható bináris hevert. Némi keresgélés után ráleltem, hogy léteznek Pythonhoz készült csomagolók (szándékosan nem fordítót írtam), amelyek fognak egy Python scriptet, a hozzá szükséges package-eket meg a futtatáshoz szükséges interpretert, és az egészből összegyúrnak egy futtatható fájlt. Az így készült fájlnak az az előnye, hogy a futtatásához nincs szükség külön Python környezetre, a hátránya pedig (legalábbis számomra) az, hogy nem tudok beleolvasni a forráskódjába.
Azaz… Vajon tényleg nem tudok?
Hamarosan arra is rájöttem, hogy a konkrét fájl a PyInstaller nevű csomagolóval készült, és ami a lényeg: hogy ezt a csomagot bizony ki lehet nyitni! Nosza, pyinstxtractorral ki is nyitottam, a benne lévő bájtkódra fordított fájlokat uncompyle6-tal visszaalakítottam Python scriptekké, és a rengeteg mellépakolt függőség mellett hamar meglett a lényeg, maga az applikáció is: a spy_decoder.py.
Ez a fájl ráfért egy képernyőre, összesen 34 sor kódból állt. Ott feküdt előttem kiterítve, és mivel szerencsére volt már dolgom a Python „alapértelmezett” GUI moduljával, a tkinterrel, hamar átláttam a lényegét: a felhasználó beírhatott egy jelszót egy szövegmezőbe, és ha az stimmelt, akkor megkapta egy bizonyos secret.txt tartalmát base64-dekódolva.
A jelszó fájdalmasan egyszerű volt: 007. Erre utalt hát a feladat szövegében szereplő „James Bond”. A secret.txt (amelyhez kicsomagolás után szintén hozzáfértem) tartalma pedig aGVhZGxpZ2h0X2Zpcm13YXJlLnppcA== volt, ami base64-ül annyit tesz: headlight_firmware.zip.
Hm.

Tehát akkor itt most az van, hogy az egész spy_decoder (és a hozzá tartozó egyik hint) értelme, egyetlen célja az volt, hogy rámutasson, hogy a headlight_firmware-rel kell foglalkozni. De hát én ezt már tudtam! Hiszen csak az nem volt jelszóval védve, és annál szólt a Chrome, hogy vigyázzak vele! Akkor most mi van?!
Az mindenesetre még inkább bizonyosságot nyert, hogy a headlight_firmware-rel kell továbbmenni, így miután nemsokára összeállt egy Linux környezet, gyorsan le is futtattam benne a binárist. És láss csodát, ki is írt valamit!
Sending CAN message with arbitration ID 0x1C0:
Data: 62 68 7B 48 65 61 64 4C 69 68 74 7D 61 67
ASCII: bh{HeadLight}ag
Ott volt a flag feketén-fehéren, bh{HeadLight}! Oké, a hozzáragasztott ag egy kicsit furcsa volt, de kicsire nem adunk! Uzsgyi, beírtam a megoldást, Enter, és…

Micsoda?! De hát ott a flag, stimmel a bh{…} formátum, lefuttattam a fájlt, kiírta, amit ki kellett, az adatbájtok és az ASCII kódjuk stimmel… Vajon miért nem fogadja el a rendszer?!
Próbáltam kisbetűvel-nagybetűvel, ag végződéssel és anélkül… de hiába.
Nem csak engem zavart össze a helyzet, további magyarázat helyett inkább mutatom, mi zajlott eközben Discordon (a neveket adatvédelmi okokból megváltoztattam, én „én” vagyok, a szervezőket pedig *-gal jelöltem, bár ez ott igazából nem látszott):
[32üs7há7ú] Helló, a #3 Firmware Reveal megoldása közben valami nagyon flag-gyanús dolgot találtam (bh{}), de nem sikerül helyesen megadnom. Valaki más is beleütközött ebbe?
[én <- 32üs7há7ú] Nálam is ez van. Már épp fel akartam adni egy ticketet, de így akkor inkább csak megvárom a választ itt 🙂
[*kódv1vő] Próbálkozzatok tovább — minden zip jelszóval védett 🙂
[én] Nem, az egyiken nincs jelszó
[*kódv1vő] A spy_decoderre gondolsz?
[én] Nem, azon kívül. Az egyik zip fájl nem jelszóvédett. Lehet, hogy valami félrement?
[Spártai] Küldd csak be ide a flagedet, hogy mind ellenőrizhessük 😀
[*feledés <- Spártai] Ez kizárást ér, légyszi olvassátok el a szabályokat
[Spártai <- *feledés] Jójó, vicc volt
[én <- Spártai] Köszi, de inkább nem 😀 De ha valaki meg tud győzni, hogy szervező, akkor annak privátban el tudom küldeni
[*feledés <- én] Nyugodtan nyiss rá egy ticketet 🙂
[*kódv1vő <- én] Hmm… fura. Nekem jelszóvédettnek tűnik, légyszi folytassuk egy ticketben
[cucchekkelő <- én] Meg tudom erősíteni — én is így akadtam rá a flagre, de aztán ugyanabba a problémába ütköztem, mint te (a flag a bh{} ellenére érvénytelennek tűnik). Lehet, hogy mindketten még csak a feladat felénél járunk?
[én] Lehet, de az nagyon zavaró lenne
[Ozirisz] Ja, az egyik zip nem jelszóvédett, nálam is ez van
[cucchekkelő <- én] Igen, én is úgy értettem, hogy a bh{} mindig flagre utal — kipróbáltam sok variációt, hogy nehogy benézzek valami nyilvánvalót… Úgyhogy a találati arányom ment a levesbe 😀
[Lu hercege] A spy_decoder visszafejtése után meg voltram győződve, hogy a jelszó nélküli zip flagje csak átverés… Esetleg a feladatkiírás nem volt elég precíz
[*kódv1vő] @mindenki: A headlight_firmware.zip tényleg jelszóvédett. A teljes kicsomagoláshoz használjatok WinRAR-t vagy a Kali Linux gyári kitömörítőjét, azoknak kérniük kell a jelszót. Ismétlem: az általatok megadott jelszó hiányos, de már nagyon közel jártok.
[*kódv1vő] Azért kaptatok hiányos jelszót, mert a Windows gyári tömörítője hibásan csomagolta ki a binárist.
[*kódv1vő] Fffúúú, jól rám ijesztettetek 🙂
[*kódv1vő (negyed órával később)] @mindenki: Nem tudjuk, mi történhetett a feltöltött headlight_firmware-rel (talán megsérülhetett vagy ilyesmi), de mindenesetre frissítettük a fájlt. Próbáljátok meg most 🙂
Hát, volt egy kis fejetlenség, na. Letöltöttem a frissített zipet, ami már tényleg jelszót kért (Windowson is), de jelszóra utaló hintet nem kaptunk. Mint némi próbálkozás után kiderült, szerencsére a fentebb már említett rockyou.txt-ből választottak jelszót a szervezők, úgyhogy némi brute force-olás árán sikerült megszerezni a frissített újracsomagolt firmware-t. Ezt Linuxra átimádkozva és ott lefuttatva pedig előállt a flag, amit már el is fogadott a rendszer: bh{h3aDL!ght$_FirmWaR3}.

Nem tudom, mit gondoljak erről a kavarásról a zip fájlokkal. A hivatalos kommunikáció szerint ugye csak egy szerencsétlen (vagy szerencsés, kinek milyen) véletlen folytán lehetett kicsomagolni az első verziót Windowson. Na de ki látott már olyat, hogy egy hibásan (hibásra) kicsomagolt futtatható fájl gyakorlatilag tökéletesen működik, csak itt-ott sérül meg annyira, hogy a kiírt szöveg (amit persze nem tudom, hogyan tárolt, de nem sima stringként) változzon meg? Ráadásul még hihetetlenebbé teszi ezt az elméletet az, hogy a hibás kiírás egy amúgy teljesen hihető flaget adott.
Az én tippem inkább az, hogy a szervezők ebből a fájlból véletlenül egy régebbi tesztverziót töltöttek fel, ami még nem a végleges flaget tartalmazta, és ami nem is volt jelszóval védve. Ha ez történt, akkor kár érte, mert rövidre zárta a feladat egyik érdekes részét, a spy_decodert, a maradékot pedig egy zip fájl brute force-olásává degradálta. Pedig az ötlet nagyon tetszett, és még a fent részletezett vargabetűkkel is megvolt az az ismerős adrenalinlöket, amikor megjelent a képernyőn a helyes flag!
Összességében
Mindent összevetve azt éreztem, hogy a feladatok kellően nehezek, néha egy kicsit túl nehezek is voltak, de egy kollégám erre finoman jelezte, hogy ezt az érzést azért tudásbeli hiányosság is okozhatta. 🙂 Viszont a kihívásokhoz körített háttértörténetek nagyon tetszettek (a Red Alert volt a kedvencem), maguk a feladatok teljesen életszerű, valamennyire tényleg autóipari problémákat dolgoztak fel: CAN kommunikációt, firmware-eket, kommunikációs és titkosítási protokollokat. Egy piros csapatos feladat (AutoGraph) például nagyon érdekesen, tanulságosan és kézzelfoghatóan mutatott be egy számomra új kriptográfiai sérülékenységet, szóval tanulni is sokat lehetett a versenyből. Látszott, hogy sok munkát tettek bele a szervezők, és az is nagyon tetszett, hogy Discordon ők maguk is valós időben elérhetőek voltak. Helyükön voltak a szervezés egyéb aspektusai is, az infrastruktúra, az arculat, a promózás, a kommunikáció mind profizmust sugalltak.

Idén nekem is sikerült egy kicsit jobban megtalálni a workCTF-life egyensúlyt (bár azért az optimálistól még így is messze voltam), és a tavalyival összevetve azt látom, hogy tényleg sikerült fejlődnöm azóta, mert sokkal otthonosabban használtam már azokat az eszközöket is, amelyekkel ott még csak ismerkedtem.
Hogy hogyan tovább? Erre a választ — a mostani fejemmel legalábbis — könnyen rávágom: jövőre veletek ugyanitt!
Hogy mit honnan:
- Unreal Tournament kék zászlót erről a blogról
- Clint Eastwoodékat, Octavia Spencert és a tücsköt giphyről
- Planetáriumi kasszás nénit Napirajzról
- SOC-ot a ResearchGate-ről
- Confused Nick Youngot a Daily Dotról
- Red Alert screenshotot a MobyGamesről
- Dorát, a felfedezőt ProProfsról
- Ken Jeongot Tenorról
