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ż envp
parametr nie jest zbyt często widoczny, main
w 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 t
w następujący sposób:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Podczas gdy powyższa nie t
została zdefiniowana w podpowłoce, teraz pojawia się po jej wyeksportowaniu (użyj, export -n t
jeś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, fn
jest przekształcana w zmienną środowiskową, tak jak nasz eksport zmiennej powłoki t
był powyżej. Nie dzieje się tak, gdy fn
był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ć, env
aby wyświetlić wszystkie zmienne powłoki oznaczone do eksportu, a zarówno normalna, jak fn
i funkcja fn
pokazują:
$ 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ć, fn
jak 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ę, fn
tak 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ą fn
dostępną w bieżącej powłoce. Ale jeśli odrodzisz powłokę ** sub **, to zrobi to.
Anulujmy eksport funkcji fn
i pozostawmy nowy regularny fn
(jak pokazano powyżej) nietknięty.
$ export -nf fn
Teraz funkcja fn
nie jest już eksportowana, ale zmienna zwykła fn
jest 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 ex
została przekazana do podpowłoki, która została zinterpretowana jako funkcja, ex
ale 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
. true
i :
obie oceniają (zgadłeś) to prawda, w bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Po drugie, env
polecenie (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 -c
uruchamia 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 f
po 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"
x
Jest więc eksportowany jako zwykła zmienna z prostą poprawną funkcją z echo vulnerable
dołączoną do końca. Jest to przekazywane do bash, a bash interpretuje x
jako funkcję (o którą nie dbamy), a następnie może wykonuje, echo vulnerable
jeśli shellshock jest obecny.
Możemy skrócić nieco linijkę, usuwając this is a test
komunikat:
$ env x='() { :;}; echo vulnerable' bash -c :
Nie przeszkadza to, this is a test
ale 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"