Objaśnienie polecenia sprawdzania szoku

32

Oto polecenie, którego użyłem do sprawdzenia powłoki bash pod kątem błędu Shellshock:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Czy ktoś może szczegółowo wyjaśnić polecenie?

heemayl
źródło
4
Zobacz także: unix.stackexchange.com/q/157329/70524 - Odpowiedź Fixee może być pomocna.
muru

Odpowiedzi:

45

Ta odpowiedź jest pochodną oryginalnego artykułu Matthew Miller na Fedorze Magazine , na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 .

Pozwól mi wyjaśnić:

env x='() { :;}; echo OOPS' bash -c :

Spowoduje to wydrukowanie „OOPS” w systemie podatnym na zagrożenia, ale zamknie się cicho, jeśli poprawiono bash.

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Spowoduje to wydrukowanie „OOPS” w systemie podatnym na zagrożenia, ale zostanie wydrukowane, “this is a test”jeśli poprawiono bash.

Prawdopodobnie słyszałeś, że ma to coś wspólnego ze zmiennymi środowiskowymi. Ale dlaczego wykonywany jest kod w zmiennych środowiskowych? Cóż, tak nie powinno być - ale z powodu cechy, którą kusiłbym nazwać nieco sprytną dla własnego dobra, jest miejsce na wadę. Bash jest tym, co widzisz jako monit terminalowy, ale jest także językiem skryptowym i ma możliwość definiowania funkcji. Robisz to w ten sposób:

$ Ubuntu()  { echo "Ubuntu is awesome."; }

a następnie masz nowe polecenie. Pamiętaj, że echotutaj jeszcze nie działa; jest po prostu zapisane, co stanie się, gdy uruchomimy nasze nowe polecenie. To będzie ważne za chwilę!

$ Ubuntu
 Ubuntu is awesome.

Przydatny! Ale, powiedzmy, z jakiegoś powodu, musimy wykonać nową instancję bash jako podproces i chcemy w tym celu uruchomić moje nowe niesamowite polecenie. Instrukcja bash -c somecommandrobi dokładnie to: uruchamia podane polecenie w nowej powłoce:

$ bash -c Ubuntu
  bash: Ubuntu: command not found

Och Smutny. Dziecko nie odziedziczyło definicji funkcji. Ale ma on nieodłączny wpływ na środowisko - zbiór par klucz-wartość, które zostały wyeksportowane z powłoki. (To jest cała koncepcja orzechów; jeśli nie jesteś z tym zaznajomiony, zaufaj mi na razie.) I, jak się okazuje, bash może również eksportować funkcje. Więc:

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

Co jest w porządku i dobre - poza tym, że mechanizm, za pomocą którego można to osiągnąć, jest trochę podejrzany . Zasadniczo, ponieważ nie ma magii Linux / Unix do wykonywania funkcji w zmiennych środowiskowych, funkcja eksportu w rzeczywistości tworzy po prostu zwykłą zmienną środowiskową zawierającą definicję funkcji. Następnie, gdy druga powłoka odczytuje środowisko „przychodzące” i napotyka zmienną z zawartością, która wygląda jak funkcja, ocenia ją.

Teoretycznie jest to całkowicie bezpieczne , ponieważ pamiętaj, że zdefiniowanie funkcji tak naprawdę jej nie wykonuje . Z wyjątkiem - i właśnie dlatego tu jesteśmy - w kodzie wystąpił błąd, w którym ocena nie kończyła się po osiągnięciu końca definicji funkcji. Po prostu idzie.

To się nigdy nie zdarzy, gdy funkcja przechowywana w zmiennej środowiskowej zostanie wykonana zgodnie z prawem, przy pomocy export -f. Ale po co być legalnym? Atakujący może po prostu wymyślić dowolną starą zmienną środowiskową, a jeśli będzie ona wyglądać jak funkcja, nowe powłoki bash to uznają!

Tak więc w naszym pierwszym przykładzie:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

envKomenda uruchamia polecenie z danej zmiennej zestawu. W tym przypadku ustawiamy xcoś, co wygląda jak funkcja. Ta funkcja jest tylko pojedynczą :, która w rzeczywistości jest prostym poleceniem, które definiuje się jako brak działania. Ale potem, po tym, semi-colonco sygnalizuje koniec definicji funkcji, pojawia się echopolecenie. Nie powinno tak być, ale nic nas nie powstrzyma.

Następnie polecenie, które należy uruchomić w tym nowym środowisku, to nowa powłoka bash, ponownie z poleceniem „ echo this is a test” lub „nic nie rób :”, po czym zakończy działanie całkowicie nieszkodliwie.

Ale - ups! Kiedy ta nowa powłoka uruchamia się i odczytuje środowisko, dostaje się do xzmiennej, a ponieważ wygląda jak funkcja, ocenia ją. Definicja funkcji jest ładowana nieszkodliwie - a następnie uruchamiana jest również nasza złośliwa funkcja. Tak więc, jeśli uruchomisz powyższe w systemie podatnym na ataki, zostaniesz z “OOPS”powrotem wydrukowany. Lub atakujący może zrobić o wiele gorzej niż tylko drukować.

αғsнιη
źródło
1
Muchas gracias za doskonałe wyjaśnienie, dlaczego to działa.
Doug R.
2
Pamiętaj, że envnie jest to konieczne. Można uzyskać ten sam rezultat (pass / fail w zależności od tego, czy Bash został zaktualizowany) za pomocą polecenia bez niego: x='() { :;}; echo OOPS' bash -c "echo this is a test". Wynika to z faktu, że poprzedzenie polecenia przypisaniem zmiennej przekazuje tę zmienną i jej wartość do środowiska polecenia ( bash -c "..."w tym przypadku).
Wstrzymano do odwołania.
1
... ale może być konieczne w niektórych nowszych łatach. Rzeczy się zmieniają.
Wstrzymano do odwołania.
4
@DennisWilliamson To, czy envjest to konieczne, zależy od powłoki, z której uruchamiany jest test, a nie od powłoki testowanej. (Mogą być takie same. Nawet wtedy testujemy, w jaki sposób bash przetwarza własne środowisko.) Powłoki w stylu Bourne'a akceptują NAME=value commandskładnię; Powłoki w stylu C (np. csh, tcsh) Nie. Test jest więc nieco bardziej przenośny env(kosztem czasami powodując zamieszanie na temat jego działania).
Eliah Kagan
2

W wersji niepakowanejbash przechowuje wyeksportowane definicje funkcji jako zmienne środowiskowe.

Zapisz funkcję xjako,

$ x() { bar; }
$ export -f x

I sprawdź jego definicję jako:

$ env | grep -A1 x
x=() {  bar
}

Można to wykorzystać, definiując własne zmienne środowiskowe i interpretując je jako definicje funkcji. Na przykład env x='() { :;}'byłoby traktowane jako

x() { :;
}

Do czego służy polecenie sprawdzania shellshocka,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Z man env,

  1. env - uruchomić program w zmodyfikowanym środowisku.

  2. :nie rób nic poza wyjściem ze statusem wyjścia 0. zobacz więcej

  3. Po uruchomieniu nowej instancji niezakończonego basha jako bash -c "echo this is a test", spreparowana zmienna środowiskowa jest traktowana jako funkcja i ładowana. Odpowiednio otrzymuje się wynik

    wrażliwy
    to jest test

Uwaga: Echo poza definicją funkcji zostało nieoczekiwanie wykonane podczas uruchamiania bash. Definicja funkcji jest tylko krokiem do przeprowadzenia oceny i wykorzystania, sama definicja funkcji i zmienna środowiskowa są dowolne. Powłoka patrzy na zmienne środowiskowe, widzi x, który wygląda tak, jakby spełniał ograniczenia, które wie o tym, jak wygląda definicja funkcji, i ocenia linię, mimowolnie również wykonując echo (może to być dowolne polecenie, złośliwe lub nie) . Zobacz także w tym

souravc
źródło
Nadal zauważyłem, że dowolna zdefiniowana funkcja bash, jeśli eksportowana jest oceniana w powłoce podrzędnej w poprawionej wersji bash. Zobacz: chayan @ chayan: ~ / testr $ test () {echo "cokolwiek"; }; test eksportu -f; bash -c test Ouput: cokolwiek Więc twoja odpowiedź nie jest właściwie skierowana. Wydaje mi się, że wyjaśnienie kasiyA błędu jako rozszerzenia zmiennej poza jej definicję jest prawidłowe.
heemayl
@ heemayl to zachowanie jest naturalne. Ale jeśli spróbujesz env test='() { echo "anything"; }' bash -c "echo otherthing", zobaczysz na wyjściu otherthing. Zostało to poprawione w łatce. nie krępuj się, jeśli nadal nie jestem pewien.
souravc
Proszę, wyjaśnij mi raz jeszcze. W twoim ostatnim komentarzu po prostu definiujemy funkcję, a następnie każemy bashowi wykonać echo. W tym przykładzie nie wywołaliśmy tej funkcji w bash. Czy nie miałoby to tego samego wyniku zarówno w łatce, jak i bez łatki? Mam pojęcie, że błąd był w zasadzie, ponieważ bash wykonywał polecenia umieszczone po definicji funkcji, podczas gdy funkcja nigdy nigdzie nie była wywoływana, na przykład, jeśli wykonamy ten test env = '() {echo "cokolwiek"; }; echo „foo” „bash -c” echo inny ”. Proszę o wyjaśnienie mnie w tym kontekście.
heemayl
@ heemayl Zredagowałem swoją odpowiedź, mam nadzieję, że teraz jest jasne. Masz rację w przykładzie w moim ostatnim komentarzu, że nie wywołaliśmy tej funkcji. Różnica polega jednak na tym, że w funkcji unpatched bashmożna wywoływać funkcję taką, jaka jest zdefiniowana, ale w poprawce bashnie ma samej definicji.
souravc
@heemayl: Nie, to nieprawda. Łata łatana nadal przekaże definicję funkcji do środowiska dziecka. Różnica sprawia, że ​​łatka polega na tym, że kod zgodny z definicją funkcji ( echo vulnerable) nie jest wykonywany. Zauważ, że w najnowszych łatach przekazana funkcja musi mieć określony prefiks ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"). %%Zamiast pierwszych można użyć niektórych nowszych łatek ().
Wstrzymano do odwołania.