Jak jawnie i bezpiecznie wymusić użycie wbudowanego polecenia w bash

19

Jest to podobne pytanie , które zajmuje się „owijania” scenariusza, w którym chcesz zastąpić na przykład cdz poleceniem, która wywołuje polecenie wbudowane cd.

Jednak w świetle shellshocka i innych oraz wiedząc, że bash importuje funkcje ze środowiska, przeprowadziłem kilka testów i nie mogę znaleźć sposobu, aby bezpiecznie wywołać wbudowaną wersję cdz mojego skryptu.

Rozważ to

cd() { echo "muahaha"; }
export -f cd

Wszelkie skrypty wywoływane w tym środowisku za pomocą cdulegną awarii (weź pod uwagę skutki czegoś takiego cd dir && rm -rf .).

Istnieją polecenia sprawdzające typ polecenia (wygodnie wywoływanego type) oraz polecenia do wykonania wbudowanej wersji, a nie funkcji ( builtini command). Ale oto oto można je również zastąpić za pomocą funkcji

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Przyniesie następujące:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

Czy jest jakiś sposób, aby bezpiecznie zmusić bash do użycia wbudowanego polecenia, lub przynajmniej wykryć, że polecenie nie jest wbudowane, bez czyszczenia całego środowiska?

Zdaję sobie sprawę, że jeśli ktoś kontroluje twoje środowisko, prawdopodobnie i tak masz problemy, ale przynajmniej w przypadku aliasów masz opcję, aby nie wywoływać aliasu poprzez wstawienie \przed nim.

falstro
źródło
możesz wcześniej uruchomić skrypt za pomocą envpolecenia, w ten sposób:env -i <SCRIPT.sh>
Arkadiusz Drabczyk
Tylko jeśli envnie jest również zdefiniowane jako funkcja. To jest przerażające. Najpierw pomyślałem, że pomogą znaki specjalne - dzwonienie z pełną ścieżką, która obejmuje /, używanie .źródła i tak dalej. Ale można ich również używać do nazw funkcji! Państwo może na nowo zdefiniować dowolną funkcję, którą chcesz, ale trudno wrócić do wywoływania oryginalnego polecenia.
orion
1
To prawda, ale jeśli zamierzasz uruchomić dowolny skrypt na zaatakowanej maszynie, to i tak masz problemy. Możesz też napisać skrypt, #/bin/shjeśli nie jest to domyślna powłoka interaktywna.
Arkadiusz Drabczyk

Odpowiedzi:

16

Olivier D jest prawie poprawny, ale musisz ustawić POSIXLY_CORRECT=1przed uruchomieniem unset. POSIX ma pojęcie specjalnych wbudowanych i bash to obsługuje . unsetjest jednym z takich wbudowanych. Szukaj SPECIAL_BUILTINw builtins/*.cw źródle bash na liście, zawiera set, unset, export, evali source.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

Rogue unsetzostał usunięty ze środowiska, jeśli rozbroić command, type, builtinto powinieneś być w stanie kontynuować, ale unset POSIXLY_CORRECTjeśli opierając się na nie-POSIX zachowań lub zaawansowanych funkcji bash.

Nie dotyczy to jednak aliasów, więc musisz użyć, \unsetaby upewnić się, że działa w interaktywnej powłoce (lub zawsze, jeśli expand_aliasesjest to możliwe).

Myślę, że dla paranoika powinno to wszystko naprawić:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do, doneI [[są zarezerwowane słowa i nie wymagają ostrożności.) Uwaga używamy unset -f, aby mieć pewność, aby funkcje ustawione, chociaż zmienne i funkcje dzielić tę samą przestrzeń nazw jest możliwe zarówno istnieć jednocześnie (dzięki Etan Reisnera), w którym to przypadku dwukrotne rozbrojenie również załatwi sprawę. Państwo może zaznaczyć funkcję tylko do odczytu, bash nie przeszkadza Ci wyłączające tylko do odczytu funkcji włącznie bash-4.2, bash-4.3 nie przeszkadza wam, ale nadal honoruje specjalnych poleceń wbudowanych, gdy POSIXLY_CORRECTjest ustawiony.

Tylko do odczytu POSIXLY_CORRECTnie jest prawdziwym problemem, nie jest to logiczna wartość ani flaga oznacza, że ​​jego obecność włącza tryb POSIX, więc jeśli istnieje tylko do odczytu, możesz polegać na funkcjach POSIX, nawet jeśli wartość jest pusta lub 0. Musisz po prostu rozbrajaj problematyczne funkcje w inny sposób niż powyżej, być może z pewnymi wycięciami:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(i zignoruj ​​wszelkie błędy) lub zaangażuj się w inne skryptobatyki .


Inne notatki:

  • functionjest słowem zastrzeżonym, można je aliasować, ale nie można przesłonić funkcją. (Aliasing functionjest raczej kłopotliwy, ponieważ \function nie można go ominąć)
  • [[, ]]są słowami zastrzeżonymi, można je aliasować (które zostaną zignorowane), ale nie można ich przesłonić funkcją (chociaż funkcje można tak nazwać)
  • (( nie jest prawidłową nazwą funkcji ani aliasem
pan. spuratic
źródło
Ciekawe dzięki! Wydaje mi się dziwne, że bash domyślnie nie chroniłby unsetet al przed nadpisaniem.
falstro
To wymaga -fargumentu, unsetprawda? W przeciwnym razie, jeśli zdefiniowana jest zarówno zmienna, jak i funkcja o tej samej nazwie, co wbudowane, najpierw unsetwybierze zmienną do rozbrojenia. To również nie powiedzie się, jeśli ustawiono którąś z ukrytych funkcji, readonlyprawda? (Chociaż jest to wykrywalne i może zostać popełnione fatalny błąd). Również to pomija [wbudowane wydaje się.
Etan Reisner,
1
Nie sądzę, żeby \unset -f unsetmiało to sens, biorąc pod uwagę, że zostanie to wykonane w pętli odczytu. Jeśli unsetjest funkcją \unset, normalnie rozwiążesz ją zamiast wbudowanej, ale możesz używać jej \unsetbezpiecznie, dopóki działa tryb POSIX. Jawne wywołanie \unset -fna [, .i :może być dobrym pomysłem, choć, jak swoimi regex wyłącza je. Dodałbym również -fdo \unsetpętli i \unset POSIXLY_CORRECTpo pętli, zamiast wcześniej. \unalias -a(po \unset -f unalias) pozwala również bezpiecznie zrezygnować z ucieczki przy kolejnych poleceniach.
Adrian Günter
1
@ AdrianGünter Spróbuj: [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]spowoduje to, że skrypt nieinteraktywny zakończy działanie z błędem wskazanym, jeśli zmienna jest rozbrojona, lub kontynuuje, jeśli zostanie ustawiona (pusta lub dowolna wartość).
mr.spuratic
1
„musisz użyć \unset” ... Należy zauważyć, że cytowanie dowolnego znaku (-ów) działałoby by uniknąć rozszerzenia aliasu, a nie tylko pierwszego. un"se"tdziałałby równie dobrze, ale mniej czytelny. „Pierwsze słowo każdego prostego polecenia, jeśli nie jest cytowane, jest sprawdzane pod kątem aliasu”. - Ref. Bash
Robin A. Meade
6

Zdaję sobie sprawę, że jeśli ktoś kontroluje twoje środowisko, prawdopodobnie i tak jesteś zepsuty

Tak to. Jeśli uruchomisz skrypt w nieznanym środowisku, wszystko może się nie udać, zaczynając od LD_PRELOADspowodowania, że ​​proces powłoki wykona dowolny kod, zanim jeszcze przeczyta twój skrypt. Próba ochrony przed wrogim środowiskiem z wnętrza skryptu jest daremna.

Sudo od ponad dekady odkaża środowisko, usuwając wszystko, co wygląda jak definicja funkcji bash. Od czasu Shellshock inne środowiska, w których działa skrypt powłoki w niezaufanym środowisku, postępują podobnie.

Nie można bezpiecznie uruchomić skryptu w środowisku ustawionym przez niezaufany byt. Martwienie się o definicje funkcji nie jest produktywne. Odkaż swoje środowisko, a robiąc to zmienne, które bash interpretowałby jako definicje funkcji.

Gilles „SO- przestań być zły”
źródło
1
Całkowicie się z tym nie zgadzam. Bezpieczeństwo nie jest propozycją binarną skoncentrowaną wokół „odkażonych” lub „niezanalizowanych”. Chodzi o usunięcie możliwości wykorzystania. Niestety złożoność nowoczesnych systemów oznacza, że ​​istnieje wiele możliwości wykorzystania, które należy odpowiednio zablokować. Wiele możliwości wykorzystania koncentruje się wokół nieszkodliwych pozornie ataków upstream, takich jak zmiana zmiennej PATH, redefiniowanie wbudowanych funkcji itp. Zaoferuj jednej osobie lub programowi taką możliwość, a cały system będzie podatny na atak.
Dejay Clayton,
@DejayClayton W pełni zgadzam się z twoim komentarzem, z wyjątkiem pierwszego zdania, ponieważ nie widzę, w jaki sposób jest to sprzeczne z moją odpowiedzią. Usunięcie okazji do wykorzystania pomaga, ale nie usuwa tylko połowy, w przypadku gdy trywialna poprawka w ataku pozwala mu dalej działać.
Gilles 'SO - przestań być zły'
Chodzi mi o to, że nie można po prostu „zdezynfekować środowisko”, a następnie przestać się martwić o różne wektory ataku. Czasami opłaca się „chronić przed wrogim środowiskiem z wnętrza skryptu”. Nawet jeśli rozwiążesz problem z „definicją funkcji”, nadal musisz się martwić takimi rzeczami, jak posiadanie ŚCIEŻKI, w której ktoś mógłby umieścić niestandardowy skrypt o nazwie „cat” lub „ls” w katalogu, a następnie każdy skrypt wywołujący „cat” ”lub„ ls ”zamiast„ / bin / cat ”i„ / bin / ls ”efektywnie wykorzystują exploita.
Dejay Clayton,
@DejayClayton Oczywiste odkażanie środowiska nie pomaga w przypadku wektorów ataku niezwiązanych ze środowiskiem. (Na przykład skrypt wywołujący /bin/catchroot może wywoływać cokolwiek). Odkażanie środowiska daje poczucie rozsądku PATH(zakładając, że robisz to dobrze). Nadal nie rozumiem, o co ci chodzi.
Gilles 'SO - przestań być zły'
Biorąc pod uwagę, że PATH jest jedną z największych możliwości exploitów i że wiele wątpliwych praktyk PATH jest udokumentowanych jako zalecane porady nawet na blogach poświęconych bezpieczeństwu, a także biorąc pod uwagę, że czasami zainstalowane aplikacje zmieniają kolejność PATH, aby zapewnić, że takie aplikacje działają, nie widzę, jak to zrobić kodowanie obronne w skrypcie byłoby złym pomysłem. To, co uważasz za rozsądną ŚCIEŻKĘ, może być podatne na exploity, których nie spotkałeś.
Dejay Clayton
1

Możesz użyć unset -fdo usunięcia wbudowanych funkcji, polecenia i typu.

odc
źródło
2
przepraszam, unset() { true; }zajmuje się tym.
falstro
1
Cholera! Lista zdefiniowanych funkcji również zależy od wbudowanego. Poddaję się!
odc