Podobno w bash istnieje luka (CVE-2014-6271): Bash, specjalnie spreparowany kod iniekcji kodu zmiennych środowiskowych
Próbuję dowiedzieć się, co się dzieje, ale nie jestem do końca pewien, czy to rozumiem. Jak można echo
wykonać w postaci pojedynczych cudzysłowów?
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
vulnerable
this is a test
EDYCJA 1 : Poprawiony system wygląda następująco:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test
EDYCJA 2 : Istnieje powiązana luka / łatka: CVE-2014-7169, która wykorzystuje nieco inny test:
$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"
niezaładowane dane wyjściowe :
vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test
częściowo (wczesna wersja) załatane wyjście :
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
bash: error importing function definition for `BASH_FUNC_x()'
test
załatane wyjście do CVE-2014-7169 włącznie:
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `BASH_FUNC_x'
test
EDYCJA 3 : historia kontynuuje:
bash
shellshock
vulnerability
jippie
źródło
źródło
vulnerable
pojawia się na wyjściu. Głównym problemem jest to, że bash analizuje i wykonuje kod również po definicji funkcji. Patrz/bin/id
część seclists.org/oss-sec/2014/q3/650 na innym przykładzie.Odpowiedzi:
bash przechowuje wyeksportowane definicje funkcji jako zmienne środowiskowe. Wyeksportowane funkcje wyglądają następująco:
Oznacza to, że zmienna środowiskowa
foo
ma dosłowną zawartość:Po uruchomieniu nowej instancji bash szuka tych specjalnie spreparowanych zmiennych środowiskowych i interpretuje je jako definicje funkcji. Możesz nawet napisać jeden sam i przekonać się, że nadal działa:
Niestety parsowanie definicji funkcji z ciągów znaków (zmiennych środowiskowych) może mieć szerszy efekt niż zamierzony. W niezałatanych wersjach interpretuje także dowolne polecenia, które pojawiają się po zakończeniu definicji funkcji. Wynika to z niewystarczających ograniczeń w określaniu akceptowalnych ciągów funkcjonalnych w środowisku. Na przykład:
Zauważ, że 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
foo
, że wygląda na to, że 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).Jest to uważane za niepewne, ponieważ zmienne zwykle nie są dozwolone ani oczekiwane, aby bezpośrednio powodować wywołanie dowolnego kodu w nich zawartego. Być może twój program ustawia zmienne środowiskowe na podstawie niezaufanych danych użytkownika. Byłoby bardzo nieoczekiwane, że tymi zmiennymi środowiskowymi można manipulować w taki sposób, aby użytkownik mógł uruchamiać dowolne polecenia bez wyraźnej woli, używając tej zmiennej środowiskowej z takiego powodu zadeklarowanego w kodzie.
Oto przykład realnego ataku. Prowadzisz serwer WWW, który uruchamia gdzieś wrażliwą powłokę w ramach swojego życia. Ten serwer WWW przekazuje zmienne środowiskowe do skryptu bash, na przykład jeśli używasz CGI, informacje o żądaniu HTTP są często dołączane jako zmienne środowiskowe z serwera WWW. Na przykład
HTTP_USER_AGENT
może być ustawiony na zawartość klienta użytkownika. Oznacza to, że jeśli sfałszujesz swojego agenta użytkownika jako coś w stylu „() {:; }; echo foo ', po uruchomieniu skryptu powłoki,echo foo
zostanie wykonane. Ponownieecho foo
może to być wszystko, złośliwe lub nie.źródło
export bar='() { echo "bar" ; }'; zsh -c bar
i wyświetla siębar
zamiastzsh:1: command not found: bar
? Czy na pewno nie mylisz powłoki, którą wywołujesz, z powłoką, której używasz do skonfigurowania testu?Może to pomóc w dalszym zademonstrowaniu, co się dzieje:
Jeśli używasz wrażliwej powłoki, to kiedy uruchomisz nową podpowłokę (tutaj, po prostu za pomocą instrukcji bash), zobaczysz, że dowolny kod (
echo "pwned"
) jest natychmiast wykonywany w ramach jego inicjacji. Najwyraźniej powłoka widzi, że zmienna środowiskowa (obojętna) zawiera definicję funkcji, i ocenia tę definicję w celu zdefiniowania tej funkcji w swoim środowisku (zauważ, że nie wykonuje funkcji: wypisałby „cześć”).Niestety, nie tylko ocenia definicję funkcji, ocenia cały tekst wartości zmiennej środowiskowej, w tym potencjalnie złośliwe instrukcje, które następują po definicji funkcji. Zauważ, że bez początkowej definicji funkcji zmienna środowiskowa nie byłaby oceniana, byłaby jedynie dodana do środowiska jako ciąg tekstowy. Jak zauważył Chris Down, jest to specyficzny mechanizm do implementacji importu eksportowanych funkcji powłoki.
Widzimy funkcję, która została zdefiniowana w nowej powłoce (i że została tam oznaczona jako wyeksportowana) i możemy ją wykonać. Ponadto, manekin nie został zaimportowany jako zmienna tekstowa:
Ani stworzenie tej funkcji, ani nic, co by zrobił, gdyby ją uruchomić, nie jest częścią exploita - jest tylko pojazdem, za pomocą którego exploit jest wykonywany. Chodzi o to, że jeśli osoba atakująca może dostarczyć złośliwy kod, poprzedzony minimalną i nieistotną definicją funkcji, w ciągu tekstowym, który zostaje umieszczony w eksportowanej zmiennej środowiskowej, zostanie on wykonany po uruchomieniu podpowłoki, co jest częstym zdarzeniem w wielu skryptach. Ponadto będzie wykonywany z uprawnieniami skryptu.
źródło
export
polecenie, podczas gdy inni mielienv
? myślałem,env
że jest używany do definiowania zmiennych środowiskowych, które będą wywoływane, gdy uruchomiona zostanie kolejna powłoka bash. jak to działa zexport
env
iexport
eksportu, dzięki czemu są one dostępne w podpowłoce. Problem tkwi w sposobie, w jaki te eksportowane definicje są importowane do środowiska podpowłoki, a w szczególności w mechanizmie importującym definicje funkcji.env
uruchamia polecenie z ustawionymi niektórymi opcjami i zmiennymi środowiskowymi. Zauważ, że w oryginalnych przykładach pytańenv
ustawiax
ciąg znaków i wywołujebash -c
polecenie do uruchomienia. Jeśli to zrobiszenv x='foo' vim
, Vim uruchomi się, a tam możesz wywołać jego zawierającą powłokę / środowisko za pomocą!echo $x
i wydrukujefoo
, ale jeśli następnie wyjdziesz iecho $x
nie zostanie zdefiniowany, ponieważ istniał tylko podczas działania vima za pomocąenv
polecenia.export
Polecenie zamiast ustawia wartości trwałych w bieżącym środowisku powłoki w tle, tak aby uruchomić później będą z nich korzystać.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
Domyślnie zmienne te nie są dziedziczone przez procesy potomne.
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: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:
A teraz możesz po prostu wywołać funkcję, wywołując ją tak, jakby była kolejną komendą powłoki:
Jeszcze raz, jeśli odrodzisz podpowłokę, nasza funkcja nie zostanie wyeksportowana:
Możemy wyeksportować funkcję za pomocą
export -f
:Oto trudna część: wyeksportowana funkcja, taka jak,
fn
jest przekształcana w zmienną środowiskową, tak jak nasz eksport zmiennej powłokit
był powyżej. Nie dzieje się tak, gdyfn
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:Teraz możemy użyć,
env
aby wyświetlić wszystkie zmienne powłoki oznaczone do eksportu, a zarówno normalna, jakfn
i funkcjafn
pokazują:Sub-powłoka będzie przyjmować obie definicje: jedną jako zmienną regularną i jedną jako funkcję:
Możesz zdefiniować,
fn
jak to zrobiliśmy powyżej, lub bezpośrednio jako zwykłe przypisanie zmiennej: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 regularnyfn
(jak pokazano powyżej) nietknięty.Teraz funkcja
fn
nie jest już eksportowana, ale zmienna zwykłafn
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:
W takim przypadku wrażliwe bash wykona te ostatnie polecenia.
Przykład:
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:
Oto podział: po pierwsze
:
in bash to tylko skróttrue
.true
i:
obie oceniają (zgadłeś) to prawda, w bash: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 ibash -c
uruchamia pojedyncze polecenie z jego wiersz poleceń: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ć: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:x
Jest więc eksportowany jako zwykła zmienna z prostą poprawną funkcją zecho vulnerable
dołączoną do końca. Jest to przekazywane do bash, a bash interpretujex
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: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:źródło
{ :;};
faktycznie mówi. Moim zdaniem byłby to miły dodatek do twojej odpowiedzi. Czy może wyjaśnić, w jaki sposób przechodzisz od przykładu do oryginalnej komendy w pytaniu?Jeśli możesz podać dowolne zmienne środowiskowe do programu, możesz sprawić, że zrobi on prawie wszystko, poprzez załadowanie wybranych bibliotek. W większości przypadków nie jest to uważane za usterkę w programie odbierającym te zmienne środowiskowe, ale raczej w mechanizmie, za pomocą którego osoba z zewnątrz może zasilać dowolne zmienne środowiskowe.
Jednak CVE-2014-6271 jest inny.
Nie ma nic złego w niezaufaniu danych w zmiennej środowiskowej. Trzeba tylko upewnić się, że nie zostanie wprowadzone do żadnej z tych zmiennych środowiskowych, które mogą modyfikować zachowanie programu. Mówiąc nieco bardziej abstrakcyjnie, dla konkretnego wywołania możesz utworzyć białą listę nazw zmiennych środowiskowych, które mogą być określone bezpośrednio przez osobę z zewnątrz.
Przykładem przedstawionym w kontekście CVE-2014-6271 są skrypty używane do analizowania plików dziennika. Mogą one mieć bardzo uzasadnioną potrzebę przekazywania niezaufanych danych w zmiennych środowiskowych. Oczywiście nazwa takiej zmiennej środowiskowej jest wybierana tak, aby nie miała żadnych negatywnych skutków.
Ale oto, co jest złego w tej szczególnej podatności na bash. Można go wykorzystać za pomocą dowolnej nazwy zmiennej. Jeśli utworzysz zmienną środowiskową o nazwie
GET_REQUEST_TO_BE_PROCESSED_BY_MY_SCRIPT
, nie spodziewałbyś się, że inny program oprócz własnego skryptu interpretuje zawartość tej zmiennej środowiskowej. Ale wykorzystując ten błąd bash, każda zmienna środowiskowa staje się wektorem ataku.Zauważ, że nie oznacza to, że nazwy zmiennych środowiskowych powinny być tajne. Znajomość nazw zmiennych środowiskowych nie ułatwia ataku.
Jeśli
program1
połączenia,program2
które z kolei wywołująprogram3
,program1
mogą przekazywać daneprogram3
przez zmienne środowiskowe. Każdy program ma określoną listę zmiennych środowiskowych, które ustawia oraz określoną listę, na którą działa. Jeśli wybierzesz nazwę nieuznawaną przezprogram2
, możesz przesyłać dane odprogram1
do,program3
nie martwiąc się, że będzie to miało negatywny wpływ naprogram2
.Osoba atakująca, która zna dokładne nazwy zmiennych eksportowanych przez
program1
i nazw zmiennych interpretowanych przez,program2
nie może wykorzystać tej wiedzy do zmodyfikowania zachowania „programu2”, jeśli zestaw nazw nie zachodzi na siebie.Ale to się zepsuło, gdyby
program2
byłbash
skrypt, ponieważ z powodu tego błędubash
interpretowałby każdą zmienną środowiskową jako kod.źródło
Wyjaśniono to w artykule, który połączyłeś ...
Co oznacza, że wywoływana przez bash funkcja wywołuje
-c "echo this is a test"
kod w pojedynczych cudzysłowach po jego wywołaniu.Oznacza, że opublikowany przykład kodu wykorzystuje fakt, że wywołany bash nie przestaje oceniać tego ciągu po wykonaniu przypisania. Przypisanie funkcji w tym przypadku.
Tak naprawdę, jak rozumiem, fragment kodu, który opublikowałeś, polega na tym, że umieszczając definicję funkcji przed kodem, który chcemy wykonać, można obejść niektóre mechanizmy bezpieczeństwa.
źródło