Nah, Anda selalu bisa melakukan:
#! /bin/bash -
{ shopt -s expand_aliases;SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";};alias skip=":||:<<'SWITCH_TO_USER $_u'"
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
${_u+:} alias skip=:;} 2>/dev/null
skip
echo test
a=foo
set a b
SWITCH_TO_USER root
echo "$a and $1 as $(id -un)"
set -x
foo() { echo "bar as $(id -un)"; }
SWITCH_TO_USER rag
foo
set +x
SWITCH_TO_USER root again
echo "hi again from $(id -un)"
(ʘ‿ʘ)
Itu pertama kali dimulai sebagai lelucon karena mengimplementasikan apa yang diminta meskipun mungkin tidak persis seperti yang diharapkan, dan secara praktis tidak berguna. Tapi ketika berevolusi menjadi sesuatu yang bekerja sampai batas tertentu dan melibatkan beberapa peretasan yang bagus, berikut adalah sedikit penjelasan:
Seperti yang dikatakan Miroslav , jika kita mengesampingkan kemampuan gaya Linux (yang toh tidak akan membantu di sini juga), satu-satunya cara untuk proses tidak berubah untuk mengubah uid adalah dengan mengeksekusi setuid executable.
Setelah Anda mendapatkan hak istimewa pengguna super (dengan mengeksekusi setuid executable yang pemiliknya root misalnya), Anda dapat mengganti id pengguna yang efektif bolak-balik antara id pengguna asli Anda, 0 dan id lainnya kecuali jika Anda melepaskan id pengguna set yang disimpan ( suka hal-hal seperti sudo
atau su
biasanya dilakukan).
Contohnya:
$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env
Sekarang saya punya env
perintah yang memungkinkan saya untuk menjalankan perintah dengan id pengguna yang efektif dan set id pengguna yang disimpan 0 ( id pengguna asli saya masih 1000):
$ ./env id -u
0
$ ./env id -ru
1000
$ ./env -u PATH =perl -e '$>=1; system("id -u"); $>=0;$>=2; system("id -u");
$>=0; $>=$<=3; system("id -ru; id -u"); $>=0;$<=$>=4; system("id -ru; id -u")'
1
2
3
3
4
4
perl
memiliki pembungkus ke setuid
/ seteuid
(yang $>
dan $<
variabel).
Begitu juga zsh:
$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2
Meskipun di atas id
perintah - perintah itu disebut dengan id pengguna nyata dan set userid disimpan dari 0 (meskipun jika saya telah menggunakan saya ./env
bukan sudo
itu akan hanya set userid yang disimpan, sedangkan id pengguna sebenarnya akan tetap 1000), yang berarti bahwa jika itu adalah perintah yang tidak dipercaya, mereka masih bisa melakukan kerusakan, jadi Anda ingin menulisnya seperti:
$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'
(Yaitu mengatur semua uids (set efektif, nyata dan disimpan) hanya untuk pelaksanaan perintah-perintah itu.
bash
tidak memiliki cara untuk mengubah id pengguna. Jadi, bahkan jika Anda memiliki setuid yang dapat dieksekusi untuk memanggil bash
skrip Anda , itu tidak akan membantu.
Dengan bash
, Anda harus menjalankan setuid yang dapat dieksekusi setiap kali Anda ingin mengubah uid.
Gagasan dalam skrip di atas adalah panggilan ke SWITCH_TO_USER, untuk mengeksekusi contoh bash baru untuk mengeksekusi sisa skrip.
SWITCH_TO_USER someuser
lebih atau kurang fungsi yang mengeksekusi skrip lagi sebagai pengguna yang berbeda (menggunakan sudo
) tetapi melewatkan awal skrip sampai SWITCH_TO_USER someuser
.
Yang menjadi rumit adalah kami ingin menjaga status bash saat ini setelah memulai bash baru sebagai pengguna yang berbeda.
Mari kita jabarkan:
{ shopt -s expand_aliases;
Kami membutuhkan alias. Salah satu trik dalam skrip ini adalah untuk melewatkan bagian skrip hingga SWITCH_TO_USER someuser
, dengan sesuatu seperti:
:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
Bentuk itu mirip dengan yang #if 0
digunakan dalam C, yaitu cara untuk melengkapi komentar beberapa kode.
:
adalah no-op yang mengembalikan true. Jadi : || :
, yang kedua :
tidak pernah dieksekusi. Namun, ini diuraikan. Dan itu << 'xxx'
adalah bentuk di sini-dokumen di mana (karena xxx
dikutip), tidak ada perluasan atau interpretasi dilakukan.
Kita bisa melakukan:
: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
Tetapi itu berarti bahwa dokumen di sini harus ditulis dan diteruskan sebagai standar :
. :||:
hindari itu.
Sekarang, di mana peretasannya adalah kita menggunakan fakta yang bash
memperluas alias lebih awal dalam proses penguraiannya. Menjadi skip
alias ke :||: << 'SWITCH_TO_USER someuther'
bagian konstruksi komentar .
Mari kita lanjutkan:
SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";}
Inilah definisi fungsi SWITCH_TO_USER . Kita akan melihat di bawah bahwa SWITCH_TO_USER pada akhirnya akan menjadi alias yang membungkus fungsi itu.
Fungsi itu melakukan sebagian besar mengeksekusi kembali skrip. Pada akhirnya kita melihat bahwa itu dieksekusi kembali (dalam proses yang sama karena exec
) bash
dengan _x
variabel di lingkungan itu (kita gunakan di env
sini karena sudo
biasanya membersihkan lingkungannya dan tidak memungkinkan melewati sewenang-wenang dan melalui). Itu bash
mengevaluasi konten $_x
variabel itu sebagai kode bash dan sumber skrip itu sendiri.
_x
didefinisikan sebelumnya sebagai:
_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'
Semua declare
, alias
, shopt -p
set +o
keluaran membuat dump dari keadaan internal shell. Artinya, mereka membuang definisi semua variabel, fungsi, alias dan opsi sebagai kode shell yang siap dievaluasi. Selain itu, kami menambahkan pengaturan parameter posisi ( $1
, $2
...) berdasarkan nilai $_a
array (lihat di bawah), dan beberapa membersihkan sehingga $_x
variabel besar tidak tinggal di lingkungan selama sisa naskah.
Anda akan melihat bahwa bagian pertama hingga set +x
dibungkus dalam grup perintah yang stderrnya diarahkan ke /dev/null
( {...} 2> /dev/null
). Itu karena, jika pada beberapa titik dalam skrip set -x
(atau set -o xtrace
) dijalankan, kami tidak ingin pembukaan itu menghasilkan jejak karena kami ingin membuatnya sesedikit mungkin mengganggu. Jadi kami menjalankan set +x
(setelah memastikan untuk membuang opsi (termasuk xtrace
) pengaturan sebelumnya) di mana jejak dikirim ke / dev / null.
The eval "$_X"
stderr juga diarahkan ke / dev / null untuk alasan yang sama tetapi juga untuk menghindari kesalahan tentang menulis upaya untuk variabel read-only khusus.
Mari kita lanjutkan dengan skrip:
alias skip=":||:<<'SWITCH_TO_USER $_u'"
Itu trik kami yang dijelaskan di atas. Pada permintaan skrip awal, itu akan dibatalkan (lihat di bawah).
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
Sekarang pembungkus alias di sekitar SWITCH_TO_USER. Alasan utamanya adalah untuk dapat meneruskan parameter posisi ( $1
, $2
...) ke yang baru bash
yang akan menafsirkan sisa skrip. Kami tidak dapat melakukannya dalam SWITCH_TO_USER
fungsi karena di dalam fungsi, "$@"
ada argumen untuk fungsi, bukan dari skrip. Pengalihan stderr ke / dev / null lagi untuk menyembunyikan xtraces, dan eval
untuk mengatasi bug di bash
. Lalu kita memanggil SWITCH_TO_USER
fungsi .
${_u+:} alias skip=:
Bagian itu membatalkan skip
alias (menggantinya dengan perintah :
no-op) kecuali $_u
variabelnya disetel.
skip
Itu skip
alias kami . Pada doa pertama, itu hanya akan menjadi :
(no-op). Pada subsequence re-doa, itu akan menjadi sesuatu seperti: :||: << 'SWITCH_TO_USER root'
.
echo test
a=foo
set a b
SWITCH_TO_USER root
Jadi di sini, sebagai contoh, pada saat itu, kami menginstal kembali skrip sebagai root
pengguna, dan skrip akan memulihkan keadaan yang disimpan, dan melompat ke SWITCH_TO_USER root
baris itu dan melanjutkan.
Artinya adalah harus ditulis persis seperti stat, dengan SWITCH_TO_USER
di awal baris dan dengan tepat satu spasi di antara argumen.
Sebagian besar status, stdin, stdout, dan stderr akan dipertahankan, tetapi bukan deskriptor file lainnya karena sudo
biasanya ditutup kecuali dikonfigurasikan secara eksplisit untuk tidak melakukannya. Jadi misalnya:
exec 3> some-file
SWITCH_TO_USER bob
echo test >&3
biasanya tidak akan berfungsi.
Perhatikan juga bahwa jika Anda melakukannya:
SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root
Yang hanya bekerja jika Anda memiliki hak untuk sudo
sebagai alice
dan alice
memiliki hak untuk sudo
sebagai bob
, dan bob
sebagai root
.
Jadi, dalam praktiknya, itu tidak terlalu berguna. Menggunakan su
alih-alih sudo
(atau sudo
konfigurasi tempat sudo
mengotentikasi pengguna target alih-alih penelepon) mungkin lebih masuk akal, tetapi itu tetap berarti Anda harus mengetahui kata sandi semua orang itu.