SZKOŁA GILDII: Bugologia w Exofusion - część...
CZĘŚĆ TEORETYCZNA: PRZYCZYNY TEGO RODZAJU BUGÓW 1. Dowolne wartości Pierwszą przyczyną tego, że da się oszukiwać w grach opartych na engine Exofusion jest to, że w pola liczbowe można wpisać dowolną wartość (liczbę, liczbę ujemną, liczbę ułamkową, tekst). Dawniej można więc było korzystać z tego w ten sposób, że wpłacałeś np. do banku -1000000 i dostawałeś w ten sposób pieniądze, które mogłeś wykorzystać sobie, jak chciałeś - np. na założenie klanu, gdzie znowu mogłeś wpłacać ujemne dotacje, a następnie skasować klan wychodząc z niego i tym samym zatrzeć ślady po ujemnych kwotach. Jednak bug ten został dawno usunięty. Aczkolwiek w niektórych grach nadal da się w jakichś miejscach wpisywać ujemne kwoty, więc warto o tym napomknąć. 2. Konwersje PHP sam ustala typ zmiennej w zależności od kontekstu. W razie potrzeby konwertuje wartość zmiennej do potrzebnego typu. Tak więc zmienna, która ma wartość liczbową (np. 123) może być raz traktowana jako liczba, a raz jako tekst - w obu przypadkach jednak będzie wyglądać tak samo. Natomiast zmienna, która jest ciągiem znaków (np. 10 adminów skacze z mostu w 2 parach kaleson) raz może być traktowana jako tekst (10 adminów skacze z mostu w 2 parach kaleson), a raz jako liczba, ale w tym drugim przypadku konwersja automatycznie obetnie wszystko poza początkiem ciągu stanowiącym poprawną liczbę, jeśli natomiast ciąg nie zaczyna się od liczby, to przyjmuje wartość 0 (w podanym przykładzie zmienna będzie traktowana, jakby miała wartość 10). Konwersje te zachodzą w locie jedynie na potrzeby danego działania, rzeczywista wartość zmiennej nie ulega zmianie. Na potrzeby zrozumienia działania bugów będą nas interesować dwa efekty związane z konwersjami. a. konwersja ciąg znaków => liczba (efekt głowy i ogona) W tym przypadku część wartości zmiennej zostanie przysłonięta w przypadkach, w których zajdzie konwersja (w przykładzie jest to fragment: adminów skacze z mostu w 2 parach kaleson) i nie będzie brana pod uwagę przy obliczeniach, a w miejscach, w których konwersja nie zajdzie, będzie brana pod uwagę w całości. Dla ułatwienia będę nazywać część, która pozostaje po konwersji głową (w przykładzie 10), a część która po konwersji jest ukryta ogonem (w przykładzie adminów skacze z mostu w 2 parach kaleson). Głowa jest brana pod uwagę w obliczeniach w każdym miejscu wystąpienia zmiennej, a ogon tylko w tych, gdzie nie przysłoni go konwersja, więc można w nim coś przemycać. Tak więc podsumujmy: cały ciąg = głowa + ogon b. konwersja liczba => ciąg znaków (efekt porównywania leksykograficznego) Gdy jedna z porównywanych wartości nie jest normalną liczbą (a więc zawiera jakiś choćby jeden znak, np. 100+100), a druga jest, to obie są konwertowane do ciągów znaków. Ma to o tyle istotne znaczenie, że ciągi znaków porównywane są inaczej niż liczby. Mamy tu do czynienia z porównywaniem leksykograficznym. Oznacza to tyle, że za mniejszy uznawany jest ten wyraz, który byłby pierwszy, gdyby umieścić je w słowniku. Dokładniej działa to tak, że najpierw porównywane są pierwsze znaki ciągów i jeśli, któryś znak jest "mniejszy", to wyraz który rozpoczyna będzie automatycznie uznany za "mniejszy", bez względu na resztę ciągu (np. "1000"<"20"). Jeśli pierwsze znaki są równe, to brany jest pod uwagę następny, i tak dalej (np. "525999"<"5267"). Jeśli w którymś wyrazie zabraknie już znaków, to za "mniejszy" uznawany jest wtedy ten, który ma mniej znaków (np. "10"<"100"). Tak więc mam nadziję, że już widać, co nam to daje. Jeśli mamy gdzieś warunek $zmienna<30, a jako wartość ten zmiennej podamy w okienku zamiast normalnej liczby 2000+2000, to zajdzie wtedy konwersja na ciągi i wartości będą porównywane leksykograficznie, a więc wtedy "2000+2000"<"30" i warunek mamy spełniony. 3. Korzystanie z bugów W wielu miejscach skryptu, gdzie wpisywane przez gracza wartości wpływają na zmiany w parametrach postaci (czyli wpływają na wartości w bazie danych) akcja taka przebiega dwustopniowo: sekcja PHP: sprawdzana jest poprawność wprowadzonych danych i dokonywane ewentualne obliczenia pomocnicze Korzystanie z bugów polega na wpisywaniu do pól liczbowego ciągów znaków, który docelowo mają zostać wklejone do zapytania SQL zamiast zwykłych liczb. Przemycimy tam działania arytmetyczne, albo nawet pewne klauzule SQL, które zmienią działanie tej dyrektywy. Warto tu zaznaczyć, że korzystanie z tego bugu możliwe jesty tylko przez pola przeznaczone do wpisywania liczb, gdyż pola, które służą do wpisywania zmiennych tekstowych są wklejane do zapytania SQL po ujęciu w apostrofy, więc nie mogą wywołać tam żadnego nieporządanego działania (o tym, jak natomiast można oszukiwać korzystając z pól tekstowych dowiemy się w wykładzie Bugologia - część III). Jednak sprawa jest o tyle trudniejsza, że nasz ciąg znaków zanim trafi do sekcja SQL musi przejść szczęśliwie spełnić warunki z sekcji PHP, które sprawdzają jego poprawność. Jednak przejście tych (błędnych przecież) wartości jest możliwe dzięki cudownemu działaniu konwersji. Jak konwersja prowadzi do przepuszczenia błędnych danych? Zobaczmy wszystko w praktyce.
CZĘŚĆ PRAKTYCZNA: TRICKI Z WYKORZYSTANIEM BUGÓW Z POLAMI LICZBOWYMI Będę tu korzystał z kodu w miarę zbliżonego do pierwotnego Exofusion - w poszczególnych przypadkach kod może się różnić, więc korzystanie z bugów też może się różnić, albo też może być niemożliwe, jeśli ktoś te luki zabezpieczył. 1. Wykorzystanie efektu głowy i ogona (konwersji ciąg znaków => liczba) Rozpatrzymy to na przykładzie sklepu z platyną: sekcja PHP: sekcja SQL:
2. Wykorzystanie efektu porównywania leksykograficznego (konwersji liczba => ciąg znaków) Dalsze tricki rozpatrzymy sobie na przykładzie banku, ale w innych miejscach takich jak dotacje w klanie, czy skup platyny, działają również (konwersja liczba => ciąg znaków przy sprawdzaniu warunku zaszła już pierwszym tricku i otworzyła tam drogę niepoprawnej kwocie, ale teraz przyjrzymy się temu bliżej). sekcja PHP: Żeby ten warunek został spełniony w kwocie, którą chcemy wpłacić do banku musi znajdować się chociaż jeden znak nie będący cyfrą (nie licząc minusa na początku i kropki dzisiętnej na końcu), gdyż tylko wtedy zajdzie konwersja liczby naszego złota do ciągu znaków i zajdzie porównanie leksykograficzne, które oszuka postawiony kwocie warunek. Żeby natomiast samo porównanie leksykograficzne dało pożądany skutek, to pierwsza cyfra wpisywanego magicznego ciągu musi być mniejsza od pierwszej cyfry złota jakie mamy w kieszeni. Oczywiście przed wszystkim wpisywany ciąg znaków musi dobrze się wpasować z zapytanie SQL (jeśli interpreter SQL nie rozpozna tego ciągu jako poprawnego, to nie zostanie wykonana żadna akcja, pomimo że nie pojawi się komunikat o błędzie w kwocie): sekcja SQL: Zobaczmy najpierw, jak dodać sobie kasy. Niektórzy robią to wpisując 1*-1000000, jednak jest to gorsza metoda i łatwa do wykrycia przez ujemne kwoty, które pojawiają się na koncie gracza, a które admin może łatwo zauważyć. Zobaczmy więc od razu, jak zrobić to w czystszy sposób bez efektów ubocznych. Problem w tym, że ta sama kwota, która będzie dodana do jednego pola w bazie - w drugiej dyrektywie zostanie odjęte od drugiego. Umieścimy więc tu kwotę 1*0 (łancuch musi się zaczynać od 1, żeby przeszedł warunki w sekcji PHP), a zaraz po niej +1000000. W efekcie tego zabiegu od kwoty pieniędzy zostanie odjęte 0 i dodane do banku 0, ale za to w obydwu tych miejscach zostanie dodatkowo dodana kwota 1000000. Czyli tym razem czysty zysk, bez efektów ubocznych.
Jeślibyśmy chcieli skasować obydwie wartości, to najłatwiej jest pomnożyć je przez 0. Jeśli jednak pomnożymy je normalnie, czyli damy ciąg 1*0 (1 musi być na początku z wiadomych względów), to ze względu na to, że zwykłe mnożenie wykonuje się przed dodawaniem, nie uzyskamy zamierzonego efektu. Musimy więc skorzystać z iloczynu bitowego (1&0), który ma niższy priorytet.
Teraz pokażemy sobie, jak ustawić sobie jakąś konkretną wartość. Niektórzy robią to metodą 2000&2000, ale jest to metoda zależna w niektórych przypadkach od kwoty, która już jest w danym polu i do tego kwota, na którą zmieniamy, powinna być jak najwięcej podzielna przez 2. Liczby nieparzystej na przykład w ten sposób nie ustaimy, chyba, że w danym polu jest 0 kasy. Proponuję tu więc skuteczniejszą technikę.
Nie można tego rozdzielić na dwa kroki, tzn. najpierw wyzerować, a potem ustawić. Można eksperymentować też z innymi dziłaniami z udziałem operatorów: + - * / % & ( ) 3. Wykorzystanie znacznika komentarza # Znaczek # w SQL jest znakiem początku komentarza (w PHP zresztą też). Wszystko co znajdzie się w danej linijce na prawo od tego znaczka, jest ignorowane przez SQL. Możemy więc w ten sposób jakby wyciąć kawałek zapytania. Żeby zrozumieć następne tricki musimy też wytłumaczyć sobie jeszcze jedną kwestię. Klauzula where w SQL wyznacza, które wiersze tabeli mają być zmieniane. Np. where id=2 oznacze, że dana dyrektywa dotyczy tylko użytkownika z id=2. Jeśli klauzula where w ogóle zostanie pominięta, to SQL przyjmuje domyślnie, że dyrektywa dotyczy WSZYSTKICH pozycji w danej tabeli. Spójrzmy teraz na konkretne przykłady.
Możemy też podmienić klazulę where na inną:
I na koniec jeszcze jeden przykład z innej beczki. Tym razem z panelu właściciela klanu, a dokładniej opcji wykopywania członków:
I koleny trick z polem służącym normalnie do wykopywania z klanu. Wiadomo, że zajrzenie do logów jest szybsze od zaglądania do poczty, więc możemy komuś tą drogą wysłać jakiś numer (np. gg). Tekstu wysłać się nie da. Przynajmniej nie na Lycosie z jego ustawieniami.
CZĘŚĆ NAJWAŻNIEJSZA: USUNIĘCIE BUGÓW 1 sposób: Konwersja na stałe Chyba łatwiejszy do zrozumienia sposób, aczkolwiek jego minusem jest to, że nie odrzuca błędnych wartości, a jedynie obcina cały niebezpieczny ogon i pozostawia jedynie głowę, czyli liczbę, która jest na samym początku. Należy w tym celu dodać jedną linijkę do kodu (zaznaczona na czerwono): $dep = (integer) $dep; Co robi ta instrukcja? Zamienia wartość zmiennej na wartość po konwersji na liczbę całkowitą. Wszystko, to co znajduje się począwszy od pierwszego znaku nie będącego cyfrą zostaje bezpowrotnie usunięte. Przechodzą przez to jedynie liczby ujemne, ale przed nimi większość gier jest już od dawna zabezpieczonych, w tym przykładzie również jest to zabezpieczone linijkę niżej. Jeśli istnieje część ułamkowa liczby, to zostanie ona również ucięta. Linijki takie należy dodać przed pierwszym odczytaniem KAŻDEJ zmiennej liczbowej otrzymanej od użytkownika. Które to są zmienne moża się zorientować szukając wszystkich pól do wpisywania liczb w grze i odczytując nazwy tych zmiennych z formularzy. 2 sposób (najlepszy): Sprawdzenie wpisanych znaków, czy są cyframi Wprowadzona liczba jest porównywana ze wzorcem liczby. Jeśli w podanych znakach znajduje się cokolwiek poza cyframi, to jest to odrzucane i pokazuje się odpowiedni komunikat. Należy wstawić odpowiedni warunek (tutaj zaznaczony na czerwono) do kodu w miejsce sprawdzania, czy dana liczba nie jest ujemna: if ($dep > $stat[credits] !ereg("^[1-9][0-9]*$", $dep)) { Co robi ta instrukcja? Porównuje zmienną ze wzorcem. W wypadku tego wzorca zgodne okażą się jedynie te ciągi, które zaczynają się od cyfry 1-9, a następnie mogą zawierajać dowolną ilość cyfr 0-9. Tak więc liczba 0, albo ciągi zaczynające się od 0 zostaną też odrzucone.Przemycenie czegokolwiek, co nie jest cyfrą (a więc także kropki i minusa) nie wchodzi tu więc w ogóle w grę. Warunki takie należy dodać przed pierwszym odczytaniem KAŻDEJ zmiennej liczbowej otrzymanej od użytkownika. Które to są zmienne moża się zorientować szukając wszystkich pól do wpisywania liczb w grze i odczytując nazwy tych zmiennych z formularzy. |
Dodaj komentarz