Cel - znalezienie panelu administratora

Ten lab ma niechroniony panel administratora - taki, który w żaden sposób nie sprawdza, czy odwiedzająca go osoba ma uprawnienia administratora. Panel znajduje się pod "nieprzewidywalnym" adresem, jednak adres ten jest jednak ujawniony w pewnym miejscu w aplikacji. Aby uznać lab za zaliczony, musimy znaleźć ten adres, a następnie skorzystać z panelu, by usunąć użytkownika o imieniu Carlos.

Krok 1: analiza kodu źródłowego strony

W poprzednim labie z tej serii adres panelu administratora znaleźliśmy w pliku robots.txt. Tym razem twórcy postanowili ukryć go nieco lepiej - adres jest generowany losowo przy każdym uruchomieniu laba, więc nie zgadniemy go ze słownika typowych ścieżek (/admin, /panel, /administrator-panel itd.). Gobuster tu nic nie zdziała. Adres jest jednak "nieprzewidywalny" tylko z pozoru.

Zajrzyjmy do kodu źródłowego strony głównej. Znajdziemy w nim taki fragment:

<script>
  var isAdmin = false;
  if (isAdmin) {
    var topLinksTag = document.getElementsByClassName("top-links")[0];
    var adminPanelTag = document.createElement('a');
    adminPanelTag.setAttribute('href', '/admin-vtdbfx');
    adminPanelTag.innerText = 'Admin panel';
    topLinksTag.append(adminPanelTag);
    var pTag = document.createElement('p');
    pTag.innerText = '|';
    topLinksTag.appendChild(pTag);
  }
</script>

To bardzo ciekawy kawałek kodu - i całkiem zabawny adres! Zwróćmy jednak uwagę na to, co się poza tym dzieje w kodzie.

Skrypt sprawdza zmienną isAdmin. Gdyby ta zmienna miała wartość true, do menu na górze strony dodałby się link do panelu administratora prowadzący pod adres /admin-vtdbfx. U nas isAdmin jest ustawione na false, więc link nigdy się nie wyświetli.

Ale to, czy link się wyświetla w widocznym dla użytkownika miejscu na stronie, nie ma żadnego znaczenia. Logika sprawdzająca, czy strona powinna się wyświetlać, podaje jak na tacy znaleziony w źródle strony JavaScript. Gdyby isAdmin = true to ten kod dodałby odpowiednie elementy do drzewa DOM, wraz z linkiem do podstrony dla administratora. Wszystko, czego potrzebujemy do rozwiązania labu, jest po stronie klienta (client-side).

I tu pojawia się sedno tego labu: jeśli coś jest zahardcodowane w kodzie źródłowym albo generowane przy wejściu na stronę (i możliwe do odczytania w ramach aktywnej sesji), to nie jest to adres nieprzewidywalny. Nieprzewidywalność adresu miałaby może jakikolwiek sens dopiero wtedy, gdyby adres nie był nigdzie ujawniany osobie bez uprawnień, choć i wtedy dałoby się go znaleźć. A tutaj jest podany jak na tacy.

Krok 2: zaglądamy pod adres /admin-vtdbfx

Sprawdźmy więc ten adres - w moim przypadku było to https://0af200c00430be4c8030a82d00da00d4.web-security-academy.net/admin-vtdbfx (u Ciebie losowa część adresu będzie inna). Naszym oczom ukazuje się panel administratora z listą użytkowników:

Panel administratora w labie PortSwigger pod adresem /admin-vtdbfx z listą użytkowników i przyciskami Delete

No to pozamiatane. Klikamy Delete przy użytkowniku o imieniu Carlos - i tym samym rozwiązujemy lab.

Co jeszcze można znaleźć w kodzie źródłowym?

Dzisiejszy lab dotyczył znaleziska w kodzie źródłowym strony - teoretycznie nieprzewidywalnego adresu do panelu administratora. Ale kod źródłowy webaplikacji potrafi zdradzić znacznie więcej. Można w nim znaleźć między innymi:

  • tag Google Tag Managera - taką informację można wykorzystać np. w phishingu, wysyłając maila powiązanego z jedną z usług faktycznie wykorzystywanych na stronie, co uwiarygadnia atak;
  • informacje o wykorzystywanych wtyczkach - np. poprzez adres katalogu, z którego zaciągane są pliki, a który chociażby przypadku WordPressa można przypisać do konkretnej wtyczki;
  • informacje o wersjach oprogramowania i wtyczek - np. wordpressowa wtyczka Yoast SEO automatycznie dodaje komentarz z wykorzystywaną wersją, podobnie zachowuje się m.in. wtyczka LiteSpeed Cache:
<!-- This site is optimized with the Yoast SEO plugin v26.6 - https://yoast.com/wordpress/plugins/seo/ -->
<!-- Page cached by LiteSpeed Cache 7.7 on 2025-12-31 05:23:16 -->

WordPress często też dodaje informację o wersji na końcu adresów plików CSS:

<link rel='stylesheet' href='https://adres-strony.pl/wp-includes/css/dist/block-library/style.min.css?ver=6.9' type='text/css' media='all' />

W tym przypadku jest to adres styli do elementów blokowych - to część możliwości low-code/no-code WordPressa, czyli edytora Gutenberg. Parametr ?ver=6.9 zdradza natomiast wersję silnika.

Każda taka informacja z osobna wygląda niegroźnie, ale razem budują obraz strony, który atakujący może wykorzystać w kolejnej fazie ataku - choćby dobierając exploity pod konkretne, znane z podatności wersje oprogramowania.

Jak się chronić?

Czasem nie da się wiele zrobić. Można próbować ukrywać wszystko, jednak ukrywanie bywa czasochłonne. niekiedy wręcz niemożliwe - szczególnie, gdy kolejne aktualizacje oprogramowania nadpisują wprowadzone zmiany. Do oceny, czy gra jest warta świeczki, służy analiza ryzyka.

Niezależnie od niej zawsze warto:

  • dbać o aktualizacje oprogramowania - nawet jeśli ktoś pozna wersję wtyczki, to znacznie trudniej mu ją wykorzystać, gdy jest ona aktualna i pozbawiona znanych podatności;
  • nie instalować od razu nowego oprogramowania - często warto poczekać 24-48h, aż zostaną znalezione i poprawione ewentualne błędy. Choć warto też pamiętać, że w przypadku krytycznych luk czy systemów czas ma znaczenie i lepiej dodać łatkę z potencjalną inną, jeszcze nieznaną luką, niż być wystawionym na cel cyberprzestępców;
  • wiedzieć, jak chronić się przed phishingiem - sprawdzać adres URL i korzystać z managera haseł, który podpowie hasło tylko na właściwej domenie (jeśli nie podpowiada - to sygnał ostrzegawczy);
  • nie kodować "na sztywno" w kodzie źródłowym danych, których nie chcielibyśmy pokazać osobom z zewnątrz - nawet jeśli nie wyświetlają się one na stronie. Jak pokazał ten lab, "niewidoczne" dla przeciętnego użytkownika nie znaczy "niedostępne".