„Uruchom dowolne polecenie, które przekaże niezaufane dane do poleceń, które interpretują argumenty jako polecenia”


18

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 $HOMEdwie 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.

  1. Czy przyczyną problemu jest find -exec sh -c "something {}" \;to, że zamiennik {}nie jest cytowany i dlatego nie jest traktowany jako pojedynczy ciąg?
  2. 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?

  3. Czy możesz podać inne przykłady (nadal z sh -club bez, jeśli dotyczy; z lub bez, findktó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:


29

Nie jest to tak naprawdę związane z cytowaniem, ale raczej z przetwarzaniem argumentów.

Rozważ ryzykowny przykład:

find -exec sh -c "something {}" \;
  • 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ła findz tymi sześcioma słowami jako argumentami.

  • Kiedy findznajdzie coś do procesu, powiedzmy foo; rm -rf $HOME, zastępuje {}się foo; rm -rf $HOME, i biegnie shz argumentami sh, -ci something foo; rm -rf $HOME.

  • shteraz widzi -ci jako wynik analizuje something foo; rm -rf $HOME( pierwszy argument nie będący opcją ) i wykonuje wynik.

Teraz rozważ bezpieczniejszy wariant:

find -exec sh -c 'something "$@"' sh {} \;
  • Trasy powłoki findz argumentami find, -exec, sh, -c, something "$@", sh, {}, ;.

  • Teraz gdy findznaleziska foo; rm -rf $HOME, zastępuje {}znowu, i biegnie shz argumentami sh, -c, something "$@", sh, foo; rm -rf $HOME.

  • shwidzi -ci przekłada something "$@"jako polecenie uruchomienia i shi foo; rm -rf $HOMEjako parametry pozycyjne ( wychodząc z$0 ) rozszerza "$@"się foo; rm -rf $HOME w postaci pojedynczej wartości , i uruchamia somethingsię jeden argument foo; rm -rf $HOME.

Możesz to zobaczyć za pomocą printf. Utwórz nowy katalog, wprowadź go i uruchom

touch "hello; echo pwned"

Uruchomienie pierwszego wariantu w następujący sposób

find -exec sh -c "printf \"Argument: %s\n\" {}" \;

produkuje

Argument: .
Argument: ./hello
pwned

podczas gdy drugi wariant działa jako

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;

produkuje

Argument: .
Argument: ./hello; echo pwned

W bezpieczniejszym wariancie, dlaczego {}nie jest uciekany ani cytowany?
Tim

1
Zasadniczo nie trzeba go cytować; musiałby być cytowany tylko w powłoce, w której ma on inne znaczenie, i nie sądzę, że tak jest w przypadku każdej z głównych używanych powłok.
Stephen Kitt

From gnu.org/software/findutils/manual/html_mono/… : „Obie te konstrukcje ( ;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?
Tim

3
Mam na myśli, że jest to ogólnie niepoprawne dla powłok. Zobacz POSIX . To konkretne stwierdzenie w findutilspodręczniku pochodzi z co najmniej 1996 r. ... Oczywiście nie ma sensu cytować {}ani jako, '{}'ani "{}"nie jest konieczne.
Stephen Kitt

@Tim Występuje typowa sytuacja, w której intuicja jeszcze nie bierze pod uwagę faktu, że zachodzą tutaj dwa bardzo różne rodzaje przetwarzania argumentów: kiedy / jak powłoka analizuje argumenty poza wierszem tekstu (który jest tam, gdzie cytowanie ma znaczenie) różni się od przekazywania nieprzetworzonych argumentów systemu operacyjnego (gdzie cytaty są zwykłymi znakami). W poleceniu powłoki 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.
mtraceur

3

Część 1:

find po prostu używa zamiany tekstu.

Tak, jeśli zrobiłeś to bez cudzysłowu:

find . -type f -exec sh -c "echo {}" \;

a osoba atakująca mogła utworzyć plik o nazwie ; echo owned, a następnie wykonałby polecenie

sh -c "echo ; echo owned"

co spowodowałoby echowówczas uruchomienie powłoki echo 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:

find . -type f -exec sh -c "echo '{}'" \;

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średnio execve, więc dodawanie cytatów powłoka nie pomoże.

find -exec sh -c 'something "$@"' sh "{}" \;

nie ma żadnego efektu, ponieważ powłoka usuwa podwójne cudzysłowy przed uruchomieniem find.

find -exec sh -c 'something "$@"' sh "'{}'" \;

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, $HOMEnie powinno być problemu, ponieważ są to argumenty something, i somethingprawdopodobnie 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 xargsi parallel.


xargsjeśli tylko jest niebezpieczny -Ilub -Jjest stosowany. W normalnej pracy dodaje tylko argumenty na końcu listy, tak jak to -exec ... {} +robi.
Charles Duffy,

1
( paralleldla 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 ).
Charles Duffy

@CharlesDuffy Masz na myśli „ zmniejszanie wrażliwej powierzchni”. Atak zilustrowany przez OP faktycznie nie działa domyślnie z GNU Parallel.
Ole Tange

1
Tak, wiem, że potrzebujesz go do parzystości w SSH - jeśli nie pojawiłeś się zdalny parallelproces „odbiornika” na drugim końcu dowolnego gniazda, który byłby w stanie wykonać bezpośredni proces bez powłoki execv. 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, SHELLnie jest niczym zaskakującym.
Charles Duffy

1
Tak - uważam, że potoki i przekierowania powinny być niemożliwe, chyba że użytkownik jawnie uruchomi powłokę, w którym to momencie otrzymają tylko jawne zachowanie powłoki, którą jawnie uruchomili. Jeśli można oczekiwać tylko tego, co execvzapewnia, jest to prostsze i mniej zadziwiające oraz zasada, która nie zmienia się w różnych lokalizacjach / środowiskach wykonawczych.
Charles Duffy

3

1. 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 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.

2. ... ale skoro {}nie jest cytowany, czy nie "$@"ma tego samego problemu co oryginalne polecenie? Na przykład "$@"zostanie rozwinięty do "/tmp/foo;", "rm", "-rf", and "$HOME"?

Nie. "$@"Rozwija się do parametrów pozycyjnych jako osobnych słów i nie dzieli ich dalej. Tutaj {}jest argumentem findsamym w sobie i findprzekazuje bieżącą nazwę pliku również jako odrębny argument sh. Jest bezpośrednio dostępny jako zmienna w skrypcie powłoki, nie jest przetwarzany jako samo polecenie powłoki.

... dlaczego {}nie jest cytowany ani cytowany?

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.

3. Czy możesz podać inne przykłady ...

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 $fpowoduje 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:

touch '"; print "HELLO";"'
for f in ./*; do
    perl -le "print \"size: \" . -s \"$f\""
done

(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:

for f in ./*; do
    perl -le 'print "size: " . -s $ARGV[0]' "$f"
done

(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 )


1

Twoje obawy są właśnie powodem, dla którego GNU Parallel cytuje dane wejściowe:

touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\\n\" {}

To się nie uruchomi echo pwned.

Uruchomi powłokę, więc jeśli wydasz polecenie, nie otrzymasz niespodzianki:

# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo

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


1
... to powiedziawszy, find . -print0 | xargs -0 printf "Argument: %s\n"jest równie bezpieczne (a raczej moro, ponieważ z -print0jednym 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.
Charles Duffy

Ale jak tylko dodasz | wcdo polecenia, musisz przeskoczyć przez obręcze, aby było bezpieczne. GNU Parallel jest domyślnie bezpieczny.
Ole Tange

ITYM: stara się ukryć każde dziwactwo języka powłoki przez skomplikowany proces starannego cytowania wszystkiego. Nie jest to tak bezpieczne, jak prawidłowe przechowywanie danych w zmiennych, a nie mieszanie ich z kodem. Jeśli teraz miałoby to automatycznie przekonwertować foo {} | wcna sh -c 'foo "$1" | wcsh {} `, mogłoby być inaczej.
ilkkachu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.