Z podręcznika findutils:
Na przykład konstrukcje takie jak te dwa polecenia
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
są bardzo niebezpieczne. Powodem tego jest rozszerzenie nazwy „{}” do nazwy pliku, która może zawierać średnik lub inne znaki specjalne dla powłoki. Jeśli na przykład ktoś utworzy plik,
/tmp/foo; rm -rf $HOME
dwie powyższe komendy mogą usunąć czyjś katalog domowy.Z tego powodu nie uruchamiaj żadnych poleceń, które przekażą niezaufane dane (takie jak nazwy plików) do poleceń, które interpretują argumenty jako polecenia do dalszej interpretacji (na przykład „sh”).
W przypadku powłoki istnieje sprytne obejście tego problemu:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
Takie podejście nie gwarantuje uniknięcia każdego problemu, ale jest znacznie bezpieczniejsze niż zastąpienie danych wybranego przez atakującego tekstem polecenia powłoki.
- Czy przyczyną problemu jest
find -exec sh -c "something {}" \;
to, że zamiennik{}
nie jest cytowany i dlatego nie jest traktowany jako pojedynczy ciąg? W rozwiązaniu
find -exec sh -c 'something "$@"' sh {} \;
,pierwszy
{}
jest zastępowany, ale skoro{}
nie jest cytowany, nie ma"$@"
również tego samego problemu co oryginalne polecenie? Na przykład,"$@"
zostanie rozszerzona"/tmp/foo;"
,"rm"
,"-rf"
, i"$HOME"
?dlaczego
{}
nie jest cytowany ani cytowany?
- Czy możesz podać inne przykłady (nadal z
sh -c
lub bez, jeśli dotyczy; z lub bez,find
które mogą nie być konieczne), w których stosuje się ten sam rodzaj problemu i rozwiązania, i które są minimalnymi przykładami, abyśmy mogli skupić się na problemie i rozwiązaniu za pomocą jak najmniej rozproszenia? Zobacz Sposoby dostarczania argumentów do polecenia wykonanego przez `bash -c`
Dzięki.
Odpowiedzi:
Nie jest to tak naprawdę związane z cytowaniem, ale raczej z przetwarzaniem argumentów.
Rozważ ryzykowny przykład:
To jest analizowany przez powłokę i podzielone na sześć słów:
find
,-exec
,sh
,-c
,something {}
(bez cudzysłowów dłużej);
. Nie ma nic do rozszerzenia. Powłoka działafind
z tymi sześcioma słowami jako argumentami.Kiedy
find
znajdzie coś do procesu, powiedzmyfoo; rm -rf $HOME
, zastępuje{}
sięfoo; rm -rf $HOME
, i biegniesh
z argumentamish
,-c
isomething foo; rm -rf $HOME
.sh
teraz widzi-c
i jako wynik analizujesomething foo; rm -rf $HOME
( pierwszy argument nie będący opcją ) i wykonuje wynik.Teraz rozważ bezpieczniejszy wariant:
Trasy powłoki
find
z argumentamifind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Teraz gdy
find
znaleziskafoo; rm -rf $HOME
, zastępuje{}
znowu, i biegniesh
z argumentamish
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
widzi-c
i przekładasomething "$@"
jako polecenie uruchomienia ish
ifoo; rm -rf $HOME
jako parametry pozycyjne ( wychodząc z$0
) rozszerza"$@"
sięfoo; rm -rf $HOME
w postaci pojedynczej wartości , i uruchamiasomething
się jeden argumentfoo; rm -rf $HOME
.Możesz to zobaczyć za pomocą
printf
. Utwórz nowy katalog, wprowadź go i uruchomUruchomienie pierwszego wariantu w następujący sposób
produkuje
podczas gdy drugi wariant działa jako
produkuje
źródło
{}
nie jest uciekany ani cytowany?;
i{}
) muszą być poprzedzone („ \ ”) lub cytowane, aby chronić je przed rozszerzeniem przez powłokę.” Czy masz na myśli, że jest to niewłaściwe dla bash?findutils
podręczniku pochodzi z co najmniej 1996 r. ... Oczywiście nie ma sensu cytować{}
ani jako,'{}'
ani"{}"
nie jest konieczne.find some/path -exec sh -c 'something "$@"' {} \;
są w rzeczywistości trzy warstwy przetwarzania / przekazywania argumentów, dwie odmiany powłoki i jedna z podstawowych odmian surowego systemu operacyjnego.Część 1:
find
po prostu używa zamiany tekstu.Tak, jeśli zrobiłeś to bez cudzysłowu:
a osoba atakująca mogła utworzyć plik o nazwie
; echo owned
, a następnie wykonałby polecenieco spowodowałoby
echo
wówczas uruchomienie powłokiecho owned
.Ale jeśli dodasz cytaty, osoba atakująca może po prostu zakończyć je, a następnie umieścić za nim złośliwe polecenie, tworząc plik o nazwie
'; echo owned
:co skutkowałoby pracującym powłoki
echo ''
,echo owned
.(jeśli zamieniłeś podwójne cudzysłowy na pojedyncze cudzysłowy, atakujący mógłby użyć również innego rodzaju cudzysłowów).
Część 2:
W
find -exec sh -c 'something "$@"' sh {} \;
The{}
początkowo nie jest interpretowane przez powłokę, jest wykonywany bezpośrednioexecve
, więc dodawanie cytatów powłoka nie pomoże.nie ma żadnego efektu, ponieważ powłoka usuwa podwójne cudzysłowy przed uruchomieniem
find
.dodaje cudzysłowy, że powłoka nie traktuje specjalnie, więc w większości przypadków oznacza to po prostu, że polecenie nie zrobi tego, co chcesz.
Mając to rozwinąć do
/tmp/foo;
,rm
,-rf
,$HOME
nie powinno być problemu, ponieważ są to argumentysomething
, isomething
prawdopodobnie nie traktują swoje argumenty jako polecenia do wykonania.Część 3:
Zakładam, że podobne rozważania dotyczą wszystkiego, co wymaga niezaufanego wejścia i uruchamia je jako (część) polecenia, na przykład
xargs
iparallel
.źródło
xargs
jeśli tylko jest niebezpieczny-I
lub-J
jest stosowany. W normalnej pracy dodaje tylko argumenty na końcu listy, tak jak to-exec ... {} +
robi.parallel
dla kontrastu domyślnie uruchamia powłokę bez wyraźnego żądania od użytkownika, zwiększając wrażliwą powierzchnię; jest to kwestia, która była przedmiotem wielu dyskusji; wyjaśnienie znajduje się w unix.stackexchange.com/questions/349483/ ... oraz dołączony link do lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).parallel
proces „odbiornika” na drugim końcu dowolnego gniazda, który byłby w stanie wykonać bezpośredni proces bez powłokiexecv
. To właśnie zrobiłbym, gdybyś wdrożył narzędzie w twoje buty. Posiadanie nieinteraktywnego programu, który zachowuje się inaczej w zależności od bieżącej wartości,SHELL
nie jest niczym zaskakującym.execv
zapewnia, jest to prostsze i mniej zadziwiające oraz zasada, która nie zmienia się w różnych lokalizacjach / środowiskach wykonawczych.W pewnym sensie cytowanie nie może tutaj pomóc . Nazwa pliku zastępowana zamiast
{}
może zawierać dowolne znaki, w tym cudzysłowy . Niezależnie od użytej formy cytowania nazwa pliku może zawierać to samo i „zerwać” z cytowania.Nie.
"$@"
Rozwija się do parametrów pozycyjnych jako osobnych słów i nie dzieli ich dalej. Tutaj{}
jest argumentemfind
samym w sobie ifind
przekazuje bieżącą nazwę pliku również jako odrębny argumentsh
. Jest bezpośrednio dostępny jako zmienna w skrypcie powłoki, nie jest przetwarzany jako samo polecenie powłoki.W większości pocisków nie musi tak być. Jeśli uruchomisz
fish
, musi to być:fish -c 'echo {}'
drukuje pustą linię. Ale nie ma znaczenia, czy to zacytujesz, powłoka po prostu usunie cytaty.Za każdym razem, gdy rozwijasz nazwę pliku (lub inny niekontrolowany ciąg), ponieważ jest w ciągu, który jest traktowany jako jakiś kod (*) , istnieje możliwość wykonania dowolnego polecenia.
Na przykład
$f
powoduje to bezpośrednie rozszerzenie kodu Perla i spowoduje problemy, jeśli nazwa pliku zawiera podwójny cudzysłów. Cytat w nazwie pliku kończy cytat w kodzie Perla, a reszta nazwy pliku może zawierać dowolny kod Perla:(Nazwa pliku musi być nieco dziwna, ponieważ Perl analizuje cały kod z góry, przed uruchomieniem dowolnego z nich. Musimy więc uniknąć błędu analizy).
Podczas gdy przechodzi to bezpiecznie przez argument:
(Nie ma sensu uruchamiać kolejnej powłoki bezpośrednio z powłoki, ale jeśli tak, to podobnie jak w
find -exec sh ...
przypadku)(* jakiś kod zawiera SQL, więc obowiązkowy XKCD: https://xkcd.com/327/ plus wyjaśnienie: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
źródło
Twoje obawy są właśnie powodem, dla którego GNU Parallel cytuje dane wejściowe:
To się nie uruchomi
echo pwned
.Uruchomi powłokę, więc jeśli wydasz polecenie, nie otrzymasz niespodzianki:
Więcej informacji na temat problemu spawnowania powłoki znajduje się na stronie : https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell
źródło
find . -print0 | xargs -0 printf "Argument: %s\n"
jest równie bezpieczne (a raczej moro, ponieważ z-print0
jednym uchwytem poprawnie obsługuje nazwy plików z nowymi liniami); Cytowanie równoległe jest obejściem problemu, który w ogóle nie istnieje, gdy nie ma powłoki.| wc
do polecenia, musisz przeskoczyć przez obręcze, aby było bezpieczne. GNU Parallel jest domyślnie bezpieczny.foo {} | wc
nash -c 'foo "$1" | wc
sh {} `, mogłoby być inaczej.