Perintah baru dapat dijalankan tanpa akses jaringan sebagai non-root menggunakan unshare -r -n
, misalnya:
$ unshare -r -n ls
a.txt b.txt
Perintah yang memang membutuhkan akses jaringan dapat diprediksi akan gagal.
$ unshare -r -n curl unix.stackexchange.com
curl: (6) Could not resolve host: unix.stackexchange.com
Saya bertanya-tanya apakah mungkin untuk menghapus akses jaringan untuk proses saat ini, berpotensi dengan menulis ke file ajaib di /sys
atau yang serupa.
Saya ingin dapat melakukan sesuatu seperti
$ /bin/sh -c 'echo 1 > /sys/unsharethis; curl unix.stackexchange.com'
Kutipan dari strace
-ing unshare -r -n ls
menunjukkan unshare
panggilan sistem
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=4759040, ...}) = 0
mmap(NULL, 4759040, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7ec6968000
close(3) = 0
unshare(CLONE_NEWUSER|CLONE_NEWNET) = 0
open("/proc/self/setgroups", O_WRONLY) = 3
write(3, "deny", 4) = 4
Yang menunjukkan kepada saya bahwa tidak membagikan akses jaringan dari proses saat ini sebenarnya adalah satu-satunya cara untuk mencapai unsharing (yaitu tidak dapat diteruskan sebagai argumen ke spawn
atau yang setara dengannya). Ini juga menunjukkan bahwa berhenti berbagi dari skrip shell tidak akan berfungsi kecuali jika shell telah secara khusus diperluas untuk mengekspos pembungkus di sekitar unshare
.
Jawaban yang Diterima:
Hal ini dapat dilakukan, semacam, dengan gdb
debugger, dan jika proses yang berjalan dapat dilampirkan (program yang mengubah status dumpable-nya, atau setgid, dll. tidak dapat dilampirkan, kecuali dari root).
Beberapa file opsional dapat membantu menggunakan gdb seperti simbol debug untuk libc6, dan beberapa file terkait Linux menyertakan file untuk mendapatkan nilai sebenarnya dari beberapa simbol nanti (misalnya pada Debian:(mungkin) libc6-dbg
, libc6-dev
dan linux-libc-dev
paket), tetapi sebenarnya setelah "resep" dibuat, mereka mungkin tidak diperlukan lagi.
Pertama apa yang lebih dari unshare()
unshare -r
sedang melakukan? Tanpa ini, pengguna baru tetap berada di nobody
dan bahkan tidak bisa menulis sebagai pengguna awalnya:
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$ strace unshare -r -n /bin/sleep 1 2>&1 |sed -n '/^unshare/,/^execve/p'
unshare(CLONE_NEWNET|CLONE_NEWUSER) = 0
open("/proc/self/setgroups", O_WRONLY) = 3
write(3, "deny", 4) = 4
close(3) = 0
open("/proc/self/uid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
open("/proc/self/gid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
execve("/bin/sleep", ["/bin/sleep", "1"], [/* 18 vars */]) = 0
Itu akan digunakan nanti.
$ ip -4 -br a
lo UNKNOWN 127.0.0.1/8
[email protected] UP 10.0.3.66/24
$ ping -c1 10.0.3.1
PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data.
64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.167 ms
--- 10.0.3.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.167/0.167/0.167/0.000 ms
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$ echo $$
338
$
Di terminal lain:
$ gdb --pid=338
Reading symbols from /bin/bash...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libtinfo.so.5...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libdl.so.2...Reading symbols from /usr/lib/debug/.build-id/b8/95f0831f623c5f23603401d4069f9f94c24761.debug...done.
done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug/.build-id/aa/889e26a70f98fa8d230d088f7cc5bf43573163.debug...done.
done.
[…]
(gdb)
Sekarang mari kita panggil fungsi pertama:
(gdb) call unshare(CLONE_NEWNET|CLONE_NEWUSER)
No symbol "CLONE_NEWNET" in current context.
Oke, mungkin ada metode agar gdb mengetahuinya, tapi saya bukan seorang guru:
(gdb) !
$ grep CLONE_NEW /usr/include/linux/sched.h # man 2 unshare
#define CLONE_NEWNS 0x00020000 /* New mount namespace group */
#define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
#define CLONE_NEWUTS 0x04000000 /* New utsname namespace */
#define CLONE_NEWIPC 0x08000000 /* New ipc namespace */
#define CLONE_NEWUSER 0x10000000 /* New user namespace */
#define CLONE_NEWPID 0x20000000 /* New pid namespace */
#define CLONE_NEWNET 0x40000000 /* New network namespace */
$ find /usr/include/ -name fcntl.h |xargs grep O_WRONLY # man 2 open
/usr/include/asm-generic/fcntl.h:#define O_WRONLY 00000001
$ exit
exit
(gdb) call unshare(0x50000000)
$1 = 0
(gdb) call open("/proc/self/setgroups", 1)
$2 = 3
(gdb) call write($2,"deny",4)
$3 = 4
(gdb) call close($2)
$4 = 0
(gdb) call open("/proc/self/uid_map", 1)
$5 = 3
(gdb) call write($5, "0 1000 1", 8)
$6 = 8
(gdb) call close($5)
$7 = 0
(gdb) call open("/proc/self/gid_map", 1)
$8 = 3
(gdb) call write($8, "0 1000 1", 8)
$9 = 8
(gdb) call close($8)
$10 = 0
(gdb) quit
A debugging session is active.
Inferior 1 [process 338] will be detached.
Quit anyway? (y or n) y
Detaching from program: /bin/bash, process 338
Pada proses yang diubah, seseorang dapat memverifikasi eth0
antarmuka menghilang:
$ ip -br a
lo DOWN 127.0.0.1/8
$ echo $$
338
$ id
uid=0(root) gid=0(root) groupes=0(root)
$ touch /
touch: setting times of '/': Permission denied
$ touch ~/test1
$ ls ~/test1
/home/user/test1
$ ping 10.0.3.1
connect: Network is unreachable
Tidak ada jalan untuk kembali:ruang nama pengguna baru tidak dapat diubah kembali ke ruang nama awalnya. Jika proses berjalan dengan hak yang cukup (misalnya root tanpa kehilangan kemampuan atau SELinux) maka itu akan mungkin (hanya menggunakan unshare(CLONE_NEWNET)
/ setns(savedopenedfd)
).
Tentu saja dimungkinkan untuk membuat skrip dalam file, dan mengubah proses berjalan yang diizinkan, atau meminta shell mengubah dirinya sendiri dari subproses gdb. Isi removenetwork.gdb
, valid di sini hanya untuk mengubah proses dengan pid:gid
==1000:1000
:
PEMBARUAN:menambahkan (perkiraan) jenis pengembalian untuk syscalls di bawah ini, ini harus menghindari beberapa versi gdb untuk dikeluhkan di lingkungan non-dev:
call (int)unshare(0x50000000)
call (int)open("/proc/self/setgroups", 1)
call (long)write($2,"deny",4)
call (int)close($2)
call (int)open("/proc/self/uid_map", 1)
call (long)write($5, "0 1000 1", 8)
call (int)close($5)
call (int)open("/proc/self/gid_map", 1)
call (long)write($8, "0 1000 1", 8)
call (int)close($8)
quit
Contoh:
$ sh -c 'id; gdb --pid=$$ < removenetwork.gdb >/dev/null 2>&1; id; curl unix.stackexchange.com'
uid=1000(user) gid=1000(user) groups=1000(user)
uid=0(root) gid=0(root) groups=0(root)
curl: (6) Could not resolve host: unix.stackexchange.com
PERBARUI :jika root tidak diperlukan sama sekali, seperti yang muncul untuk Pertanyaan ini, maka tidak perlu memetakan ke root sama sekali. Cukup ganti kemunculan write($XX, "0 1000 1", 8)
dengan write($XX, "1000 1000 1", 11)
(untuk uid:gid
==1000:1000
kasus). Grup tambahan masih tetap hilang, tetapi uid/gid tidak berubah (dipetakan ke dirinya sendiri).