Chciałbym zaprezentować tutaj pewną ciekawostkę z zakresu bezpieczeństwa stron internetowych. W jaki sposób wykryć typ serwera zdalnego? Do wyboru mamy kilka serwerów, typu Apache, Microsoft IIS, Lighttpd czy nginx? W poście tym zaprezentuję sposób na identyfikację tylko dwóch pierwszych.
W jaki sposób wykryć serwer po stronie klienta?
Nie potrzeba żadnych specjalistycznych narzędzi i metod, by móc wykryć rodzaj serwera HTTP używanego przez inne strony internetowe. Sposób na wykrywanie serwera nie wymagający żadnych zewnętrznych programów zostanie przedstawiony w tym poście.
Stwórzmy na szybko obiekt służący do późniejszego wyciągania informacji o zdalnych serwerach HTTP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | /** * * @author Kamil Brenk * @support kontakt@kamilbrenk.pl * * Detect Server Name * Class to detect server name: Apache or IIS in JavaScript * * HOWTO: * detectServer.loadImages('http://www.blog.kamilbrenk.pl/'); * window.setTimeout(function() { * ds = detectServer.init(); * ds.page_url // http://blog.kamilbrenk.pl * ds.server_name // Apache * }, 500); * **/ var detectServer = { /** * * Initialization and manage all operations * * @return object returns: page url and server name * **/ init: function() { // set default value for server name this.server_name = 'niezidentyfikowany'; // check validate url if ( !this.checkUrl() ) { alert('Proszę podać prawidowy adres!'); return { page_url: '-', server_name: '-' }; } // delete last slash in url this.deleteSlashInURL(); // check server type this.checkServerType(); // return results return { page_url: this.detect_url, server_name: this.server_name }; }, /** * * Check server type * loops through the properties of an object (servers type) * **/ checkServerType: function() { for (var img in this.images) { // check image size if ( this.images[img].width === this.servInfo[img].image.width && this.images[img].height === this.servInfo[img].image.height ) { this.server_name = this.servInfo[img].name; } } }, /** * * Load all images * * @param string detect_url full url to server (only domain) * **/ loadImages: function(detect_url) { // set url this.detect_url = detect_url; // object with created images this.images = {}; for (var i in this.servInfo) { // create new image object var new_req = new Image(); new_req.src = this.detect_url + this.servInfo[i].image.src; // bind new object image this.images[i] = new_req; } }, /** * * Check valid Url * * @return string return result of validation * **/ checkUrl: function() { // valid url regex // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ var _validUrl = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; // return result return _validUrl.test(this.detect_url); }, /** * * Delete last slash in URL * if exist * **/ deleteSlashInURL: function() { // check if exist if (this.detect_url.charAt(this.detect_url.length-1) !== '/') return false; // delete this.detect_url = this.detect_url.substring(0, this.detect_url.length-1); }, /** * * Servers Info * for: Apache, IIS * **/ servInfo: { apache: { name: 'Apache', image: { src: '/icons/tar.gif', width: 20, height: 22 } }, IIS: { name: 'Microsoft IIS', image: { src: '/pageerror.gif', width: 36, height: 48 } } } } |
Obiekt ten zawiera kilka metod:
- init – inicjalizacja i kontrola nad pobieraniem nazw serwerów HTTP; wewnętrzne zarządzanie kolejnymi metodami,
- checkServerType – właściwa metoda sprawdzająca typ serwera HTTP,
- loadImages – dynamiczne wczytanie obrazków wymagane do sprawdzenia typu serwera,
- checkUrl – wyrażenie regularne przeprowadzające test adresu Url strony, której typ serwera ma zostać sprawdzony,
- deleteSlashInURL – prosta metoda usuwająca ostatni slash (/) z adresu Url, jeśli takowy istnieje,
- servInfo – obiekt przechowujący dane specyficzne dla różnych serwerów.
Teraz wystarczy odpalić powyższy obiekt i pobrać dane dotyczące serwera HTTP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | window.onload = function() { // load all images detectServer.loadImages('http://www.kamilbrenk.pl/'); // ..when images are loaded window.setTimeout(function() { // detect server type var detect_server = detectServer.init(); // show page url document.getElementById('pageUrl').innerHTML = detect_server.page_url; // show result detect document.getElementById('servName').innerHTML = detect_server.server_name; }, 500); } |
Zastosowałem tutaj window.setTimeout, aby skrypt zdążył wysłać request do obrazków, a dopiero po chwili wykonał sprawdzenie przebiegu wczytywania. Można by to napisać dużo zgrabniej, jednak na powyższe potrzeby raczej nie jest to konieczne.
Jak to działa?
Gdy już poznaliśmy metody wykorzystywane przez obiekt, mogę wyjaśnić sposób, w jaki jest określany typ serwera HTTP.
Otóż specyficzną cechą dla serwera Apache jest posiadanie obrazu pod adresem /icons/tar.gif, który powinien mieć wymiary 20x22px. Cechą szczególną serwera Microsoft IIS jest grafika pod adresem /pageerror.gif o wymiarach 36x48px. I już mamy klucz do ciekawego określania typu serwera.
Wyczytałem także, iż router bezprzewodowy Linksys WRK54-G ma obraz /UI_Linksys.gif o wymiarach 165x57px.
No więc powyższy obiekt tworzy dynamicznie obrazki próbujące w celu sprawdzenia, czy wymiary i ścieżki się zgadzają. Jeśli się zgadzają to bardzo prawdopodobne jest, iż mamy do czynienia z określonym serwerem HTTP. Oczywiście można zabronić dostępu do wybranych plików i folderów na serwerze, lecz większość o tym zapomina (lub po prostu nie wie).
Tutaj rodzi się pytanie, czy inne serwery posiadają również takie luki bezpieczeństwa wskazujące na typ używanego serwera HTTP? Jeśli ktoś wie to byłbym wdzięczny za info :-)
Gdzie czyha niebezpieczeństwo?
Otóż głównym niebezpieczeństwem w tym przypadku jest fakt, iż znając używane oprogramowanie przez serwer zewnętrzny możemy wyszukać w Internecie popularnych luk bezpieczeństwa i wykrytych już błędów, a następnie przeprowadzić atak na taki serwis.
W końcu nie każdy aktualizuje swoje serwerowe oprogramowanie na bieżąco, więc jest duże prawdopodobieństwo, że wykryta już luka bezpieczeństwa jest naprawiona w najnowszej wersji serwera, jednak aktualnie wykorzystywana przez providera jest wciąż podatna na dany atak. Określenie typu serwera HTTP i sprawdzenie popularnych luk bezpieczeństwa jest dobrym punktem rozpoczęcia zabawy hakera.
Inne sposoby wykrycia serwera HTTP
Owszem, wykryć typ serwera HTTP można na kilka sposobów – poprzez skrypty działające po stronie serwera (PHP, .NET, Python), poprzez programy zewnętrzne (telnet), jak i przy pomocy „sztuczek” po stronie klienta (o tym już było).
Po stronie serwera (PHP) można to zrobić następująco:
1 2 3 4 5 6 | $get = get_headers('http://testy-zawodowe.pl/', true); echo $get['Server']; /** * Result: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g **/ |
Metoda ta jest dużo prostsza, jednak nie wszystkie serwisy odsyłają typ serwera w nagłówku.
Słowem zakończenia
Jak widać, haker ma wiele możliwości określenia rodzaju serwera HTTP. Przed wszystkimi tymi metodami istnieją sposoby obrony, także miejmy nadzieję, że notka ta okaże się pomocna w wykrywaniu potencjalnych metod pobierania typu serwera HTTP, a następnie ich blokowaniu, bowiem nie powinniśmy dopuszczać, by klient posiadał takie informacje.
Wykrywanie typu serwera HTTP – pokaż przykład
Mała uwaga:
Post ten ma charakter edukacyjny, a celem jest uświadamiane twórców stron o zagrażających im niebezpieczeństwach.
Typ serwera można wykryć w łatwiejszy sposób, poprzez analizę zwracanych nagłówków http. Większość serwerów przesyła nagłówek o nazwie „server” w którym znajduje się…. nazwa serwera web :) np. „Server: Microsoft-IIS/7.5”. Można to łatwo sprawdzić narzędziem typu Fiddler.
pozdrawiam
karpio
@karpio: podałem rozwiązanie od strony serwera, czytaj wyżej (nagłówek Inne sposoby wykrycia serwera HTTP)
By the way, sposobów jest wiele, ja jednak tutaj zaprezentowałem dość nietypowe i możliwe do przeprowadzenia od strony klienta – ot zwykła ciekawostka :-)
mea culpa. nie doczytałem ;)