Saya menulis ini sebagai penyusunan ulang gaya tutorial dari jawaban yang sangat baik oleh Chris Down di atas.
Dalam bash, Anda dapat memiliki variabel shell seperti ini
$ t="hi there"
$ echo $t
hi there
$
Secara default, variabel-variabel ini tidak diwarisi oleh proses anak.
$ bash
$ echo $t
$ exit
Tetapi jika Anda menandai mereka untuk ekspor, bash akan menetapkan bendera yang berarti mereka akan masuk ke lingkungan subproses (meskipun envp
parameternya tidak banyak terlihat, main
program C Anda memiliki tiga parameter: di main(int argc, char *argv[], char *envp[])
mana array pointer terakhir adalah array variabel shell dengan definisi mereka).
Jadi mari kita ekspor t
sebagai berikut:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Sedangkan di atas t
tidak ditentukan dalam subkulit, sekarang muncul setelah kami mengekspornya (gunakan export -n t
jika Anda ingin berhenti mengekspornya).
Tetapi fungsi dalam bash adalah binatang yang berbeda. Anda mendeklarasikannya seperti ini:
$ fn() { echo "test"; }
Dan sekarang Anda bisa menjalankan fungsi dengan memanggilnya seolah-olah itu adalah perintah shell lain:
$ fn
test
$
Sekali lagi, jika Anda menelurkan subkulit, fungsi kami tidak diekspor:
$ bash
$ fn
fn: command not found
$ exit
Kami dapat mengekspor fungsi dengan export -f
:
$ export -f fn
$ bash
$ fn
test
$ exit
Inilah bagian yang sulit: fungsi yang diekspor seperti fn
dikonversi menjadi variabel lingkungan seperti ekspor variabel shell t
kami di atas. Ini tidak terjadi ketika fn
merupakan variabel lokal, tetapi setelah ekspor kita dapat melihatnya sebagai variabel shell. Namun, Anda juga dapat memiliki variabel shell reguler (yaitu, tidak berfungsi) dengan nama yang sama. bash membedakan berdasarkan isi variabel:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$
Sekarang kita dapat menggunakan env
untuk menampilkan semua variabel shell yang ditandai untuk diekspor dan fungsi reguler fn
dan fn
muncul:
$ env
.
.
.
fn=regular
fn=() { echo "test"
}
$
Sub-shell akan mencerna kedua definisi: satu sebagai variabel reguler dan satu sebagai fungsi:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Anda dapat mendefinisikan fn
seperti yang kami lakukan di atas, atau langsung sebagai tugas variabel biasa:
$ fn='() { echo "direct" ; }'
Perhatikan ini adalah hal yang luar biasa tinggi untuk dilakukan! Biasanya kita akan mendefinisikan fungsi fn
seperti yang kita lakukan di atas dengan fn() {...}
sintaks. Tetapi karena bash mengekspornya melalui lingkungan, kita dapat "memotong" langsung ke definisi reguler di atas. Perhatikan bahwa (berlawanan dengan intuisi Anda, mungkin) ini tidak menghasilkan fungsi baru yang fn
tersedia di shell saat ini. Tetapi jika Anda menelurkan shell ** sub **, maka itu akan.
Mari kita batalkan ekspor fungsi fn
dan biarkan reguler baru fn
(seperti yang ditunjukkan di atas) tetap utuh.
$ export -nf fn
Sekarang fungsinya fn
tidak lagi diekspor, tetapi variabel regulernya fn
, dan berisi () { echo "direct" ; }
di dalamnya.
Sekarang ketika subkulit melihat variabel biasa yang dimulai dengan ()
itu mengartikan sisanya sebagai definisi fungsi. Tapi ini hanya ketika shell baru dimulai. Seperti yang kita lihat di atas, hanya mendefinisikan variabel shell biasa dimulai dengan ()
tidak menyebabkannya berperilaku seperti fungsi. Anda harus memulai subkulit.
Dan sekarang bug "shellshock":
Seperti yang baru saja kita lihat, ketika shell baru mencerna definisi dari variabel biasa yang dimulai dengan ()
itu menafsirkannya sebagai fungsi. Namun, jika ada lebih banyak diberikan setelah kurung kurawal yang mendefinisikan fungsi, ia mengeksekusi apa pun yang ada di sana.
Ini adalah persyaratan, sekali lagi:
- Pesta baru muncul
- Variabel lingkungan dicerna
- Variabel lingkungan ini dimulai dengan "()" dan kemudian berisi fungsi tubuh di dalam kurung kurawal, dan kemudian memiliki perintah sesudahnya
Dalam hal ini, bash yang rentan akan menjalankan perintah yang terakhir.
Contoh:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
Variabel ekspor reguler ex
diteruskan ke subshell yang ditafsirkan sebagai fungsi ex
tetapi perintah trailing dieksekusi ( this is bad
) ketika subshell muncul.
Menjelaskan tes satu baris yang apik
Garis satu yang populer untuk menguji kerentanan Shellshock adalah yang dikutip dalam pertanyaan @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Berikut ini rinciannya: pertama :
in bash hanyalah singkatan true
. true
dan :
keduanya mengevaluasi (Anda dapat menebaknya) benar, dalam bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Kedua, env
perintah (juga dibangun ke dalam bash) mencetak variabel lingkungan (seperti yang kita lihat di atas) tetapi juga dapat digunakan untuk menjalankan perintah tunggal dengan variabel yang diekspor (atau variabel) yang diberikan kepada perintah itu, dan bash -c
menjalankan satu perintah dari garis komando:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Jadi menjahit semua hal ini bersama-sama, kita dapat menjalankan bash sebagai sebuah perintah, memberinya beberapa hal dummy untuk dilakukan (seperti bash -c echo this is a test
) dan mengekspor variabel yang dimulai dengan ()
sehingga subkulit akan menafsirkannya sebagai fungsi. Jika shellshock ada, itu juga akan segera menjalankan perintah trailing di subkulit. Karena fungsi yang kami lewati tidak relevan bagi kami (tetapi harus diuraikan!), Kami menggunakan fungsi valid terpendek yang bisa dibayangkan:
$ f() { :;}
$ f
$
Fungsi di f
sini hanya menjalankan :
perintah, yang mengembalikan true dan keluar. Sekarang tambahkan beberapa perintah "jahat" dan ekspor variabel reguler ke subkulit dan Anda menang. Ini adalah satu-liner lagi:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Jadi x
diekspor sebagai variabel reguler dengan fungsi valid sederhana dengan echo vulnerable
ditempel di akhir. Ini diteruskan ke bash, dan bash mengartikan x
sebagai fungsi (yang tidak kita pedulikan) maka mungkin mengeksekusi echo vulnerable
jika shellshock hadir.
Kami dapat mempersingkat one-liner sedikit dengan menghapus this is a test
pesan:
$ env x='() { :;}; echo vulnerable' bash -c :
Ini tidak mengganggu this is a test
tetapi menjalankan :
perintah diam lagi. (Jika Anda berhenti -c :
maka Anda duduk di subkulit dan harus keluar secara manual.) Mungkin versi yang paling ramah pengguna adalah yang ini:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"