27 minute read

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 flaget DDC{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 nmaps 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:

Encryption using AES_CTR

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:

But I used Encryption

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:

DDC2022 win

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_ids.

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:

Password Strength

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.

Ghidra reverser en binary

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 er 123456789, skal du altså submitte DDC{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.

Thunar fileriddler.hkn

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}