Я написав це як підручник в стилі підручника, який переглядав чудову відповідь Крісом Дауном вище.
У bash ви можете мати такі змінні оболонки
$ t="hi there"
$ echo $t
hi there
$
За замовчуванням ці змінні не успадковуються дочірніми процесами.
$ bash
$ echo $t
$ exit
Але якщо ви позначите їх для експорту, bash встановить прапор, що означає, що вони потраплять у середовище підпроцесів (хоча envp
параметр мало що видно, main
у вашій програмі C є три параметри: main(int argc, char *argv[], char *envp[])
де останній масив покажчиків - це масив змінних оболонок з їх визначеннями).
Тож давайте експортуємо t
так:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Тоді як вище t
було визначено в нижній частині корпусу, воно з’являється після того, як ми експортували його (використовуйте, export -n t
якщо ви хочете припинити його експорт).
Але функції в баші - це інша тварина. Ви заявляєте їх так:
$ fn() { echo "test"; }
А тепер ви можете просто викликати функцію, викликаючи її так, ніби це була інша команда оболонки:
$ fn
test
$
Ще раз, якщо ви нерестуєте передплату, наша функція не експортується:
$ bash
$ fn
fn: command not found
$ exit
Ми можемо експортувати функцію за допомогою export -f
:
$ export -f fn
$ bash
$ fn
test
$ exit
Ось складна частина: експортована функція на зразок fn
перетворюється на змінну середовища так, як t
вище був експорт змінної оболонки . Це не відбувається, коли fn
була локальною змінною, але після експорту ми можемо бачити її як змінну оболонки. Однак ви також можете мати звичайну змінну оболонки (тобто без функції) з тим самим іменем. bash розрізняє на основі вмісту змінної:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$
Тепер ми можемо використовувати env
для показу всіх змінних оболонок, позначених для експорту, і звичайних, fn
і функцій fn
:
$ env
.
.
.
fn=regular
fn=() { echo "test"
}
$
Під-оболонці буде передаватися обидва визначення: одне як звичайна змінна і одне як функція:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Ви можете визначити, fn
як ми це робили вище, або безпосередньо як звичайне призначення змінної:
$ fn='() { echo "direct" ; }'
Зауважте, це надзвичайно незвичайна річ! Зазвичай ми б визначали функцію, fn
як це було зроблено вище із fn() {...}
синтаксисом. Але оскільки Bash експортує його через навколишнє середовище, ми можемо «скоротити» безпосередньо до вищезгаданого регулярного визначення. Зауважте, що (можливо, проти вашої інтуїції) це не призводить до появи нової функції, fn
наявної в поточній оболонці. Але якщо ви породжуєте ** під ** оболонку, то вона буде.
Скасуємо експорт функції fn
та залишимо новий звичайний fn
(як показано вище) недоторканим.
$ export -nf fn
Тепер функція fn
більше не експортується, але звичайна змінна fn
є, і вона міститься () { echo "direct" ; }
в ній.
Тепер, коли піддіаграма бачить звичайну змінну, яка починається з ()
неї, інтерпретує решту як визначення функції. Але це лише тоді, коли починається нова оболонка. Як ми бачили вище, просто визначення звичайної змінної оболонки, починаючи з ()
, не призводить до того, що вона поводиться як функція. Ви повинні запустити передплату.
А тепер помилка "оболонки":
Як ми нещодавно бачили, коли нова оболонка заглиблює визначення регулярної змінної, починаючи з ()
неї, інтерпретує її як функцію. Однак якщо після закриття дужки, що визначає функцію, надано більше, вона виконує все , що там є.
Це ще раз вимоги:
- Породжується новий баш
- Застосовується змінна середовище
- Ця змінна середовище починається з "()", а потім містить тіло функції всередині дужок, а потім має команди після цього
У цьому випадку вразливий баш виконує останні команди.
Приклад:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
Звичайна експортована змінна ex
була передана до підклітини, яка була інтерпретована як функція, ex
але трейлінг команди виконувались ( this is bad
), коли покоління породжувалося.
Пояснення витонченого однорядкового тесту
Популярна однолінійка для тестування на вразливість Shellshock - це та, що цитується у запитанні @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Ось розбивка: спочатку :
в bash - це лише скорочення true
. true
і :
обидва оцінюють (ви здогадалися) правдою, в баш:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
По-друге, env
команда (також вбудована в bash) друкує змінні середовища (як ми бачили вище), але також може бути використана для запуску однієї команди з експортованою змінною (або змінними), наданої цій команді, і bash -c
виконує одну команду зі своєї командний рядок:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Отже, зшивши всі ці речі разом, ми можемо запустити bash як команду, дати йому якусь фіктивну річ (на зразок bash -c echo this is a test
) та експортувати змінну, яка починається з ()
того, що нижня частина буде інтерпретувати її як функцію. Якщо оболонка присутня, вона також негайно виконує будь-які трейлінг-команди в нижній частині. Оскільки функція, яку ми передаємо, не має значення для нас (але повинна розбиратися!), Ми використовуємо найкоротший дозволений функцію, що можна уявити:
$ f() { :;}
$ f
$
Тут функція f
просто виконує :
команду, яка повертає true та виходить. Тепер додайте до цього якусь "злу" команду та експортуйте звичайну змінну в підшару, і ви виграєте. Ось знову один лайнер:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Так x
експортується у вигляді звичайної змінної з простою дійсною функцією з echo vulnerable
прикріпленою до кінця. Це передається bash, і bash інтерпретується x
як функція (яка нас не хвилює), то, можливо, виконує echo vulnerable
if присутнім оболонки.
Ми можемо трохи скоротити однолінійку, видаливши this is a test
повідомлення:
$ env x='() { :;}; echo vulnerable' bash -c :
Це не заважає, this is a test
але запускає тиху :
команду ще раз. (Якщо ви відмовитеся від цього, -c :
тоді ви сідаєте в передплату і виходите вручну.) Мабуть, найбільш зручною для користувача версія була б ця:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"