Napisałem to jako powtórkę w stylu samouczka doskonałej odpowiedzi Chrisa Down powyżej.
W bash możesz mieć takie zmienne powłoki
$ t="hi there"
$ echo $t
hi there
$
Domyślnie zmienne te nie są dziedziczone przez procesy potomne.
$ bash
$ echo $t
$ exit
Ale jeśli zaznaczysz je do eksportu, bash ustawi flagę, co oznacza, że przejdą do środowiska podprocesów (chociaż envpparametr nie jest zbyt często widoczny, mainw twoim programie C ma trzy parametry: main(int argc, char *argv[], char *envp[])gdzie ostatnia tablica wskaźników jest tablicą zmiennych powłoki z ich definicjami).
Eksportujmy więc tw następujący sposób:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Podczas gdy powyższa nie tzostała zdefiniowana w podpowłoce, teraz pojawia się po jej wyeksportowaniu (użyj, export -n tjeśli chcesz przestać eksportować).
Ale funkcje w bash są innym zwierzęciem. Deklarujesz je w ten sposób:
$ fn() { echo "test"; }
A teraz możesz po prostu wywołać funkcję, wywołując ją tak, jakby była kolejną komendą powłoki:
$ fn
test
$
Jeszcze raz, jeśli odrodzisz podpowłokę, nasza funkcja nie zostanie wyeksportowana:
$ bash
$ fn
fn: command not found
$ exit
Możemy wyeksportować funkcję za pomocą export -f:
$ export -f fn
$ bash
$ fn
test
$ exit
Oto trudna część: wyeksportowana funkcja, taka jak, fnjest przekształcana w zmienną środowiskową, tak jak nasz eksport zmiennej powłoki tbył powyżej. Nie dzieje się tak, gdy fnbyła zmienną lokalną, ale po eksporcie możemy zobaczyć ją jako zmienną powłoki. Jednak możesz również mieć zwykłą (tj. Niefunkcjonalną) zmienną powłoki o tej samej nazwie. bash rozróżnia na podstawie zawartości zmiennej:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$
Teraz możemy użyć, envaby wyświetlić wszystkie zmienne powłoki oznaczone do eksportu, a zarówno normalna, jak fni funkcja fnpokazują:
$ env
.
.
.
fn=regular
fn=() { echo "test"
}
$
Sub-powłoka będzie przyjmować obie definicje: jedną jako zmienną regularną i jedną jako funkcję:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Możesz zdefiniować, fnjak to zrobiliśmy powyżej, lub bezpośrednio jako zwykłe przypisanie zmiennej:
$ fn='() { echo "direct" ; }'
Uwaga: jest to niezwykła rzecz do zrobienia! Normalnie zdefiniowalibyśmy tę funkcję, fntak jak to zrobiliśmy powyżej, za pomocą fn() {...}składni. Ale ponieważ bash eksportuje go przez środowisko, możemy „skrócić” bezpośrednio do powyższej zwykłej definicji. Zauważ, że (być może wbrew twojej intuicji) nie skutkuje to nową funkcją fndostępną w bieżącej powłoce. Ale jeśli odrodzisz powłokę ** sub **, to zrobi to.
Anulujmy eksport funkcji fni pozostawmy nowy regularny fn(jak pokazano powyżej) nietknięty.
$ export -nf fn
Teraz funkcja fnnie jest już eksportowana, ale zmienna zwykła fnjest i zawiera się () { echo "direct" ; }w niej.
Teraz, gdy podpowłoka widzi zwykłą zmienną, która zaczyna się od ()niej, interpretuje resztę jako definicję funkcji. Ale to tylko wtedy, gdy zaczyna się nowa powłoka. Jak widzieliśmy powyżej, samo zdefiniowanie zwykłej zmiennej powłoki zaczynającej się od ()nie powoduje, że zachowuje się ona jak funkcja. Musisz uruchomić podpowłokę.
A teraz błąd „shellshock”:
Jak właśnie widzieliśmy, gdy nowa powłoka przyjmuje definicję zmiennej regularnej, zaczynając od ()niej, interpretuje ją jako funkcję. Jeśli jednak po nawiasie zamykającym jest więcej danych, które definiują funkcję, wykonuje to wszystko, co tam jest.
Oto jeszcze raz wymagania:
- Pojawia się nowa bitwa
- Przyjmowana jest zmienna środowiskowa
- Ta zmienna środowiskowa zaczyna się od „()”, a następnie zawiera treść funkcji w nawiasach klamrowych, a następnie zawiera polecenia
W takim przypadku wrażliwe bash wykona te ostatnie polecenia.
Przykład:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
Regularnie eksportowana zmienna exzostała przekazana do podpowłoki, która została zinterpretowana jako funkcja, exale polecenia końcowe zostały wykonane ( this is bad) podczas odrodzenia podpowłoki.
Wyjaśnienie zręcznego testu jednowierszowego
Popularnym narzędziem do testowania podatności na Shellshock jest cytowany w pytaniu @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Oto podział: po pierwsze :in bash to tylko skrót true. truei :obie oceniają (zgadłeś) to prawda, w bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Po drugie, envpolecenie (również wbudowane w bash) drukuje zmienne środowiskowe (jak widzieliśmy powyżej), ale można go również użyć do uruchomienia pojedynczego polecenia z wyeksportowaną zmienną (lub zmiennymi) przekazanymi temu poleceniu i bash -curuchamia pojedyncze polecenie z jego wiersz poleceń:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Więc łącząc wszystkie te rzeczy razem, możemy uruchomić bash jako polecenie, dać mu coś do roboty (lubić bash -c echo this is a test) i wyeksportować zmienną, która zaczyna się od, ()aby podpowłoka zinterpretowała ją jako funkcję. Jeśli shellshock jest obecny, natychmiast wykona również wszelkie końcowe polecenia w podpowłoce. Ponieważ funkcja, którą przekazujemy, jest dla nas nieistotna (ale musi zostać przeanalizowana!), Używamy najkrótszej prawidłowej funkcji, jaką można sobie wyobrazić:
$ f() { :;}
$ f
$
Ta funkcja fpo prostu wykonuje :polecenie, które zwraca true i kończy działanie. Teraz dołącz do tego jakieś „złe” polecenie i wyeksportuj zmienną regularną do podpowłoki, a wygrasz. Oto znowu jedna linijka:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
xJest więc eksportowany jako zwykła zmienna z prostą poprawną funkcją z echo vulnerabledołączoną do końca. Jest to przekazywane do bash, a bash interpretuje xjako funkcję (o którą nie dbamy), a następnie może wykonuje, echo vulnerablejeśli shellshock jest obecny.
Możemy skrócić nieco linijkę, usuwając this is a testkomunikat:
$ env x='() { :;}; echo vulnerable' bash -c :
Nie przeszkadza to, this is a testale ponownie uruchamia ciche :polecenie. (Jeśli -c :zrezygnujesz, to siedzisz w podpowłoce i musisz wyjść ręcznie.) Być może najbardziej przyjazna dla użytkownika wersja byłaby:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"