CertCTF 2025
Hade nöjet att delta i CertCTF som anordnades som en del av en kandidatuppsats inom digital forensik pĂ„ Högskolan i Halmstad i samarbete med MSB och CERT-SE. För en som inte tidigare “tĂ€vlat” i CTF:er, utan bara tuggat igenom diverse Hack The Box, TryHackMe och OverTheWire sĂ„ var CertCTF otrligt roligt och lĂ€rorikt.
Passade Ă€ven pĂ„ att dokumentera de flaggor jag lyckades samla pĂ„ mig. Nedan följer en liten braindump jag knĂ„pade ihop baserat pĂ„ mina anteckningar. Jag fick tyvĂ€rr inte ihop alla flaggor jag hade hoppats pĂ„, men jag Ă€r nöjd med att ha placerat mig pĂ„ 11:e plats av 61 deltagare. Inte illa för att vara första gĂ„ngen! đ
NÀr CTF:en startade fick man tillgÄng till en zip-fil innehÄllandes:
- NÀtverksdump ifrÄn angreppet
- Windows-loggar ifrÄn en server som var utsatt
- Minnesdump frÄn den utsatta servern
Inkluderat var Àven scenariot för CTF:en som finns att lÀsa nedan.
Intro
Gentle Dentals IT-ansvarig, Micke, har precis avslutat sin 45 minuter lÄnga kafferast dÀr han och chefen för tandvÄrdskliniken flitigt diskuterat dyra IT-sÀkerhetslösningar som chefen bedömer onödiga. NÀr Micke sÀtter sig vid skrivbordet fÄr han höra av tandlÀkaren Per att skrivaren beter sig konstigt.
NÀr Micke undersöker skrivaren fÄr han syn pÄ en mini-dator som sitter inkopplad pÄ ethernet-porten som skrivaren brukar anvÀnda. Micke fÄr panik och inser att nÄgot fruktansvÀrt kan ha hÀnt. Han ringer dig som kan det hÀr med IT-attacker och ber om hjÀlp dÄ Gentle Dental har vissa viktiga, högt uppsatta kunder.
Du fÄr ut all nÀtverkstrafik som skedde under Mickes kafferast pÄ företagets nÀtverk tillsammans med hÀndelseloggfiler och en minnesdump frÄn deras fil-server. Ditt uppdrag Àr att utreda IT-attacken som skett för att ta reda pÄ vad som har hÀnt.
Angriparens IPv4-address
Vilken IPv4-adress anvÀnde angriparen sig av initialt i attacken?
Format för svaret: 111.222.333.444
Konstaterade att det skickades otroligt mycket ARP-förfrÄgningar ifrÄn en specifik address, det ser helt klart ut som att nÄgon skannar nÀtet efter svarande hosts.
Flagga: 192.168.177.141
DomÀnkontrollantens IPv4-adress
Vilken IPv4-adress hade domÀnkontrollanten?
Format för svaret: 111.222.333.444
DomÀnkontrollanter pratar ofta LDAP, en sökning i nÀtverksdumpen pÄ ldap sÄ kan man konstatera att 192.168.177.129 snackar LDAP pÄ port 389 och troligtvis Àr domÀnkontrollanten
Flagga: 192.168.177.129
FTP-Serverns IPv4-adress
Vilken IPv4-adress hade FTP-servern?
Format för svaret: 111.222.333.444
FTP anvÀnder oftast port 21. Söker man i nÀtverksdumpen efter trafik pÄ port 21 sÄ kan man se att angriparens server försöker prata mot flera IP adresser, men att det bara Àr 192.168.177.155 som svarar.
Flagga: 192.168.177.155
Angriparens hostname
Vad var angriparens hostname?
Lite klurigare men Ă€ndĂ„ ganska lĂ€tt. NĂ€r man kollar pĂ„ nĂ€tverksdumpen och filtrerar pĂ„ angriparens server sĂ„ kan man se att den gjorde den DHCP request. I DHCP requesten finns under Option 12 hostens hostname rakt upp och ned đ
Flagga: Kali
Tidszon
Vilken lokal tid (svensk tid) anlÀnde sista paketet i inspelningen av nÀtverkstrafiken? Avrunda nerÄt till hel sekund!
Format för svaret (HH:MM:SS): 01:23:45
HÀr var det bara att sÀtta wireshark till att visa lokal tid och scrolla lÀngst ned i nÀtverksdumpen.
Flagga: 09:42:01
Windows Defender
Vilken MpPreference parameter Àndrade angriparen för Windows Defender Antivirus?
Format för svaret: ParameterNamnet
HĂ€r fick man ta en titt i Windows defender loggarna.
Notera att registervÀrdet
HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions
Àndras pÄ nÄgot sÀtt. Detta Àr samma sak som att Àndra i
Get-MpPreference | Select-Object ExclusionPath
Flaggan blir dÀrmed: ExclusionPath
Obehörig inloggning
Angriparen lyckades logga in pÄ en av datorerna pÄ nÄgot sÀtt. Vilket Logon ID tillhör den obehöriga inloggningen?
Format för svaret: 0x123AB
Tittar man i Security loggarna efter events med Titta i Security loggarna efter angriparens lokala IP 192.168.155.141 sÄ hittar man denna lyckade inloggning, jackpot!
Flagga: 0xFFCAB
Lösenord
Vilket lösenord anvÀnde angriparen för att logga in i utmaningen Obehörig inloggning
Format för svaret: Password123
Baserat pÄ loggen i Obehörig inloggning vet vi tidpunkten för inloggning samt att autentiseringen gjordes via NTLM V2. NTLM V2 Àr vida kÀnt att det Àr ett osÀkert sÄ detta borde man kunna knÀcka. Vi har Àven tillgÄng till nÀtverkstrafiken för autentiseringen.
För o knÀcka NTLMV2 behöver man veta lite saker: User, Domain, Challenge, NTLMV2Response samt HMAC-MD5.
Challengen kan vi hitta i Session Setup Responsen för NTLM autentiseringen
Challenge: NTLM Server Challenge: 19c218f2f9a912c1
HMAC-MD5 och NTLMv2Response hittar vi i sjÀlva autentiseringen
NTProofStr Àr samma som HMAC-MD5
HMAC-MD5: 7db9780888f4832bd162cf679cbffc0e
Scrollar man ner lite i samma packet sÄ hittar vi Àven User och Domain
NTLMv2Response: 010100000000000080bf6de99591db01f7bccb719a3b1d400000000002001800470045004e0054004c004500440045004e00540041004c0001001400460054005000530045005200560049004300450004002400670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c0003003a0046005400500053006500720076006900630065002e00670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c00070008001829aae99591db010000000000000000
Detta kan man sedan lÀgga ihop till ett format som hashcat kan knÀcka
FTPService::gentledental.local:19c218f2f9a912c1:7db9780888f4832bd162cf679cbffc0e:010100000000000080bf6de99591db01f7bccb719a3b1d400000000002001800470045004e0054004c004500440045004e00540041004c0001001400460054005000530045005200560049004300450004002400670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c0003003a0046005400500053006500720076006900630065002e00670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c00070008001829aae99591db010000000000000000
AnvÀnd valfri wordlist, t.ex rockyou
Kör igÄng katten med
hashcat.exe -a0 -m5600 hashcatcrack.txt rockyou.txt
a0 specificerar straight mode, dvs applicera en wordlist.
m5600 specificerar hash typen, i detta fall NetNTLMv2 hashes.
hascatcrack.txt Àr strÀngen vi satte ihop tidigare.
rockyou.txt Àr wordlisten vi anvÀnder.
Sverige, vi har ett resultat!
Session..........: hashcat
Status...........: Cracked
FTPSERVICE::gentledental.local:19c218f2f9a912c1:7db9780888f4832bd162cf679cbffc0e:010100000000000080bf6de99591db01f7bccb719a3b1d400000000002001800470045004e0054004c004500440045004e00540041004c0001001400460054005000530045005200560049004300450004002400670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c0003003a0046005400500053006500720076006900630065002e00670065006e0074006c006500640065006e00740061006c002e006c006f00630061006c00070008001829aae99591db010000000000000000:DentalSurgery528
Lösenordet finns i slutet av strÀngen.
Flagga: DentalSurgery528
Angriparens server
Vilken IPv4-adress hade angriparens egna server?
Format för svaret: 111.222.333.444
I Powershell Operational loggarna kan man se att powershellscriptet som körs av angriparen b.la modiferar DNS Doh
Kort utklipp ur loggen:
$script:ClassName = 'ROOT/StandardCimv2/MSFT_DNSClientDohServerAddress'
Detta i kombination med den trafik som kan observeras gÄ ut ifrÄn FTP servern till en okÀnd IP, men som klassificeras som DNS trafik, fick mig att tÀnka att data exfiltreras via DNS. DNS-anropen ser Àven ytterst misstÀnkta ut, varför skulle nÄgot prata med Facebook domÀner helt random?
Drog en kvalificerad gissning att 10.245.122.37 Àr angriparens server och det visade sig stÀmma.
Flagga: 10.245.122.37
Ransomware
Utöver den “vanliga” utmaningen fanns det Ă€ven med en delutmaning gĂ€llande ransomware. Scenariot kan man lĂ€sa nedan.
Din kollega som har varit ansvarig för diskforensiken hittade en exekverbar fil och tre andra textfiler. En fil med kÀnslig data verkar nu innehÄlla nÄgon form av krypterad data. Kollegan Àr rÀtt sÀker pÄ att den kÀnsliga filen gÄr att dekryptera och kommer till dig och ber om hjÀlp dÄ du Àr expert inom omrÄden sÄsom reverse engineering och kryptering.
Vad Àr nyckeln för att dekryptera filen?
Tillsammans med introt fick man Àven tillgÄng till filerna:
- ransom_note.txt
- pubkey.txt
- sensative_data.encrypted
- enc.exe
enc.exe anvÀndes för att kryptera filen.
pubkey.txt innehöll en publik nyckel: 506395958
sensative_data.encrypted innehöll krypterad data i formatet
(540488803, 187826906) (512275051, 36345508)...
Om man öppnar upp enc.exe i Ghidra och pillar runt lite sÄ kan man hitta funktionen nedan som gissningsvis anvÀnds för att generera den public keyn.
Efter mycket googlande, och lite AI-chattande, fick jag följande svar som kÀndes rimligt:
void gen_pub_k(uint param_1)
{
mod_exp(2, param_1, 0x40000017);
return;
}
mod_exp(base, exponent, modulus): This function is likely performing modular exponentiation:
result = (2^{\text{param_1}}) \mod 0x40000017 The value 0x40000017 in decimal is 1073741847, which is close to 2^30 , possibly chosen for cryptographic reasons.
param_1 is the exponent (probably a secret key or part of the key derivation process).
Bara ett par klick bort i Ghidra sÄ hittar man mod_exp funktionen.
Exakt hur denna fungerar Ă€r för mig en gĂ„ta, man skulle kanske lĂ€st lite krypteringskurser pĂ„ högskolan… Men ChatGPT hade ett svar!
Med det sagt hjÀlpte den ocksÄ till att generera ett Python-script för att rÀkna ut den privata nyckeln, om nÄgot fick man modifiera det lite sÄ att det skulle fungera..
import math
def brute_force_discrete_log(base, modulus, public_key, max_attempts=2**24):
"""Brute-force method to find x in base^x ⥠public_key (mod modulus)"""
value = 1 # Start with base^0 = 1
for x in range(max_attempts):
if value == public_key:
return x # Found private key!
value = (value * base) % modulus # Compute next power
return None # Not found within the attempt limit
def baby_step_giant_step(base, modulus, public_key):
"""Baby-step Giant-step algorithm for discrete logarithm"""
m = math.isqrt(modulus) + 1 # Step size
table = {}
# Baby-step: Compute base^j % modulus for j in [0, m]
value = 1
for j in range(m):
table[value] = j
value = (value * base) % modulus
# Compute base^(-m) mod modulus (modular inverse of base^m)
base_inv_m = mod_inverse(pow(base, m, modulus), modulus)
# Giant-step: Compute public_key * base^(-i*m) % modulus
value = public_key
for i in range(m):
if value in table:
return i * m + table[value] # Solution found
value = (value * base_inv_m) % modulus
return None # No solution found
# Given values
base = 2
modulus = 1073741847 # 0x40000017
public_key = 506395958 # Example public key (replace with actual value)
# Try brute-force first (for small keys)
private_key = brute_force_discrete_log(base, modulus, public_key)
if private_key is None:
print("Brute-force failed, trying Baby-Step Giant-Step...")
private_key = baby_step_giant_step(base, modulus, public_key)
if private_key is not None:
print(f"Recovered private key: {private_key}")
else:
print("Failed to recover the private key.")
Kör man detta sÄ resulterar det i att man fÄr ut den privata nycklen som anvÀndes vid krypteringen, 177370085.
I Ghidra kan man kolla vidare och hitta följande funktion, som anvÀnds för att kryptera datan.
void eg_enc(int param_1,uint param_2,uint *param_3,uint *param_4)
{
int iVar1;
uint uVar2;
uint uVar3;
iVar1 = rand();
uVar3 = iVar1 % 0x40000016 + 1;
uVar2 = mod_exp(2,uVar3,0x40000017);
*param_3 = uVar2;
uVar3 = mod_exp(param_2,uVar3,0x40000017);
*param_4 = (uVar3 * param_1) % 0x40000017;
return;
}
Men Ànnu lite AI hjÀlp kan man fÄ ut följande lösning:
Ăven hĂ€r fick man lite hjĂ€lp med att generera ett script som avkrypterar filerna med hjĂ€lp av private keyn.
from sympy import mod_inverse
def elgamal_decrypt(private_key, p, c1, c2):
"""Decrypts a single ElGamal encrypted pair (c1, c2)."""
s = pow(c1, private_key, p) # Compute shared secret: s = c1^private_key mod p
s_inv = mod_inverse(s, p) # Compute modular inverse of s
M = (c2 * s_inv) % p # Recover plaintext
return M
# Given values
p = 1073741847 # Prime modulus
private_key = 177370085 # Recovered private key
# Encrypted data from file
encrypted_pairs = [
(540488803, 187826906), (512275051, 37345508), (231599851, 823437630),
(631399654, 628252041), (1035050165, 1006835836), (896417195, 206453254),
(14141576, 263001145), (204987116, 186642158), (606374554, 598224082),
(547306682, 128791588), (587262377, 531917810), (982294489, 538345953),
(471862643, 967908048), (634400714, 763656344), (375171454, 544183238),
(1055094817, 793021731), (39117044, 951544349), (361114958, 4360479),
(327139844, 501552997), (467622466, 316485080), (36903752, 556784675),
(44658571, 501318067), (443001524, 1031119903), (555206443, 620324572),
(499990172, 366058919)
]
# Decrypt all pairs
decrypted_messages = [elgamal_decrypt(private_key, p, c1, c2) for c1, c2 in encrypted_pairs]
print("Decrypted messages:", decrypted_messages)
Kör man detta resluterar det i gÀng med ASCII vÀrden:
Decrypted messages: [mpz(80), mpz(101), mpz(108), mpz(108), mpz(101), mpz(95), mpz(83), mpz(118), mpz(97), mpz(110), mpz(115), mpz(108), mpz(111), mpz(115), mpz(95), mpz(72), mpz(97), mpz(114), mpz(95), mpz(83), mpz(118), mpz(97), mpz(110), mpz(115), mpz(10)]
Som i sin tur kan avkodas till text:
decrypted_ascii = [80, 101, 108, 108, 101, 95, 83, 118, 97, 110, 115, 108, 111, 115, 95, 72, 97, 114, 95, 83, 118, 97, 110, 115, 10]
decrypted_text = "".join(chr(c) for c in decrypted_ascii)
print(decrypted_text)
Resultat?
Flagga 1: 177370085
Flagga 2: Pelle_Svanslos_Har_Svans
Slut pÄ det roliga
Kan absolut inte sÀga att jag förstÄr allting som görs, speciellt inte den bakomliggaande mattematiken, men nÄgonting nytt har man ÀndÄ lÀrt sig. Att decompila binÀrfiler och hitta funktioner i den var ÀndÄ riktigt kul! Utredningsflaggorna kÀnde jag mig hemma pÄ, medan ransomware var betydligt klurigare.
Man kan inte slÀnga under stolen att AI Àr ett anvÀndbart verktyg, speciellt nÀr man sjÀlv har ett litet hum om vad det Àr man vill fÄ ut. Utan ChatGPT och Deepseeks utvecklande resonemang hade jag lÀtt fastnat pÄ vissa bitar. Handlar helt enkelt om att anvÀnda dem pÄ rÀtt sÀtt, man fÄr se dem som en förlÀngning av ens egna förmÄga.
Hade jag haft lite mer tid under helgen Àr jag sÀker pÄ att hade kunnat ta nÄgra av de andra flaggorna, speciellt de som hade med autentiseringen att göra.
En full writeup av CTF:ens alla flaggor finns pÄ organisatörernas github: https://github.com/heltseriost/CertCTF2025-Writeup
LÀste igenom samtliga och blir lite frustrerad nÀr man inser att man var sÄ nÀra att lösa en flagga, men inte riktigt nÄdde hela vÀgen. En riktig miss jag gjorde var att inte titta pÄ minnesdumpen, fastnade totalt i event loggar och decompiling. Men det Àr en del av att fortsÀtta lÀra sig.
Till nÀsta CTF, som inte Àr allt för lÄngt borta, tar jag med mig detta. Att inte fastna allt för djupt i detaljerna, likasÄ försöka bilda mig en bild av hela hÀndelseförloppet och inte bara varje individuell flagga.
Allt som allt en rolig CTF, speciellt för en nybörjare. Ransomware delen var svÄr, men ÀndÄ görbar med hjÀlp av google och lite AI. Helt klart nöjd med min 11:e plats.
Avslutningsvis…
https://www.youtube.com/watch?v=bLlj_GeKniA
Ses pĂ„ lan, eller kanske pĂ„ UNDUTMANINGEN 2025 đ€