Ochrona polecenia powłoki za pomocą zmiennej łańcuchowej

9

W języku programowania wykonuję proste polecenie powłoki

cd var; echo > create_a_file_here

z var jest zmienną, która zawiera ciąg (miejmy nadzieję) katalogu do miejsca, gdzie chcę, aby utworzyć plik „create_a_file_here”. Teraz, jeśli ktoś zobaczy ten wiersz kodu, można go wykorzystać, przypisując na przykład:

var = "; rm -rf /"

Sprawy mogą być dość brzydkie. Jednym ze sposobów uniknięcia powyższego przypadku może być przeszukanie ciągu w var dla znaków specjalnych, takich jak „;” przed wykonaniem polecenia powłoki, ale wątpię, czy obejmuje to wszystkie możliwe exploity.

Czy ktoś zna dobry sposób na to, aby „cd var” zmieniał tylko katalog i nic więcej?

ES___
źródło
4
W zależności od tego, w jaki sposób można wywołać powłokę, można również przekazać varjako argument. Na przykład dzwoniąc shz argumentami -c, 'cd "$1"; echo > create_a_file_here', 'sh', varroboty i nie wymaga żadnych zmian var. 'sh'Argumentem jest przekazywana jako $0.
ipsec
1
Jaki język programowania? Używasz POSIX sh, czy tworzysz własny język programowania z podobną składnią, ale który rozwija się varzamiast wymagać pisania cd "$var"? Czy to jest bashz shopt -s cdable_vars? Och, myślę, że masz na myśli, że jakiś inny program prosi powłokę o uruchomienie tych poleceń. Więc po prostu zacytuj var, ale upewnij się, że nie zawiera on samego znaku cudzysłowu ...
Peter Cordes
@PeterCordes Jeśli mówisz o Bash, podwójnie cytowana zmienna, która zawiera podwójny cudzysłów, jest w porządku. np . s='"'; echo "$s"odbitki ".
wjandrea
@WJAndrea: tak, ale nie ma cytatu „karta atutowa”, którego nie można pokonać przy konstruowaniu przypisania zmiennej z niezaufanego wejścia. Och, rozwiązanie: wykonaj var=untrusted stringw programie nadrzędnym, podobnie varjak zmienna środowiskowa, która jest już ustawiona podczas wywoływania sh. Następnie musisz go zacytować za każdym razem, gdy go rozszerzysz, co można zrobić niezawodnie. Ach, widzę, że ten pomysł jest już częścią odpowiedzi Stéphane>. <
Peter Cordes

Odpowiedzi:

9

Jeśli dobrze rozumiem, varto zmienna w twoim języku programowania.

W swoim języku programowania prosisz powłokę o interpretację łańcucha, który jest konkatenacją "cd ", zawartością tej zmiennej i "; echo > create_a_file_here".

Jeśli tak, jeśli zawartość varnie jest ściśle kontrolowana, jest to luka w zabezpieczeniach polegająca na wstrzykiwaniu poleceń.

Możesz spróbować poprawnie cytować zawartość zmiennej¹ w składni powłoki, aby zagwarantować, że zostanie ona przekazana jako jeden argument do cdwbudowanego.

Innym podejściem byłoby przekazanie zawartości tej zmiennej w inny sposób. Oczywistym sposobem byłoby przekazanie tego w zmiennej środowiskowej. Na przykład w C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Tym razem kod, o który prosisz powłokę, aby ją zinterpretował, został naprawiony, nadal musimy go poprawnie zapisać w składni powłoki (tutaj zakłada się, że jest to powłoka zgodna z POSIX):

  • należy cytować rozszerzenie zmiennej powłoki, aby zapobiec split + glob
  • trzeba -Pdo cdzrobić prostychdir()
  • musisz --zaznaczyć koniec opcji, aby uniknąć problemów z varrozpoczęciem -(lub +w niektórych powłokach)
  • Ustawiamy CDPATHpusty ciąg na wypadek, gdyby znajdował się w środowisku
  • echoPolecenie uruchamiamy tylko wtedy, gdy się cdpowiedzie.

Pozostaje (przynajmniej) jeden problem: jeśli vartak -, to nie chdir do katalogu o nazwie, -ale do poprzedniego katalogu (jak jest przechowywany w $OLDPWD) i OLDPWD=- CDPATH= cd -P -- "$DIR"nie ma gwarancji, że obejdzie go. Potrzebujesz więc czegoś takiego:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Pamiętaj, że samo wykonanie a niesystem(concat("cd \"", var, "\"; echo...")); jest właściwą drogą, po prostu przenosisz problem.

Na przykład var = "$(rm -rf /)"nadal byłby problem.

Tylko niezawodny sposób, aby tekst wycenę dla Bourne-jak muszli jest użycie apostrofów , a także dbać o pojedynczych cytatów, które mogą wystąpić w ciągu. Na przykład zmień a char *var = "ab'cd"na char *escaped_var = "'ab'\\''cd'". Oznacza to, że zastąpienie wszystkich 'do '\''i owinąć całą sprawę wnętrze '...'.

Że nadal zakłada, że notowane ciąg nie jest używane w backticks, i że nadal potrzebne --, -P, &&, CDPATH=...

Stéphane Chazelas
źródło
12

Proste rozwiązanie: nie wywołuj powłoki z programu. W ogóle.

Twój przykład tutaj jest trywialny, zmiana katalogu i tworzenie plików powinno być łatwe w dowolnym języku programowania. Ale nawet jeśli musisz uruchomić polecenie zewnętrzne, zwykle nie trzeba tego robić przez powłokę.

Tak więc np. W Pythonie, zamiast uruchamiać os.system("somecmd " + somearg), użyj subprocess.run(["somecmd", somearg]). W C zamiast system(), użyj fork()i exec()(lub znajdź bibliotekę, która to robi).

Jeśli potrzebujesz użyć powłoki, zacytuj argumenty wiersza poleceń lub przekaż je przez środowisko, jak w odpowiedzi Stéphane'a . Ponadto, jeśli martwisz się postacią specjalną, poprawnym rozwiązaniem jest nie filtrowanie (czarnej listy) potencjalnie niebezpiecznych postaci, a jedynie utrzymywanie postaci bezpiecznych (biała lista).

Zezwól tylko na postacie, których funkcje znasz, w ten sposób zmniejsza się ryzyko pominięcia czegoś. Rezultatem końcowym może być to, że zdecydujesz się tylko na to [a-zA-Z0-9_], ale to może wystarczyć, aby wykonać zadanie. Możesz także sprawdzić, czy twoje ustawienia regionalne i zestaw narzędzi nie zawierają liter akcentowanych takich jak äi tam ö. Prawdopodobnie nie są uważane za specjalne przez żadną powłokę, ale znowu, lepiej być pewnym, czy zdadzą, czy nie.

ilkkachu
źródło
1
I upewnij się, że to, czego używasz do dopasowania [a-zA-Z0-9], nie obejmuje rzeczy takich jak àczyje kodowanie może być również źle interpretowane przez niektóre powłoki (jak bash) w niektórych lokalizacjach.
Stéphane Chazelas,
@ StéphaneChazelas (poza zainteresowaniem :) czy są (i tuż pod tymi dotkniętymi lokalizacjami?)
Wilf
10

W języku programowania powinny istnieć lepsze sposoby wykonywania zadań niż wykonywanie poleceń powłoki. Na przykład zastąpienie cd varodpowiednikiem języka programowania chdir (var);powinno zapewnić, że wszelkie oszustwa o wartości vartylko powodują błąd „Nie znaleziono katalogu” zamiast niezamierzonych i prawdopodobnie złośliwych działań.

Możesz także używać ścieżek bezwzględnych zamiast zmieniać katalogi. Wystarczy połączyć nazwę katalogu, ukośnik i nazwę pliku, którego chcesz użyć.

W C mogę zrobić coś takiego:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Z pewnością twój język programowania może zrobić coś podobnego?

telcoM
źródło
Dziękuję za odpowiedź, ale niestety muszę użyć obejścia z poleceniami bash. Testowałem cytowanie zmiennej - jak sugerowało muru - i wydaje się, że działa!
ES___