Tidak masalah bagaimana Anda menyelesaikan ini, itu masih tidak akan banyak membantu. Satu- cdsatunya efek di dalam bash -cshell.
Kusalananda
Saya hanya memberikan contoh minimal untuk pertanyaan saja, itu bukan solusi yang berfungsi untuk masalah saya yang sebenarnya, yaitu unix.stackexchange.com/a/269080/674 ditambah bahwa dalam string perintah yang diberikan kepada sudo bash -csaya memiliki ekspansi variabel untuk pathname yang mungkin berisi spasi putih.
Tim
Jawaban:
13
Tidak ada pemisahan kata (seperti pada fitur yang membagi variabel pada ekspansi yang tidak dikutip) dalam kode tersebut karena $myvartidak tidak dikutip.
Namun ada kerentanan injeksi perintah seperti $myvaryang diperluas sebelum diteruskan ke bash. Jadi isinya ditafsirkan sebagai kode bash!
Spasi di sana akan menyebabkan beberapa argumen diteruskan cd, bukan karena pemisahan kata , tetapi karena mereka akan diuraikan sebagai beberapa token dalam sintaks shell. Dengan nilai bye;reboot, itu akan reboot! ¹
Di sini, Anda ingin:
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
(di mana Anda melewatkan konten $myvarsebagai argumen pertama dari skrip inline itu; perhatikan bagaimana keduanya $myvardan $1dikutip untuk shell masing-masing untuk mencegah IFS-word-splitting (and globbing)).
Atau:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
(tempat Anda mengirimkan konten $myvardalam variabel lingkungan).
Tentu saja Anda tidak akan mencapai sesuatu yang bermanfaat dengan menjalankan hanyacd dalam skrip inline itu (selain memeriksa apakah rootbisa cdmasuk ke sana). Agaknya, Anda ingin skrip itu cdada di sana dan kemudian melakukan sesuatu yang lain di sana seperti:
Jika maksudnya adalah menggunakan sudountuk dapat cdmasuk ke direktori yang Anda tidak memiliki akses, maka itu tidak bisa benar-benar berfungsi.
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
akan memulai interaktif bashdengan direktori saat ini di $myvar. Tapi cangkang itu akan berjalan sebagai root.
Anda bisa melakukannya:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
Untuk mendapatkan interaktif bashyang tidak terjangkau dengan direktori saat ini $myvar, tetapi jika Anda tidak memiliki izin untuk cdmasuk ke direktori itu di tempat pertama, Anda tidak akan dapat melakukan apa pun di direktori itu bahkan jika itu adalah direktori kerja Anda saat ini.
$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.':Permission denied
Pengecualiannya adalah jika Anda memiliki izin pencarian ke direktori itu sendiri tetapi tidak ke salah satu komponen direktori dari jalurnya:
¹ secara tegas, untuk nilai-nilai $myvarseperti $(seq 10)(secara harfiah), akan ada pemisahan kata tentu saja pada perluasan penggantian perintah oleh bash shell dimulai sebagairoot
Ada sihir kutipan baru (bash 4.4) yang memungkinkan Anda untuk lebih langsung melakukan apa yang Anda inginkan. Tergantung pada konteks yang lebih besar, menggunakan salah satu teknik lain mungkin lebih baik, tetapi itu berfungsi dalam konteks terbatas ini.
Jika Anda tidak dapat mempercayai konten $myvar, satu-satunya pendekatan yang masuk akal adalah menggunakan GNU printfuntuk membuat versi yang lolos dari itu:
ARGUMENT dicetak dalam format yang dapat digunakan kembali sebagai input shell, keluar dari karakter yang tidak dapat dicetak dengan $''sintaks POSIX yang diusulkan .
GNU printfhanya akan dipanggil di sana pada sistem GNU dan jika itu $(printf '%q' "$myvar")dijalankan di shell di mana printftidak dibangun. Sebagian besar cangkang telah printfdibangun saat ini. Tidak semua mendukung %qdan ketika mereka melakukannya, mereka akan mengutip hal dalam format yang didukung oleh shell yang sesuai, yang mungkin tidak harus sama dengan yang dipahami oleh bash. Sebenarnya, bash's printfgagal untuk mengutip hal-hal dengan benar untuk bahkan dirinya sendiri untuk beberapa nilai-nilai $myvardi beberapa lokal.
Stéphane Chazelas
Dengan bash-4.3setidaknya, itu masih kerentanan injeksi perintah dengan myvar=$'\xa3``reboot`\xa3`' lokal zh_HK.big5hkscs misalnya.
Stéphane Chazelas
3
Pertama-tama, karena orang lain telah mencatat cdperintah Anda tidak berguna karena terjadi dalam konteks shell yang segera keluar. Tetapi secara umum pertanyaan itu masuk akal. Yang Anda butuhkan adalah cara untuk mengutip shell string yang sewenang-wenang , sehingga dapat digunakan dalam konteks di mana ia akan ditafsirkan sebagai string yang dikutip shell. Saya punya contoh di halaman trik sulap saya :
quote (){ printf %s\\n "$1"| sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/";}
Apa yang Stéphane Chazelas sarankan adalah cara terbaik untuk melakukan ini. Saya hanya akan memberikan jawaban tentang cara mengutip dengan aman sintaks ekspansi parameter yang bertentangan dengan menggunakan sedsubproses, seperti dalam jawaban R ..
Kutipan tunggal tidak akan berfungsi di sini, karena variabel Anda tidak akan diperluas. Jika Anda yakin bahwa variabel untuk jalur Anda dibersihkan, solusi paling sederhana adalah dengan hanya menambahkan tanda kutip di sekitar ekspansi yang dihasilkan, setelah itu dalam bash shell baru:
Masih kerentanan perintah injeksi, seperti untuk nilai $myvarlike $(reboot)atau"; reboot; #
Stéphane Chazelas
1
menggunakan sudo bash -c "cd -P -- '$myvar'"akan membatasi masalah hanya nilai-nilai $myvar yang mengandung karakter kutipan tunggal yang akan lebih mudah disanitasi.
cd
satunya efek di dalambash -c
shell.sudo bash -c
saya memiliki ekspansi variabel untuk pathname yang mungkin berisi spasi putih.Jawaban:
Tidak ada pemisahan kata (seperti pada fitur yang membagi variabel pada ekspansi yang tidak dikutip) dalam kode tersebut karena
$myvar
tidak tidak dikutip.Namun ada kerentanan injeksi perintah seperti
$myvar
yang diperluas sebelum diteruskan kebash
. Jadi isinya ditafsirkan sebagai kode bash!Spasi di sana akan menyebabkan beberapa argumen diteruskan
cd
, bukan karena pemisahan kata , tetapi karena mereka akan diuraikan sebagai beberapa token dalam sintaks shell. Dengan nilaibye;reboot
, itu akan reboot! ¹Di sini, Anda ingin:
(di mana Anda melewatkan konten
$myvar
sebagai argumen pertama dari skrip inline itu; perhatikan bagaimana keduanya$myvar
dan$1
dikutip untuk shell masing-masing untuk mencegah IFS-word-splitting (and globbing)).Atau:
(tempat Anda mengirimkan konten
$myvar
dalam variabel lingkungan).Tentu saja Anda tidak akan mencapai sesuatu yang bermanfaat dengan menjalankan hanya
cd
dalam skrip inline itu (selain memeriksa apakahroot
bisacd
masuk ke sana). Agaknya, Anda ingin skrip itucd
ada di sana dan kemudian melakukan sesuatu yang lain di sana seperti:Jika maksudnya adalah menggunakan
sudo
untuk dapatcd
masuk ke direktori yang Anda tidak memiliki akses, maka itu tidak bisa benar-benar berfungsi.akan memulai interaktif
bash
dengan direktori saat ini di$myvar
. Tapi cangkang itu akan berjalan sebagairoot
.Anda bisa melakukannya:
Untuk mendapatkan interaktif
bash
yang tidak terjangkau dengan direktori saat ini$myvar
, tetapi jika Anda tidak memiliki izin untukcd
masuk ke direktori itu di tempat pertama, Anda tidak akan dapat melakukan apa pun di direktori itu bahkan jika itu adalah direktori kerja Anda saat ini.Pengecualiannya adalah jika Anda memiliki izin pencarian ke direktori itu sendiri tetapi tidak ke salah satu komponen direktori dari jalurnya:
¹ secara tegas, untuk nilai-nilai
$myvar
seperti$(seq 10)
(secara harfiah), akan ada pemisahan kata tentu saja pada perluasan penggantian perintah olehbash
shell dimulai sebagairoot
sumber
Ada sihir kutipan baru (bash 4.4) yang memungkinkan Anda untuk lebih langsung melakukan apa yang Anda inginkan. Tergantung pada konteks yang lebih besar, menggunakan salah satu teknik lain mungkin lebih baik, tetapi itu berfungsi dalam konteks terbatas ini.
sumber
Jika Anda tidak dapat mempercayai konten
$myvar
, satu-satunya pendekatan yang masuk akal adalah menggunakan GNUprintf
untuk membuat versi yang lolos dari itu:Seperti yang
printf
dikatakan halaman manual:sumber
printf
hanya akan dipanggil di sana pada sistem GNU dan jika itu$(printf '%q' "$myvar")
dijalankan di shell di manaprintf
tidak dibangun. Sebagian besar cangkang telahprintf
dibangun saat ini. Tidak semua mendukung%q
dan ketika mereka melakukannya, mereka akan mengutip hal dalam format yang didukung oleh shell yang sesuai, yang mungkin tidak harus sama dengan yang dipahami olehbash
. Sebenarnya,bash
'sprintf
gagal untuk mengutip hal-hal dengan benar untuk bahkan dirinya sendiri untuk beberapa nilai-nilai$myvar
di beberapa lokal.bash-4.3
setidaknya, itu masih kerentanan injeksi perintah denganmyvar=$'\xa3``reboot`\xa3`'
lokal zh_HK.big5hkscs misalnya.Pertama-tama, karena orang lain telah mencatat
cd
perintah Anda tidak berguna karena terjadi dalam konteks shell yang segera keluar. Tetapi secara umum pertanyaan itu masuk akal. Yang Anda butuhkan adalah cara untuk mengutip shell string yang sewenang-wenang , sehingga dapat digunakan dalam konteks di mana ia akan ditafsirkan sebagai string yang dikutip shell. Saya punya contoh di halaman trik sulap saya :Dengan fungsi ini, Anda dapat melakukan:
sumber
Apa yang Stéphane Chazelas sarankan adalah cara terbaik untuk melakukan ini. Saya hanya akan memberikan jawaban tentang cara mengutip dengan aman sintaks ekspansi parameter yang bertentangan dengan menggunakan
sed
subproses, seperti dalam jawaban R ..Output dari skrip itu adalah:
sumber
Ya, ada pemisahan kata.
Nah, secara teknis, baris perintah terpecah pada bash parsing baris.
Pertama mari kita mengutip dengan benar:
Solusi paling sederhana (dan tidak aman) untuk mendapatkan perintah untuk bekerja karena saya yakin Anda akan menginginkannya adalah:
Mari kita konfirmasi apa yang terjadi (dengan asumsi
myvar="/path to/my directory"
):Seperti yang Anda lihat, string path terpecah pada spasi. Dan:
Tidak terpecah.
Namun, perintah (seperti yang tertulis) tidak aman. Penggunaan yang lebih baik:
Itu akan melindungi cd terhadap file yang dimulai dengan tanda hubung (-) atau tautan berikut yang dapat menyebabkan masalah.
Itu akan bekerja jika Anda dapat percaya bahwa string
$myvar
adalah jalur.Namun, "injeksi kode" masih dimungkinkan karena Anda "mengeksekusi string":
Anda membutuhkan kutipan string yang lebih kuat, seperti:
Seperti yang Anda lihat di atas, nilai tidak ditafsirkan tetapi hanya digunakan sebagai
"$1'
.Sekarang kita dapat (dengan aman) menggunakan sudo:
Jika ada beberapa argumen, Anda dapat menggunakan:
sumber
Kutipan tunggal tidak akan berfungsi di sini, karena variabel Anda tidak akan diperluas. Jika Anda yakin bahwa variabel untuk jalur Anda dibersihkan, solusi paling sederhana adalah dengan hanya menambahkan tanda kutip di sekitar ekspansi yang dihasilkan, setelah itu dalam bash shell baru:
sumber
$myvar
like$(reboot)
atau"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
akan membatasi masalah hanya nilai-nilai$myvar
yang mengandung karakter kutipan tunggal yang akan lebih mudah disanitasi.