- CVE-2026-31431 Copy Fail memungkinkan pengguna lokal tanpa hak istimewa mendapatkan shell
root, dan bahkan di dalam kontainer rootless Podman, eskalasi hak istimewa ke root di dalam kontainer juga dimungkinkan
- Kontainer rootless Podman menggabungkan namespace pengguna, pemisahan UID, dan Linux capabilities untuk memetakan
root di dalam kontainer ke pengguna host tanpa hak istimewa serta membatasi hak akses host
- Dalam pengujian, pengguna
foo di kontainer rootless non-root dapat menjadi root di dalam kontainer setelah menjalankan Copy Fail, tetapi hak aksesnya dibatasi pada cakupan yang dapat dilakukan oleh pengguna host tanpa hak istimewa bar, dan tidak dapat membaca file milik root di host
- Jika menerapkan
--security-opt=no-new-privileges atau --cap-drop=all, maka bahkan setelah menjalankan Copy Fail, shell tetap berada sebagai foo dengan capabilities none, sehingga mencegah perolehan shell root langsung dan peningkatan capability
- Dampak Copy Fail dapat bertahan melampaui siklus hidup kontainer sehingga diperlukan patch kernel dan reboot, dan pertahanan berlapis seperti root filesystem hanya-baca, pembatasan resource cgroups, image runtime yang ramping, dan firewall juga harus diterapkan
Cakupan Paparan Copy Fail dan Kontainer Rootless Podman
- CVE-2026-31431 dipublikasikan pada 29 April di copy.fail, dan dengan menjalankan skrip Python yang dipublikasikan, pengguna lokal tanpa hak istimewa dapat memperoleh shell
root
- Copy Fail juga dapat dieksploitasi di dalam kontainer Linux, dan memungkinkan perolehan shell
root di dalam kontainer rootless Podman
- Dalam pengujian,
root kontainer dibatasi pada tingkat host ke cakupan hak akses pengguna tanpa hak istimewa bar yang menjalankan kontainer
- Implementasi rootless Podman menggabungkan namespace pengguna, pemisahan UID, dan Linux capabilities untuk membatasi hak akses host dari proses kontainer
- Copy Fail menunjukkan bahwa bahkan kontainer rootless tidak kebal terhadap kerentanan, tetapi cakupan serangan pasca-kompromi dapat dikurangi melalui konfigurasi Podman
Cara Kerja Kontainer Rootless
-
Contoh dasar: pengguna tanpa hak istimewa bar menjalankan server HTTP
- Lingkungan contoh terdiri dari pengguna tanpa hak istimewa
bar dengan UID 1001 yang membangun image berbasis ubuntu:latest dengan Podman dan menjalankan python3 -m http.server
- Jika dilihat dari host dengan
ps, proses python3 berjalan sebagai milik pengguna bar
- Podman menggunakan model fork/exec, sehingga proses kontainer menjadi turunan dari proses
podman run, dan melalui pemisahan UID umum, proses kontainer dapat dipisahkan dari root host maupun pengguna lain
- Dalam konfigurasi Docker umum, meskipun pengguna tanpa hak istimewa menjalankan
docker run, klien Docker berkomunikasi dengan daemon berhak root, dan daemon itulah yang akhirnya membuat proses kontainer, sehingga di host proses kontainer dapat terlihat sebagai root
-
Rootless rootful
- Image kontainer biasanya menjalankan perintah kontainer sebagai
root internal jika tidak ada instruksi USER eksplisit atau flag --user
- Dalam output
podman top, proses server HTTP dipetakan ke pengguna host 1001, tetapi berjalan sebagai pengguna root di dalam kontainer
- Konfigurasi ini adalah keadaan rootless rootful: berjalan sebagai pengguna tanpa hak istimewa di host, tetapi sebagai
root di dalam kontainer
-
Namespace pengguna
- Kontainer rootless Podman menggunakan namespace pengguna untuk memetakan UID/GID secara berbeda di dalam dan di luar kontainer
- Dalam contoh,
root dengan UID internal kontainer 0 dipetakan ke bar dengan UID host 1001
- Konfigurasi
bar:165536:65536 di /etc/subuid menentukan rentang UID yang dapat dialokasikan ke proses namespace milik bar
- Dalam contoh, selain UID
1001 milik bar, UID dari 165536 hingga 231072 dapat dialokasikan ke proses bar
- Jika menjalankan
sleep sebagai pengguna internal kontainer www-data, di dalam akan terlihat sebagai www-data, tetapi di host ditampilkan sebagai 165568
- Jika masuk ke namespace pengguna dengan
podman unshare, direktori home yang di host dimiliki bar:bar akan terlihat sebagai root:root di dalam namespace
- Docker juga mendukung namespace pengguna, tetapi memerlukan konfigurasi terpisah dan hanya mengizinkan satu namespace pengguna, sedangkan Podman menjalankan kontainer rootless tiap pengguna UNIX di namespace pengguna milik pengguna tersebut
-
Operasi berhak akses dan Linux capabilities
- Podman menggunakan Linux capabilities untuk memberikan hak akses root yang terperinci kepada proses kontainer
- Selama build image, pekerjaan seperti
apt install dimungkinkan melalui kombinasi capability seperti chown, dac_override, fowner, setgid, setuid, net_bind_service, dan sys_chroot
- Jika semua capability dihapus dengan
podman build --cap-drop=all, apt akan gagal pada setgroups, setegid, seteuid, chown, dan lainnya sehingga build image gagal
- Dimungkinkan juga menambahkan hanya capability yang diperlukan, dan dalam contoh
CAP_SETUID,CAP_SETGID,CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER ditambahkan untuk melakukan instalasi paket
- Dalam keadaan eksekusi default, server HTTP berjalan sebagai
root di dalam kontainer dan memiliki banyak effective capabilities seperti CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,NET_BIND_SERVICE,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT
- Karena server HTTP tidak memerlukan hak akses tersebut, semua capability dapat dihapus dengan
podman run --cap-drop=all, dan dalam kondisi ini podman top menampilkan effective capabilities sebagai none
-
Rootless non-root
- Untuk menjalankan server HTTP sebagai pengguna tanpa hak istimewa juga di dalam kontainer, Anda bisa memakai pengguna yang sudah ada di
/etc/passwd, misalnya www-data, atau membuat pengguna khusus saat build image
- Dalam contoh, pengguna dan grup
foo dengan UID 1002 dibuat, lalu diberikan izin baca ke /var/www/html dan USER foo:foo ditetapkan
- Jika image ini dijalankan dengan
--cap-drop=all, proses akan menjadi foo di dalam kontainer, UID host 166537, dan effective capabilities none
- Proses kontainer harus dijalankan dengan hak akses minimum yang diperlukan; misalnya, jika
foo perlu bind ke port berhak 80, maka --cap-add=CAP_NET_BIND_SERVICE harus ditambahkan
- Cara menjalankan kontainer dibagi menjadi empat kategori
- pengguna host
root + kontainer root: root rootful
- pengguna host
root + pengguna tanpa hak istimewa di kontainer: root non-root
- pengguna host tanpa hak istimewa + kontainer
root: rootless rootful
- pengguna host tanpa hak istimewa + pengguna tanpa hak istimewa di kontainer: rootless non-root
- Podman memudahkan menjalankan kontainer rootless rootful, dan jika proses kontainer bisa dijalankan sebagai pengguna tanpa hak istimewa, konfigurasi rootless non-root juga relatif mudah dibuat
Bind mount dan isolasi UID
- Saat direktori host dimount ke kontainer, apakah file yang dimiliki host
root, host bar, dan namespace foo bisa diakses akan bergantung pada pemetaan UID
- Dalam contoh, file
root.txt milik host root dan bar.txt milik host bar dibuat di direktori /var/lib/bar/test, lalu dimount baca/tulis ke /test di dalam kontainer
- Jika kontainer dijalankan sebagai
foo, file milik host bar akan terlihat sebagai root:root di dalam kontainer, sedangkan file milik host root tidak dipetakan ke namespace sehingga terlihat sebagai nobody:nogroup
foo di dalam kontainer tidak dapat membaca bar.txt maupun root.txt, dan rootless non-root memberikan isolasi tambahan dibanding rootless rootful
foo.txt yang dibuat foo di direktori mount akan terlihat di host sebagai dimiliki UID 166537, dan pengguna host bar tidak dapat membaca isi file tersebut
- Jika kontainer dijalankan sebagai
root internal, root namespace dapat membaca file milik host bar dan file milik foo, tetapi tetap tidak dapat membaca file milik host root
- Jika dijalankan sebagai
root internal sambil menerapkan --cap-drop=all, maka file milik foo juga tidak bisa dibaca, dan hanya file milik host bar yang bisa dibaca
Pengujian Copy Fail
-
Kondisi pengujian
- Pengujian Copy Fail menggunakan versi eksploit dari commit yang awalnya dipublikasikan,
8e918b5
- Image kontainer contoh dibangun dari image server HTTP yang sudah ada dengan menambahkan
curl agar skrip eksploit bisa diunduh dari dalam kontainer
- Nama image dibangun sebagai
copyfail
- Kernel uji adalah
6.12.74+deb13+1-amd64 dari Debian, dan berdasarkan Debian, versi terbaru di bawah 6.12.85 masih bisa dianggap sebagai kernel yang belum ditambal
- Secara umum, jika pengguna tak berprivilege
foo memanggil su, maka kata sandi root akan diminta
- Dalam setiap pengujian, pengguna kontainer mengunduh skrip Copy Fail ke
/tmp lalu menjalankannya, dan jika berhasil memperoleh shell root, maka sleep akan dipanggil
- Karena Copy Fail tetap bertahan melampaui siklus hidup kontainer, VM di-reboot sebelum setiap pengujian
-
Hasil pada rootless rootful
- Jika kontainer dijalankan dengan
--user=root, maka proses di dalam kontainer sejak awal sudah berstatus root
- Dalam kondisi ini, saat skrip Copy Fail dijalankan dan
su dipanggil, shell uid=0(root) memang diperoleh, tetapi karena pengguna root pada dasarnya bisa membuka shell root lain lewat su tanpa kata sandi, Copy Fail praktis tidak menambah apa pun
- Di
podman top, /bin/bash, python3 copy_fail_exp.py, su, dan sleep semuanya terlihat sebagai root di dalam kontainer dan pengguna host 1001
- Set capability yang sama tetap dipertahankan, dan terlihat
CHOWN,DAC_OVERRIDE,FOWNER,FSETID,KILL,NET_BIND_SERVICE,SETFCAP,SETGID,SETPCAP,SETUID,SYS_CHROOT
root internal dapat membaca bar.txt dan foo.txt di /test yang dimount, tetapi tidak dapat membaca root.txt milik host root
-
Hasil pada rootless non-root
- Setelah kontainer dijalankan sebagai
foo, menjalankan skrip Copy Fail lalu memanggil su akan menaikkan hak akses ke root di dalam kontainer
id pada shell hasilnya ditampilkan sebagai uid=0(root) gid=1002(foo) groups=1002(foo)
- Di
podman top, /bin/bash awal, proses eksekusi eksploit, dan pemanggilan su terlihat sebagai UID host 166537, pengguna kontainer foo, dan capabilities none
- Setelah eskalasi hak akses,
[sh] dan sleep terlihat sebagai pengguna host 1001, pengguna kontainer root, dan memperoleh set capability yang sama seperti rootless rootful
root kontainer yang telah ditingkatkan hak aksesnya juga tetap tidak dapat membaca root.txt milik host root
- Dalam kondisi ini kontainer memang telah dikompromikan, tetapi cakupan serangannya dibatasi pada ruang lingkup yang bisa dilakukan kontainer dan pengguna host tak berprivilege
bar
-
Hasil saat no-new-privileges diterapkan
- Podman dapat menggunakan
--security-opt=no-new-privileges agar proses kontainer tidak memperoleh hak akses lebih besar daripada saat mulai berjalan
- Jika opsi ini diterapkan ke kontainer rootless non-root lalu Copy Fail dijalankan, shell tetap terbuka tetapi masih berstatus
uid=1002(foo)
- Di
podman top juga, semua proses tetap sebagai UID host 166537, pengguna kontainer foo, dan capabilities none
- Pada
/test yang dimount, foo juga hanya bisa membaca file miliknya sendiri dan tidak bisa membaca bar.txt maupun root.txt
- Kontainer tetap dikompromikan, tetapi dibatasi pada pengguna internal tak berprivilege
foo tanpa capability
-
Hasil saat --cap-drop=all diterapkan
- Meski kontainer rootless non-root dijalankan dengan
--cap-drop=all, foo pada dasarnya memang tidak memiliki capability
- Dalam kondisi ini, saat Copy Fail dijalankan dan
su dipanggil, shell yang terbuka tetap berstatus uid=1002(foo)
- Di
podman top, /bin/bash, eksekusi eksploit, su, shell, dan sleep semuanya tetap dalam status foo dan capabilities none
- Eksploit gagal memperoleh shell
root, dan foo hanya dapat membaca file miliknya sendiri di /test
- Hasil ini mirip dengan pengujian
no-new-privileges, dan kedua langkah ini dapat digunakan bersama untuk secara efektif mengurangi paparan capability
-
Persistensi eksploit
- Walaupun perolehan shell
root dan capability secara langsung dapat dicegah dengan no-new-privileges atau --cap-drop=all, efek eksploit itu sendiri tetap tersisa
- Jika setelahnya kontainer baru dijalankan tanpa pembatasan capability, pengguna kontainer tak berprivilege
foo bisa menjadi root kontainer hanya dengan memanggil su
- Karena itu, patch kernel dan reboot tetap diperlukan
Strategi pertahanan berlapis
-
Image hanya-baca
- Menambahkan
--read-only ke podman run akan me-mount filesystem root kontainer sebagai hanya-baca
- Secara default, Podman me-mount beberapa direktori seperti
/tmp, /run, dan /var/tmp agar dapat ditulisi, jadi untuk membuatnya benar-benar hanya-baca, perlu menambahkan --read-only-tmpfs=false
- Jika kontainer hanya-baca berhasil dibobol, penulisan ke sistem tidak diizinkan sehingga beberapa serangan pasca-eksploitasi dapat dibatasi
- Namun, karena output
curl bisa di-pipe ke python3, pengaturan hanya-baca saja tidak mencegah eksekusi eksploit itu sendiri
- Server HTTP
python3 pada contoh tidak memerlukan penulisan ke filesystem sehingga opsi ini dapat digunakan dengan aman
- Banyak image prebuilt mengasumsikan akses tulis ke direktori tertentu, sehingga mungkin tidak berfungsi dengan baik pada filesystem root hanya-baca
- Filesystem root hanya-baca bersifat terpisah dari volume writable yang terpasang ke kontainer, dan saat terjadi kompromi, direktori mount tersebut tetap bisa ditulisi
-
Pembatasan sumber daya
- Docker dan Podman dapat menggunakan cgroups untuk membatasi sumber daya yang diberikan ke kontainer
- Kontainer tidak memerlukan memori, CPU, dan PID tanpa batas
- Setelah memeriksa penggunaan sumber daya kontainer dengan
podman stats, pembatasan dapat diterapkan sesuai kebutuhan
-
Membatasi biner yang tersedia
- Contoh menggunakan image
ubuntu demi penyederhanaan, tetapi image ubuntu menyertakan banyak biner yang dapat digunakan penyerang saat terjadi kompromi
- Sebagian besar biner tersebut tidak diperlukan untuk menjalankan server HTTP
- Sebaiknya image runtime dibuat setipis mungkin
- Build multistage dapat digunakan untuk memisahkan lingkungan build-time dan runtime
- Dapat menggunakan image bertujuan khusus seperti python3, varian
-slim milik Debian, atau distro yang lebih kecil seperti alpine
- Jika kompatibel dengan proses kontainer, distroless images atau
scratch dapat digunakan untuk membuat runtime tanpa shell, package manager, dan utilitas sistem
-
Firewall
iptables atau nftables dapat digunakan untuk membatasi proses kontainer dengan firewall
- Hanya koneksi masuk dan keluar yang benar-benar diperlukan oleh proses kontainer yang boleh diizinkan
- Pada contoh server HTTP, koneksi ke DNS atau server lokal/jarak jauh tidak diperlukan, sehingga pembatasan dapat dibuat dengan hanya mengizinkan paket
tcp yang datang dari koneksi masuk yang sudah terbentuk
Implikasi operasional
- Kontainer rootless Podman standar secara default menyediakan isolasi yang lebih baik daripada konfigurasi kontainer Docker standar
- Docker juga mendukung menjalankan secara rootless dan penggunaan user namespace tanpa hak istimewa, tetapi memerlukan lebih banyak upaya konfigurasi dibanding Podman, dan perbedaan arsitektur juga berpengaruh
- Docker masih digunakan secara luas, dan alat self-hosting seperti Dokku, Kamal, Coolify, dan Dokploy juga menggunakan Docker secara default
- Jika image Docker Hub dijalankan tanpa peninjauan memadai atau tanpa menerapkan penguncian, layanan dapat berjalan dengan permukaan serangan yang lebih luas daripada yang dibutuhkan
- Detail implementasi image kontainer harus dipahami
- Harus diketahui pengguna atau para pengguna mana yang menjalankan proses kontainer
- Harus diketahui direktori mana pada filesystem root yang menjadi dependensi proses kontainer
- Harus dibedakan Linux capabilities yang diperlukan dan yang tidak diperlukan
- Dengan menggabungkan berbagai mekanisme yang disediakan Podman dan kontainer, kontainer dapat diperkeras dan blast radius saat terjadi kompromi dapat dikurangi
- Tergantung pada workload, kontainer tidak boleh dijadikan satu-satunya batas keamanan yang diandalkan
- Pemisahan yang efektif dapat dicapai dengan menggunakan kontainer bersama mesin fisik atau virtual yang terpisah
- Podman menyediakan cara untuk mengisolasi setiap workload bahkan dalam host yang sama dengan menjalankannya sebagai pengguna tanpa hak istimewa yang terpisah dan dengan user namespace masing-masing
Bacaan tambahan
1 komentar
Opini Lobste.rs
Fokus seharusnya pada primitif dasar yang dimungkinkan oleh kerentanan ini, bukan hanya pada eksploit yang sudah dipublikasikan
Kerentanan ini memungkinkan penulisan ke page cache terlepas dari status read-only, sehingga kontainer berbahaya dapat memodifikasi halaman yang berasal dari file image dasar overlayfs, dan tergantung cara kontainer dideploy, dampaknya bisa menjalar ke kontainer lain juga
Dalam konfigurasi rootless seperti ini, targetnya adalah kontainer lain yang berjalan sebagai pengguna yang sama di sistem host
Cara eksploit lain adalah menjalankan atau menemukan kontainer berbasis image dasar yang diketahui sudah digunakan, lalu memodifikasi page cache di dalam kontainer itu sehingga kontainer lain yang berbagi runtime dan data overlayfs yang sama akan mengeksekusi kode tersebut
Rootless dan namespace pengguna memang penting, tetapi di sini tidak banyak membantu; seperti yang dikatakan situs copy.fail, di kontainer sebaiknya dipertimbangkan untuk memblokir system call
socket(AF_ALG, ...)dengan seccompAkan bagus kalau dijelaskan lebih rinci apa yang dimaksud dengan “tergantung cara kontainer dideploy”
Keuntungan rootless Podman adalah, tergantung workload-nya, Anda tidak harus menjalankan kontainer di host sebagai pengguna yang sama
Kalau yang dimaksud adalah menjalankan banyak kontainer rootless sebagai pengguna workstation utama, saya setuju, tetapi di server masing-masing bisa dipisah ke pengguna yang berbeda, dan image kontainer yang sama pun bisa dijalankan sebagai pengguna non-privileged yang berbeda
Ini cukup berbeda dari default Docker yang kebanyakan menjalankan semuanya sebagai
root, tetapi saya juga menulis di akhir artikel bahwa ini bukan batas keamanan yang ultimate, dan apakah tepat membagi kontainer rootless ke beberapa pengguna non-privileged bergantung pada kasus penggunaannyaBeberapa workload tertentu saya pisahkan menggunakan VM
Saya penasaran apakah pernyataan bahwa rootless dan namespace pengguna tidak membantu di sini maksudnya adalah dalam hal mencegah eksploit
Saya belum pernah menerapkan kebijakan seccomp eksplisit pada kontainer, jadi saya tidak membahasnya, tetapi ini jadi alasan bagus untuk mempelajarinya lebih lanjut
Saya suka Podman dan kontainer rootless, tetapi setelah melihat CopyFail saya sampai pada kesimpulan yang sama seperti komentar saudara
Bahkan dengan keuntungan kontrol akses tambahan dari
podman+rootless, ini kembali menegaskan nasihat klasik bahwa kontainer bukan batas keamanan, dan satu eksploit kernel saja bisa menembus semuanyaSaya hanya mengelola sistem sebagai hobi, tetapi sebagai perkembangan baru di area ini saya melihat libkrun backend for crun with podman
Janjinya adalah bisa menangani sebagian besar workload yang sudah dikontainerisasi apa adanya, tetapi secara internal dijalankan di MicroVM dengan guest kernel terpisah; namun saya tidak tahu tingkat kematangan, validasi di dunia nyata, atau audit keamanannya, dan sebagian tampak cukup cutting-edge
Karena MicroVM sedang diadopsi secara aktif oleh alat coding LLM, kondisi seperti itu mungkin tidak akan bertahan lama
podman machinejuga tampak menjanjikan, tetapi sayangnya itu tampaknya hanya ditujukan untuk workstation developer dan memakai model satu VM eksekusi kontainer per sistem hostMeski begitu, saya rasa ungkapan “kontainer bukan batas keamanan” terlalu menyederhanakan. Kontainer jelas merupakan batas keamanan, hanya saja tidak sekuat yang ingin kita percayai
Pada deployment lokal, garis ini sedikit lebih kabur
Dari sudut pandang hardware, VM tidak secara inheren lebih aman daripada proses, tetapi ada tiga alasan mengapa batasnya lebih bisa dipertahankan
Escape dari VM lebih jarang dibanding system call, sehingga ada ruang untuk menerapkan lebih banyak mitigasi side-channel tanpa mengorbankan performa
Antarmuka host VM jauh lebih sederhana. Perangkat blok memiliki antarmuka baca/tulis berbasis blok, dan perangkat jaringan mengirim serta menerima frame
Panggilan
setsockoptyang disediakan Linux atau *BSD pada socket merupakan permukaan serangan yang jauh lebih besar daripada kebanyakan driver emulasi atau paravirtualisasi, padahal itu sendiri hanya sebagian kecil sekali dari seluruh permukaan serangan kernelAntarmuka VM juga memiliki state yang jauh lebih sedikit. Ada transaksi yang sedang berjalan pada ring model request-response, tetapi selain itu hampir tidak ada
Hal-hal seperti kredensial, UID, GID, dan tabel file descriptor menambah kompleksitas berbasis state ke kernel, dan jika ada bug proses dapat menyalahgunakannya
Kesulitan pada varian workstation adalah kompleksitas ini kembali dimasukkan lagi
Misalnya, layer dasar kontainer bisa diekspos sebagai perangkat blok yang berisi filesystem immutable, tetapi volume dan folder bersama kemungkinan akan di-mount melalui 9pfs atau VirtIO-FS, yakni 9p atau FUSE di atas VirtIO
Itu memperbesar kembali permukaan serangan
Kalau beruntung, penyerang membutuhkan rantai eksploit
Saya lebih akrab dengan sisi FreeBSD, dan biasanya komponen yang menyediakan perangkat paravirtualisasi atau emulasi disandbox dengan Capsicum, sehingga penyerang harus lebih dulu mengambil alih proses host, lalu tetap harus menembus kernel untuk mengakses hal-hal yang sebelumnya tidak bisa diakses VM
Tetapi tanpa sandbox tambahan semacam ini, escape dari kontainer kembali menjadi dunia di mana ia dapat melakukan semua hal yang bisa dilakukan pengguna, dan itu tidak jauh lebih baik daripada root yang jebol di desktop
Secara pribadi saya lebih menyukai gVisor. Itu bukan runtime VMM, tetapi sudah ada selama beberapa tahun, dipakai juga oleh perusahaan seperti Tencent, dan sangat cocok dengan lingkungan saya yang sudah menjalankan semua kontainer di dalam VM Proxmox
Hal lain yang sedang saya uji adalah syd-oci, yang tampaknya agak kurang mendapat perhatian dibanding rekomendasi utama seperti MicroVM atau gVisor
Terima kasih juga untuk referensi libkrun; ini tampak seperti kemungkinan yang menjanjikan
Kemungkinannya juga besar akan mendorong audit keamanan